diff --git a/.azure/lint-linux.yml b/.azure/lint-linux.yml index 532c2072ed29..a1809bd829bd 100644 --- a/.azure/lint-linux.yml +++ b/.azure/lint-linux.yml @@ -27,6 +27,8 @@ jobs: -r requirements.txt \ -r requirements-dev.txt \ -e . + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. displayName: 'Install dependencies' env: SETUPTOOLS_ENABLE_FEATURES: "legacy-editable" @@ -35,7 +37,7 @@ jobs: set -e source test-job/bin/activate echo "Running black, any errors reported can be fixed with 'tox -eblack'" - black --check qiskit test tools examples setup.py qiskit_pkg + black --check qiskit test tools examples setup.py echo "Running rustfmt check, any errors reported can be fixed with 'cargo fmt'" cargo fmt --check displayName: "Formatting" @@ -44,7 +46,7 @@ jobs: set -e source test-job/bin/activate echo "Running ruff" - ruff qiskit test tools examples setup.py qiskit_pkg/setup.py + ruff qiskit test tools examples setup.py echo "Running pylint" pylint -rn qiskit test tools echo "Running Cargo Clippy" diff --git a/.azure/test-linux.yml b/.azure/test-linux.yml index c97d4f693975..512e1d773c35 100644 --- a/.azure/test-linux.yml +++ b/.azure/test-linux.yml @@ -29,6 +29,7 @@ jobs: QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip QISKIT_TEST_CAPTURE_STREAMS: 1 + HAVE_VISUAL_TESTS_RUN: false steps: - task: UsePythonVersion@0 @@ -70,15 +71,16 @@ jobs: # Use stable Rust, rather than MSRV, to spot-check that stable builds properly. rustup override set stable source test-job/bin/activate - python -m pip install -U pip setuptools wheel - # Install setuptools-rust for building sdist - python -m pip install -U -c constraints.txt setuptools-rust - python setup.py sdist + python -m pip install -U pip + python -m pip install -U build + python -m build --sdist . python -m pip install -U \ -c constraints.txt \ -r requirements.txt \ -r requirements-dev.txt \ - dist/qiskit-terra*.tar.gz + dist/qiskit-*.tar.gz + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. displayName: "Install Terra from sdist" - ${{ if eq(parameters.installFromSdist, false) }}: @@ -90,6 +92,8 @@ jobs: -r requirements.txt \ -r requirements-dev.txt \ -e . + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. displayName: "Install Terra directly" env: SETUPTOOLS_ENABLE_FEATURES: "legacy-editable" @@ -197,7 +201,9 @@ jobs: env: SETUPTOOLS_ENABLE_FEATURES: "legacy-editable" - - bash: image_tests/bin/python -m unittest discover -v test/visual + - bash: | + echo "##vso[task.setvariable variable=HAVE_VISUAL_TESTS_RUN;]true" + image_tests/bin/python -m unittest discover -v test/visual displayName: 'Run image test' env: # Needed to suppress a warning in jupyter-core 5.x by eagerly migrating to @@ -213,7 +219,7 @@ jobs: archiveType: tar archiveFile: '$(Build.ArtifactStagingDirectory)/visual_test_failures.tar.gz' verbose: true - condition: failed() + condition: and(failed(), eq(variables.HAVE_VISUAL_TESTS_RUN, 'true')) - task: ArchiveFiles@2 displayName: Archive circuit results @@ -222,7 +228,7 @@ jobs: archiveType: tar archiveFile: '$(Build.ArtifactStagingDirectory)/circuit_results.tar.gz' verbose: true - condition: failed() + condition: and(failed(), eq(variables.HAVE_VISUAL_TESTS_RUN, 'true')) - task: ArchiveFiles@2 displayName: Archive graph results @@ -231,7 +237,7 @@ jobs: archiveType: tar archiveFile: '$(Build.ArtifactStagingDirectory)/graph_results.tar.gz' verbose: true - condition: failed() + condition: and(failed(), eq(variables.HAVE_VISUAL_TESTS_RUN, 'true')) - task: PublishBuildArtifacts@1 displayName: 'Publish image test failure diffs' @@ -240,4 +246,4 @@ jobs: artifactName: 'image_test_failure_img_diffs' Parallel: true ParallelCount: 8 - condition: failed() + condition: and(failed(), eq(variables.HAVE_VISUAL_TESTS_RUN, 'true')) diff --git a/.azure/test-macos.yml b/.azure/test-macos.yml index 2b195afbdae3..c241f51f57e9 100644 --- a/.azure/test-macos.yml +++ b/.azure/test-macos.yml @@ -44,6 +44,8 @@ jobs: -r requirements.txt \ -r requirements-dev.txt \ -e . + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. pip check displayName: 'Install dependencies' env: diff --git a/.azure/test-windows.yml b/.azure/test-windows.yml index 30591a5dadbe..f546fdb41dc3 100644 --- a/.azure/test-windows.yml +++ b/.azure/test-windows.yml @@ -43,6 +43,8 @@ jobs: -r requirements.txt \ -r requirements-dev.txt \ -e . + # Build and install both qiskit and qiskit-terra so that any optionals + # depending on `qiskit` will resolve correctly. pip check displayName: 'Install dependencies' env: diff --git a/.azure/tutorials-linux.yml b/.azure/tutorials-linux.yml deleted file mode 100644 index c7cc9ba57ad5..000000000000 --- a/.azure/tutorials-linux.yml +++ /dev/null @@ -1,42 +0,0 @@ -parameters: - - name: "pythonVersion" - type: string - displayName: "Version of Python to test" - -jobs: - - job: "Tutorials" - pool: {vmImage: 'ubuntu-latest'} - - variables: - PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip - - steps: - - task: UsePythonVersion@0 - inputs: - versionSpec: '${{ parameters.pythonVersion }}' - displayName: 'Use Python ${{ parameters.pythonVersion }}' - - - bash: tools/install_ubuntu_docs_dependencies.sh - displayName: 'Install dependencies' - - - bash: tox -e tutorials - displayName: "Execute tutorials" - env: - QISKIT_CELL_TIMEOUT: 300 - - - task: ArchiveFiles@2 - inputs: - rootFolderOrFile: 'executed_tutorials' - archiveType: tar - archiveFile: '$(Build.ArtifactStagingDirectory)/executed_tutorials.tar.gz' - verbose: true - condition: succeededOrFailed() - - - task: PublishBuildArtifacts@1 - displayName: 'Publish updated tutorials' - inputs: - pathtoPublish: '$(Build.ArtifactStagingDirectory)' - artifactName: 'executed_tutorials' - Parallel: true - ParallelCount: 8 - condition: succeededOrFailed() diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000000..f94ee26f4498 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Non-API docs issues + url: https://github.com/Qiskit/documentation/issues/new/choose + about: Open an issue about documentation in the Start, Build, Transpile, Verify, Run, or Migration guides sections of docs.quantum.ibm.com (non-API documentation) \ No newline at end of file diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml index 8f922121f938..a375b8750d4e 100644 --- a/.github/workflows/docs_deploy.yml +++ b/.github/workflows/docs_deploy.yml @@ -16,10 +16,6 @@ on: description: "Push to qiskit.org?" required: false type: boolean - do_translatables: - description: "Push translatable strings?" - required: false - type: boolean jobs: build: @@ -53,33 +49,8 @@ jobs: - name: Install dependencies run: tools/install_ubuntu_docs_dependencies.sh - # This is just to have tox create the environment, so we can use it to execute the tutorials. - # We want to re-use it later for the build, hence 'tox run --notest' instead of 'tox devenv'. - - name: Prepare Python environment - run: tox run -e docs --notest - - # The reason to use the custom script rather than letting 'nbsphinx' do its thing normally - # within the Sphinx build is so that the execution process is the same as in the test CI. - - name: Execute tutorials in place - run: .tox/docs/bin/python tools/execute_tutorials.py docs/tutorials - env: - QISKIT_CELL_TIMEOUT: "300" - - name: Build documentation - # We can skip re-installing the package, since we just did it a couple of steps ago. - run: tox run -e docs --skip-pkg-install - env: - QISKIT_ENABLE_ANALYTICS: "true" - # We've already built them. - QISKIT_DOCS_BUILD_TUTORIALS: "never" - DOCS_PROD_BUILD: "true" - - - name: Build translatable strings - run: tox run -e gettext - env: - # We've already built them. - QISKIT_DOCS_BUILD_TUTORIALS: "never" - DOCS_PROD_BUILD: "true" + run: tox run -e docs - name: Store built documentation artifact uses: actions/upload-artifact@v3 @@ -91,13 +62,6 @@ jobs: !**/.buildinfo if-no-files-found: error - - name: Store translatable strings artifact - uses: actions/upload-artifact@v3 - with: - name: qiskit-translatables - path: ./docs/locale/en/* - if-no-files-found: error - deploy: if: github.event_name != 'workflow_dispatch' || inputs.do_deployment name: Deploy to qiskit.org @@ -198,26 +162,3 @@ jobs: JOINED_PREFIXES: ${{ steps.choose.outputs.joined_prefixes }} RCLONE_KEY: ${{ secrets.ENCRYPTED_RCLONE_KEY}} RCLONE_IV: ${{ secrets.ENCRYPTED_RCLONE_IV }} - - deploy_translatables: - if: (github.event_name == 'workflow_dispatch' && inputs.do_translatables) || (github.event_name == 'push' && github.ref_type == 'tag' && github.ref_name == needs.build.outputs.latest_tag) - name: Push translatable strings - needs: [build] - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - path: 'qiskit' - - - uses: actions/download-artifact@v3 - with: - name: qiskit-translatables - path: 'deploy' - - - name: Deploy translations - id: ssh_key - run: qiskit/tools/deploy_translatable_strings.sh "${{ github.workspace }}/deploy" - env: - encrypted_deploy_po_branch_key: ${{ secrets.ENCRYPTED_DEPLOY_PO_BRANCH_KEY }} - encrypted_deploy_po_branch_iv: ${{ secrets.ENCRYPTED_DEPLOY_PO_BRANCH_IV }} diff --git a/.github/workflows/neko.yml b/.github/workflows/neko.yml index bda60fd39790..30921c8b051b 100644 --- a/.github/workflows/neko.yml +++ b/.github/workflows/neko.yml @@ -16,4 +16,6 @@ jobs: - uses: Qiskit/qiskit-neko@main with: test_selection: terra - repo_install_command: "pip install -c constraints.txt ." + # We have to forcibly uninstall any old version of qiskit or qiskit-terra during the + # changeover, because it's not possible to safely upgrade an existing installation to 1.0. + repo_install_command: "pip uninstall qiskit qiskit-terra && pip install -c constraints.txt ." diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 336b86c8db52..99a46a97b63e 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -21,7 +21,7 @@ jobs: python-version: '3.10' - uses: dtolnay/rust-toolchain@stable - name: Build wheels - uses: pypa/cibuildwheel@v2.13.0 + uses: pypa/cibuildwheel@v2.16.2 - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl @@ -42,7 +42,7 @@ jobs: python-version: '3.10' - uses: dtolnay/rust-toolchain@stable - name: Build wheels - uses: pypa/cibuildwheel@v2.13.0 + uses: pypa/cibuildwheel@v2.16.2 env: CIBW_BEFORE_ALL: rustup target add aarch64-apple-darwin CIBW_ARCHS_MACOS: arm64 universal2 @@ -91,7 +91,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.13.0 + uses: pypa/cibuildwheel@v2.16.2 env: CIBW_ARCHS_LINUX: s390x CIBW_TEST_SKIP: "cp*" @@ -124,7 +124,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.13.0 + uses: pypa/cibuildwheel@v2.16.2 env: CIBW_ARCHS_LINUX: ppc64le CIBW_TEST_SKIP: "cp*" @@ -157,7 +157,7 @@ jobs: with: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.13.0 + uses: pypa/cibuildwheel@v2.16.2 env: CIBW_ARCHS_LINUX: aarch64 - uses: actions/upload-artifact@v3 @@ -168,7 +168,7 @@ jobs: with: packages-dir: wheelhouse/ sdist: - name: Build and publish terra sdist + name: Build and publish sdist runs-on: ${{ matrix.os }} needs: ["upload_shared_wheels"] environment: release @@ -185,36 +185,8 @@ jobs: with: python-version: '3.10' - name: Install deps - run: pip install -U twine setuptools-rust wheel build + run: pip install -U build - name: Build sdist run: python -m build . --sdist - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - metapackage: - name: Build and publish terra sdist - runs-on: ${{ matrix.os }} - needs: ["sdist"] - environment: release - permissions: - id-token: write - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest] - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - name: Install Python - with: - python-version: '3.10' - - name: Install deps - run: pip install -U twine setuptools-rust wheel build - - name: Build packages - run: | - set -e - cd qiskit_pkg - python -m build . - - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - packages-dir: qiskit_pkg/dist diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49d0d0ee376a..84259495d8d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,23 +1,20 @@ # Contributing -First read the overall project contributing guidelines. These are all -included in the qiskit documentation: +Qiskit is an open-source project committed to bringing quantum computing to +people of all backgrounds. This page describes how you can join the Qiskit +community in this goal. -https://qiskit.org/documentation/contributing_to_qiskit.html -## Contributing to Qiskit Terra - -In addition to the general guidelines there are specific details for -contributing to terra, these are documented below. - -### Contents +## Contents +* [Before you start](#before-you-start) * [Choose an issue to work on](#Choose-an-issue-to-work-on) -* [Pull request checklist](#pull-request-checklist) +* [Set up Python virtual development environment](#set-up-python-virtual-development-environment) +* [Installing Qiskit from source](#installing-qiskit-from-source) +* [Issues and pull requests](#issues-and-pull-requests) +* [Contributor Licensing Agreement](#contributor-licensing-agreement) * [Changelog generation](#changelog-generation) -* [Release Notes](#release-notes) -* [Installing Qiskit Terra from source](#installing-qiskit-terra-from-source) -* [Test](#test) - * [Snapshot testing for visualizations](#snapshot-testing-for-visualizations) +* [Release notes](#release-notes) +* [Testing](#testing) * [Style and Lint](#style-and-lint) * [Development Cycle](#development-cycle) * [Branches](#branches) @@ -29,12 +26,142 @@ contributing to terra, these are documented below. * [Checking for optionals](#checking-for-optionals) * [Dealing with git blame ignore list](#dealing-with-the-git-blame-ignore-list) -### Choose an issue to work on -Qiskit Terra uses the following labels to help non-maintainers find issues best suited to their interests and experience level: -* [good first issue](https://github.com/Qiskit/qiskit-terra/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) - these issues are typically the simplest available to work on, perfect for newcomers. They should already be fully scoped, with a clear approach outlined in the descriptions. -* [help wanted](https://github.com/Qiskit/qiskit-terra/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) - these issues are generally more complex than good first issues. They typically cover work that core maintainers don't currently have capacity to implement and may require more investigation/discussion. These are a great option for experienced contributors looking for something a bit more challenging. -* [short project](https://github.com/Qiskit/qiskit-terra/issues?q=is%3Aopen+is%3Aissue+label%3A%22short+project%22) - these issues are bigger pieces of work that require greater time commitment. Good options for hackathons, internship projects etc. +## Before you start + +If you are new to Qiskit contributing we recommend you do the following before diving into the code: + +* Read the [Code of Conduct](https://github.com/Qiskit/qiskit/blob/main/CODE_OF_CONDUCT.md) +* Familiarize yourself with the Qiskit community (via [Slack](https://qisk.it/join-slack), + [Stack Exchange](https://quantumcomputing.stackexchange.com), [GitHub](https://github.com/qiskit-community/feedback/discussions) etc.) + + +## Choose an issue to work on +Qiskit uses the following labels to help non-maintainers find issues best suited to their interests and experience level: + +* [good first issue](https://github.com/Qiskit/qiskit/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) - these issues are typically the simplest available to work on, ideal for newcomers. They should already be fully scoped, with a clear approach outlined in the descriptions. +* [help wanted](https://github.com/Qiskit/qiskit/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) - these issues are generally more complex than good first issues. They typically cover work that core maintainers don't currently have capacity to implement and may require more investigation/discussion. These are a great option for experienced contributors looking for something a bit more challenging. +* [short project](https://github.com/Qiskit/qiskit/issues?q=is%3Aopen+is%3Aissue+label%3A%22short+project%22) - these issues are bigger pieces of work that require greater time commitment. Good options for hackathons, internship projects etc. + + +## Set up Python virtual development environment + +Virtual environments are used for Qiskit development to isolate the development environment +from system-wide packages. This way, we avoid inadvertently becoming dependent on a +particular system configuration. For developers, this also makes it easy to maintain multiple +environments (e.g. one per supported Python version, for older versions of Qiskit, etc.). + + + +### Set up a Python venv + +All Python versions supported by Qiskit include built-in virtual environment module +[venv](https://docs.python.org/3/tutorial/venv.html). + +Start by creating a new virtual environment with `venv`. The resulting +environment will use the same version of Python that created it and will not inherit installed +system-wide packages by default. The specified folder will be created and is used to hold the environment's +installation. It can be placed anywhere. For more detail, see the official Python documentation, +[Creation of virtual environments](https://docs.python.org/3/library/venv.html). + +``` +python3 -m venv ~/.venvs/qiskit-dev +``` + +Activate the environment by invoking the appropriate activation script for your system, which can +be found within the environment folder. For example, for bash/zsh: + + +``` +source ~/.venvs/qiskit-dev/bin/activate +``` + +Upgrade pip within the environment to ensure Qiskit dependencies installed in the subsequent sections +can be located for your system. + +``` +pip install -U pip +``` + +``` +pip install -e . +``` + +### Set up a Conda environment + +For Conda users, a new environment can be created as follows. + +``` +conda create -y -n QiskitDevenv python=3 +conda activate QiskitDevenv +``` + +``` +pip install -e . +``` + +## Installing Qiskit from source + +Qiskit is primarily written in Python but there are some core routines +that are written in the [Rust](https://www.rust-lang.org/) programming +language to improve the runtime performance. For the released versions of +qiskit we publish precompiled binaries on the +[Python Package Index](https://pypi.org/) for all the supported platforms +which only requires a functional Python environment to install. However, when +building and installing from source you will need a rust compiler installed. You can do this very easily +using rustup: https://rustup.rs/ which provides a single tool to install and +configure the latest version of the rust compiler. +[Other installation methods](https://forge.rust-lang.org/infra/other-installation-methods.html) +exist too. For Windows users, besides rustup, you will also need install +the Visual C++ build tools so that Rust can link against the system c/c++ +libraries. You can see more details on this in the +[rustup documentation](https://rust-lang.github.io/rustup/installation/windows.html). + +If you use Rustup, it will automatically install the correct Rust version +currently used by the project. + +Once you have a Rust compiler installed, you can rely on the normal Python +build/install steps to install Qiskit. This means you just run +`pip install .` in your local git clone to build and install Qiskit. + +Do note that if you do use develop mode/editable install (via `python setup.py develop` or `pip install -e .`) the Rust extension will be built in debug mode +without any optimizations enabled. This will result in poor runtime performance. +If you'd like to use an editable install with an optimized binary you can +run `python setup.py build_rust --release --inplace` after you install in +editable mode to recompile the rust extensions in release mode. + +Note that in order to run `python setup.py ...` commands you need have build +dependency packages installed in your environment, which are listed in the +`pyproject.toml` file under the `[build-system]` section. + + +## Issues and pull requests + +We use [GitHub pull requests](https://help.github.com/articles/about-pull-requests) to accept +contributions. + +While not required, opening a new issue about the bug you're fixing or the +feature you're working on before you open a pull request is an important step +in starting a discussion with the community about your work. The issue gives us +a place to talk about the idea and how we can work together to implement it in +the code. It also lets the community know what you're working on, and if you +need help, you can reference the issue when discussing it with other community +and team members. + +* For documentation issues relating to pages in the Start, Build, Transpile, Verify, Run, and Migration guides sections of [docs.quantum.ibm.com](https://docs.quantum.ibm.com/), please open an issue in the [Qiskit/documentation repo](https://github.com/Qiskit/documentation/issues/new/choose) rather than the Qiskit/qiskit repo. In other words, any page that DOES NOT have `/api/` in the url should be addressed in the Qiskit/documentation repo. (Exception: the [Migration guide](https://docs.quantum.ibm.com/api/migration-guides) urls contain `/api/` but are managed in the Qiskit/documentation repo.) +* For issues relating to API reference pages (any page that contains `/api/` in the url), please open an issue in the repo specific to that API reference, for example [Qiskit/qiskit](https://github.com/Qiskit/qiskit/issues/new/choose), [Qiskit/qiskit-aer](https://github.com/Qiskit/qiskit-aer/issues/new/choose), or [Qiskit/qiskit-ibm-runtime](https://github.com/Qiskit/qiskit-ibm-runtime/issues/new/choose). + +If you've written some code but need help finishing it, want to get initial +feedback on it prior to finishing it, or want to share it and discuss prior +to finishing the implementation, you can open a *Draft* pull request and prepend +the title with the **\[WIP\]** tag (for Work In Progress). This will indicate +to reviewers that the code in the PR isn't in its final state and will change. +It also means that we will not merge the commit until it is finished. You or a +reviewer can remove the [WIP] tag when the code is ready to be fully reviewed for merging. + +Before marking your Pull Request as "ready for review" make sure you have followed the +PR Checklist below. PRs that adhere to this list are more likely to get reviewed and +merged in a timely manner. ### Pull request checklist @@ -42,7 +169,7 @@ When submitting a pull request and you feel it is ready for review, please ensure that: 1. The code follows the code style of the project and successfully - passes the tests. For convenience, you can execute `tox` locally, + passes the CI tests. For convenience, you can execute `tox` locally, which will run these checks and report any issues. If your code fails the local style checks (specifically the black @@ -55,18 +182,54 @@ please ensure that: If your pull request is adding a new class, function, or module that is intended to be user facing ensure that you've also added those to a documentation `autosummary` index to include it in the api documentation. - For more details you can refer to: - - https://qiskit.org/documentation/contributing_to_qiskit.html#documentation-structure - - 3. If it makes sense for your change that you have added new tests that cover the changes. 4. Ensure that if your change has an end user facing impact (new feature, deprecation, removal etc) that you have added a reno release note for that change and that the PR is tagged for the changelog. +5. All contributors have signed the CLA. +6. The PR has a concise and explanatory title (e.g. `Fixes Issue1234` is a bad title!). +7. If the PR addresses an open issue the PR description includes the `fixes #issue-number` + syntax to link the PR to that issue (**you must use the exact phrasing in order for GitHub + to automatically close the issue when the PR merges**) + +### Code Review + +Code review is done in the open and is open to anyone. While only maintainers have +access to merge commits, community feedback on pull requests is extremely valuable. +It is also a good mechanism to learn about the code base. + +Response times may vary for your PR, it is not unusual to wait a few weeks for a maintainer +to review your work, due to other internal commitments. If you have been waiting over a week +for a review on your PR feel free to tag the relevant maintainer in a comment to politely remind +them to review your work. -### Changelog generation +Please be patient! Maintainers have a number of other priorities to focus on and so it may take +some time for your work to get reviewed and merged. PRs that are in a good shape (i.e. following the [Pull request checklist](#pull-request-checklist)) +are easier for maintainers to review and more likely to get merged in a timely manner. Please also make +sure to always be kind and respectful in your interactions with maintainers and other contributors, you can read +[the Qiskit Code of Conduct](https://github.com/Qiskit/qiskit/blob/main/CODE_OF_CONDUCT.md). + + +## Contributor Licensing Agreement + +Before you can submit any code, all contributors must sign a +contributor license agreement (CLA). By signing a CLA, you're attesting +that you are the author of the contribution, and that you're freely +contributing it under the terms of the Apache-2.0 license. + +When you contribute to the Qiskit project with a new pull request, +a bot will evaluate whether you have signed the CLA. If required, the +bot will comment on the pull request, including a link to accept the +agreement. The [individual CLA](https://qiskit.org/license/qiskit-cla.pdf) +document is available for review as a PDF. + +Note: If your contribution is part of your employment or your contribution +is the property of your employer, then you will more than likely need to sign a +[corporate CLA](https://qiskit.org/license/qiskit-corporate-cla.pdf) too and +email it to us at . + +## Changelog generation The changelog is automatically generated as part of the release process automation. This works through a combination of the git log and the pull @@ -89,10 +252,10 @@ The current categories for each label are as follows: | Changelog: Removal | Removed | | Changelog: Bugfix | Fixed | -### Release Notes +## Release notes When making any end user facing changes in a contribution we have to make sure -we document that when we release a new version of qiskit-terra. The expectation +we document that when we release a new version of qiskit. The expectation is that if your code contribution has user facing changes that you will write the release documentation for these changes. This documentation must explain what was changed, why it was changed, and how users can either use or adapt @@ -109,7 +272,7 @@ documentation at the same time as the code. To accomplish this we use the [reno](https://docs.openstack.org/reno/latest/) tool which enables a git based workflow for writing and compiling release notes. -#### Adding a new release note +### Adding a new release note Making a new release note is quite straightforward. Ensure that you have reno installed with: @@ -181,7 +344,7 @@ After you've finished writing your release notes you'll want to add the note file to your commit with `git add` and commit them to your PR branch to make sure they're included with the code in your PR. -##### Linking to issues +#### Linking to issues If you need to link to an issue or other github artifact as part of the release note this should be done using an inline link with the text being the issue @@ -192,7 +355,7 @@ as: fixes: - | Fixes a race condition in the function ``foo()``. Refer to - `#12345 ` for more + `#12345 ` for more details. ``` @@ -202,7 +365,7 @@ After release notes have been added, you can use reno to see what the full outpu of the release notes is. In general the output from reno that we'll get is a rst (ReStructuredText) file that can be compiled by [sphinx](https://www.sphinx-doc.org/en/master/). To generate the rst file you -use the ``reno report`` command. If you want to generate the full terra release +use the ``reno report`` command. If you want to generate the full release notes for all releases (since we started using reno during 0.9) you just run: reno report @@ -219,48 +382,14 @@ https://github.com/Qiskit/qiskit/blob/master/docs/release_notes.rst) #### Building release notes locally -Building The release notes are part of the standard qiskit-terra documentation +Building The release notes are part of the standard qiskit documentation builds. To check what the rendered html output of the release notes will look like for the current state of the repo you can run: `tox -edocs` which will build all the documentation into `docs/_build/html` and the release notes in particular will be located at `docs/_build/html/release_notes.html` -## Installing Qiskit Terra from source - -Qiskit Terra is primarily written in Python but there are some core routines -that are written in the [Rust](https://www.rust-lang.org/) programming -language to improve the runtime performance. For the released versions of -qiskit-terra we publish precompiled binaries on the -[Python Package Index](https://pypi.org/) for all the supported platforms -which only requires a functional Python environment to install. However, when -building and installing from source you will need a rust compiler installed. You can do this very easily -using rustup: https://rustup.rs/ which provides a single tool to install and -configure the latest version of the rust compiler. -[Other installation methods](https://forge.rust-lang.org/infra/other-installation-methods.html) -exist too. For Windows users, besides rustup, you will also need install -the Visual C++ build tools so that Rust can link against the system c/c++ -libraries. You can see more details on this in the -[rustup documentation](https://rust-lang.github.io/rustup/installation/windows.html). - -If you use Rustup, it will automatically install the correct Rust version -currently used by the project. - -Once you have a Rust compiler installed, you can rely on the normal Python -build/install steps to install Qiskit Terra. This means you just run -`pip install .` in your local git clone to build and install Qiskit Terra. - -Do note that if you do use develop mode/editable install (via `python setup.py develop` or `pip install -e .`) the Rust extension will be built in debug mode -without any optimizations enabled. This will result in poor runtime performance. -If you'd like to use an editable install with an optimized binary you can -run `python setup.py build_rust --release --inplace` after you install in -editable mode to recompile the rust extensions in release mode. - -Note that in order to run `python setup.py ...` commands you need have build -dependency packages installed in your environment, which are listed in the -`pyproject.toml` file under the `[build-system]` section. - -## Test +## Testing Once you've made a code change, it is important to verify that your change does not break any existing tests and that any new tests that you've added @@ -389,7 +518,7 @@ you will need to check that your changes don't break any snapshot tests, and add new tests where necessary. You can do this as follows: 1. Make sure you have pushed your latest changes to your remote branch. -2. Go to link: `https://mybinder.org/v2/gh///?urlpath=apps/test/ipynb/mpl_tester.ipynb`. For example, if your GitHub username is `username`, your forked repo has the same name the original, and your branch is `my_awesome_new_feature`, you should visit https://mybinder.org/v2/gh/username/qiskit-terra/my_awesome_new_feature?urlpath=apps/test/ipynb/mpl_tester.ipynb. +2. Go to link: `https://mybinder.org/v2/gh///?urlpath=apps/test/ipynb/mpl_tester.ipynb`. For example, if your GitHub username is `username`, your forked repo has the same name the original, and your branch is `my_awesome_new_feature`, you should visit https://mybinder.org/v2/gh/username/qiskit/my_awesome_new_feature?urlpath=apps/test/ipynb/mpl_tester.ipynb. This opens a Jupyter Notebook application running in the cloud that automatically runs the snapshot tests (note this may take some time to finish loading). 3. Each test result provides a set of 3 images (left: reference image, middle: your test result, right: differences). In the list of tests the passed tests are collapsed and failed tests are expanded. If a test fails, you will see a situation like this: @@ -400,7 +529,7 @@ can sometimes result in minor changes elsewhere to spacing etc. In these cases you just need to update the reference images as follows: - download the mismatched images (link at top of Jupyter Notebook output) - unzip the folder - - copy and paste the new images into `qiskit-terra/test/ipynb/mpl/references`, + - copy and paste the new images into `qiskit/test/ipynb/mpl/references`, replacing the existing reference images - add, commit and push your changes, then restart the Jupyter Notebook app in your browser. The tests should now pass. @@ -414,7 +543,7 @@ you just need to update the reference images as follows: Screenshot_2021-03-26_at_15 38 31 - - download the new images, then copy and paste into `qiskit-terra/test/ipynb/mpl/references` + - download the new images, then copy and paste into `qiskit/test/ipynb/mpl/references` - add, commit and push your changes, restart the Jupyter Notebook app in your browser. The new tests should now pass. @@ -422,14 +551,14 @@ Note: If you have run `test/ipynb/mpl_tester.ipynb` locally it is possible some ## Style and lint -Qiskit Terra uses three tools for verify code formatting and lint checking. The +Qiskit uses three tools for verify code formatting and lint checking. The first tool is [black](https://github.com/psf/black) which is a code formatting tool that will automatically update the code formatting to a consistent style. The second tool is [pylint](https://www.pylint.org/) which is a code linter which does a deeper analysis of the Python code to find both style issues and potential bugs and other common issues in Python. The third tool is the linter [ruff](https://github.com/charliermarsh/ruff), which has been recently -introduced into Qiskit Terra on an experimental basis. Only a very small number +introduced into Qiskit on an experimental basis. Only a very small number of rules are enabled. You can check that your local modifications conform to the style rules by @@ -451,9 +580,9 @@ rather than via `tox`. If you have installed the development packages in your py `pip install -r requirements-dev.txt`, then `ruff` and `black` will be available and can be run from the command line. See [`tox.ini`](tox.ini) for how `tox` invokes them. -## Development Cycle +## Development cycle -The development cycle for qiskit-terra is all handled in the open using +The development cycle for qiskit is all handled in the open using the project boards in Github for project management. We use milestones in Github to track work for specific releases. The features or other changes that we want to include in a release will be tagged and discussed in Github. @@ -464,12 +593,12 @@ previous version in the release notes. * `main`: -The main branch is used for development of the next version of qiskit-terra. +The main branch is used for development of the next version of qiskit. It will be updated frequently and should not be considered stable. The API can and will change on main as we introduce and refine new features. * `stable/*` branches: -Branches under `stable/*` are used to maintain released versions of qiskit-terra. +Branches under `stable/*` are used to maintain released versions of qiskit. It contains the version of the code corresponding to the latest release for that minor version on pypi. For example, stable/0.8 contains the code for the 0.8.2 release on pypi. The API on these branches are stable and the only changes @@ -486,7 +615,7 @@ PR is opened after this date it will not be considered for inclusion in that release. Note, that meeting these deadlines does not guarantee inclusion in a release: they are preconditions. You can refer to the milestone page for each release to see these dates for each release (for example for 0.21.0 the page is: -https://github.com/Qiskit/qiskit-terra/milestone/23). +https://github.com/Qiskit/qiskit/milestone/23). After the proposal freeze a release review period will begin, during this time release candidate PRs will be reviewed as we finalize the feature set and merge @@ -501,7 +630,7 @@ is a need additional release candidates can be published from `stable/*` and whe release is ready a full release will be tagged and published from `stable/*`. ## Adding deprecation warnings -The qiskit-terra code is part of Qiskit and, therefore, the [Qiskit Deprecation Policy](https://qiskit.org/documentation/contributing_to_qiskit.html#deprecation-policy) fully applies here. Additionally, qiskit-terra does not allow `DeprecationWarning`s in its testsuite. If you are deprecating code, you should add a test to use the new/non-deprecated method (most of the time based on the existing test of the deprecated method) and alter the existing test to check that the deprecated method still works as expected, [using `assertWarns`](https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertWarns). The `assertWarns` context will silence the deprecation warning while checking that it raises. +The qiskit code is part of Qiskit and, therefore, the [Qiskit Deprecation Policy](https://qiskit.org/documentation/contributing_to_qiskit.html#deprecation-policy) fully applies here. Additionally, qiskit does not allow `DeprecationWarning`s in its testsuite. If you are deprecating code, you should add a test to use the new/non-deprecated method (most of the time based on the existing test of the deprecated method) and alter the existing test to check that the deprecated method still works as expected, [using `assertWarns`](https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertWarns). The `assertWarns` context will silence the deprecation warning while checking that it raises. For example, if `Obj.method1` is being deprecated in favour of `Obj.method2`, the existing test (or tests) for `method1` might look like this: @@ -525,25 +654,25 @@ def test_method2(self): self.assertEqual(result, ) ``` -`test_method1_deprecated` can be removed after `Obj.method1` is removed (following the [Qiskit Deprecation Policy](https://qiskit.org/documentation/contributing_to_qiskit.html#deprecation-policy)). +`test_method1_deprecated` can be removed after `Obj.method1` is removed (following the [Qiskit Deprecation Policy](./DEPRECATION.md)). ## Using dependencies -We distinguish between "requirements" and "optional dependencies" in qiskit-terra. -A requirement is a package that is absolutely necessary for core functionality in qiskit-terra, such as Numpy or Scipy. +We distinguish between "requirements" and "optional dependencies" in qiskit. +A requirement is a package that is absolutely necessary for core functionality in qiskit, such as Numpy or Scipy. An optional dependency is a package that is used for specialized functionality, which might not be needed by all users. If a new feature has a new dependency, it is almost certainly optional. ### Adding a requirement -Any new requirement must have broad system support; it needs to be supported on all the Python versions and operating systems that qiskit-terra supports. +Any new requirement must have broad system support; it needs to be supported on all the Python versions and operating systems that qiskit supports. It also cannot impose many version restrictions on other packages. -Users often install qiskit-terra into virtual environments with many different packages in, and we need to ensure that neither we, nor any of our requirements, conflict with their other packages. +Users often install qiskit into virtual environments with many different packages in, and we need to ensure that neither we, nor any of our requirements, conflict with their other packages. When adding a new requirement, you must add it to [`requirements.txt`](requirements.txt) with as loose a constraint on the allowed versions as possible. ### Adding an optional dependency -New features can also use optional dependencies, which might be used only in very limited parts of qiskit-terra. +New features can also use optional dependencies, which might be used only in very limited parts of qiskit. These are not required to use the rest of the package, and so should not be added to `requirements.txt`. Instead, if several optional dependencies are grouped together to provide one feature, you can consider adding an "extra" to the package metadata, such as the `visualization` extra that installs Matplotlib and Seaborn (amongst others). To do this, modify the [`setup.py`](setup.py) file, adding another entry in the `extras_require` keyword argument to `setup()` at the bottom of the file. @@ -552,7 +681,7 @@ You should also add a new "tester" to [`qiskit.utils.optionals`](qiskit/utils/op ### Checking for optionals -You cannot `import` an optional dependency at the top of a file, because if it is not installed, it will raise an error and qiskit-terra will be unusable. +You cannot `import` an optional dependency at the top of a file, because if it is not installed, it will raise an error and qiskit will be unusable. We also largely want to avoid importing packages until they are actually used; if we import a lot of packages during `import qiskit`, it becomes sluggish for the user if they have a large environment. Instead, you should use [one of the "lazy testers" for optional dependencies](https://qiskit.org/documentation/apidoc/utils.html#module-qiskit.utils.optionals), and import your optional dependency inside the function or class that uses it, as in the examples within that link. Very lightweight _requirements_ can be imported at the tops of files, but even this should be limited; it's always ok to `import numpy`, but Scipy modules are relatively heavy, so only import them within functions that use them. @@ -560,7 +689,7 @@ Very lightweight _requirements_ can be imported at the tops of files, but even t ## Dealing with the git blame ignore list -In the qiskit-terra repository we maintain a list of commits for git blame +In the qiskit repository we maintain a list of commits for git blame to ignore. This is mostly commits that are code style changes that don't change the functionality but just change the code formatting (for example, when we migrated to use black for code formatting). This file, diff --git a/Cargo.lock b/Cargo.lock index ebf4186064d3..b007e6dc746a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,9 +92,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -109,9 +109,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -136,12 +136,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "rayon", ] @@ -162,21 +162,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "matrixmultiply" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" dependencies = [ "autocfg", "rawpointer", @@ -247,9 +247,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", @@ -301,12 +301,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 1.9.3", + "indexmap 2.1.0", ] [[package]] @@ -327,9 +327,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -341,8 +341,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b" dependencies = [ "cfg-if", - "hashbrown 0.14.2", - "indexmap 2.0.2", + "hashbrown 0.14.3", + "indexmap 2.1.0", "indoc", "libc", "memoffset", @@ -403,7 +403,7 @@ dependencies = [ name = "qiskit-qasm2" version = "1.0.0" dependencies = [ - "hashbrown 0.14.2", + "hashbrown 0.14.3", "pyo3", ] @@ -412,8 +412,8 @@ name = "qiskit_accelerate" version = "1.0.0" dependencies = [ "ahash", - "hashbrown 0.14.2", - "indexmap 2.0.2", + "hashbrown 0.14.3", + "indexmap 2.1.0", "ndarray", "num-bigint", "num-complex", @@ -430,9 +430,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -525,9 +525,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags", ] @@ -546,8 +546,8 @@ checksum = "72abf7976bc09a30391248b3c6509338b235c02b0e9b0bf8af686c289cad3f45" dependencies = [ "ahash", "fixedbitset", - "hashbrown 0.14.2", - "indexmap 2.0.2", + "hashbrown 0.14.3", + "indexmap 2.1.0", "num-traits", "petgraph", "priority-queue", @@ -565,15 +565,15 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -582,15 +582,15 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unindent" @@ -612,9 +612,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "windows-targets" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -627,60 +627,60 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "zerocopy" -version = "0.7.14" +version = "0.7.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69c48d63854f77746c68a5fbb4aa17f3997ece1cb301689a257af8cb80610d21" +checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.14" +version = "0.7.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c258c1040279e4f88763a113de72ce32dde2d50e2a94573f15dd534cea36a16d" +checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 02e453ce587b..b950aabe7111 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = ["crates/*"] +resolver = "2" [workspace.package] version = "1.0.0" diff --git a/DEPRECATION.md b/DEPRECATION.md new file mode 100644 index 000000000000..5b0de6611261 --- /dev/null +++ b/DEPRECATION.md @@ -0,0 +1,227 @@ +# Deprecation Policy + +Many users and other packages depend on different parts of Qiskit. We must +make sure that whenever we make changes to the code, we give users ample time to +adjust without breaking code that they have already written. + +Most importantly: *do not* change any interface that is public-facing unless we +absolutely have to. Adding things is ok, taking things away is annoying for +users but can be handled reasonably with plenty notice, but changing behavior +generally means users cannot write code that will work with two subsequent +versions of Qiskit, which is not acceptable. + +Beware that users will often be using functions, classes and methods that we, +the Qiskit developers, may consider internal or not widely used. Do not make +assumptions that "this is buried, so nobody will be using it"; if it is public, +it is subject to the policy. The only exceptions here are functions and modules +that are explicitly internal, *i.e.* those whose names begin with a leading +underscore (`_`). + +The guiding principles are: + +- we must not remove or change code without active warnings for least three + months or two complete version cycles; + +- there must always be a way to achieve valid goals that does not issue any + warnings; + +- never assume that a function that isn't explicitly internal isn't in use; + +- all deprecations, changes and removals are considered API changes, and can + only occur in minor releases not patch releases, per the [stable branch policy](https://github.com/Qiskit/qiskit/blob/main/MAINTAINING.md#stable-branch-policy). + + +## Removing a feature + +When removing a feature (for example a class, function or function parameter), +we will follow this procedure: + +- The alternative path must be in place for one minor version before any + warnings are issued. For example, if we want to replace the function `foo()` + with `bar()`, we must make at least one release with both functions before + issuing any warnings within `foo()`. You may issue + `PendingDeprecationWarning`s from the old paths immediately. + + *Reason*: we need to give people time to swap over without breaking their + code as soon as they upgrade. + +- After the alternative path has been in place for at least one minor version, + [issue the deprecation warnings](#issuing-deprecation-warnings). Add a + release note with a `deprecations` section listing all deprecated paths, + their alternatives, and the reason for deprecation. [Update the tests to test the warnings](#testing-deprecated-functionality). + + *Reason*: removals must be highly visible for at least one version, to + minimize the surprise to users when they actually go. + +- Set a removal date for the old feature, and remove it (and the warnings) when + reached. This must be at least three months after the version with the + warnings was first released, and cannot be the minor version immediately + after the warnings. Add an `upgrade` release note that lists all the + removals. For example, if the alternative path was provided in `0.19.0` + and the warnings were added in `0.20.0`, the earliest version for removal + is `0.22.0`, even if `0.21.0` was released more than three months after + `0.20.0`. + + **Note: These are _minimum_** requirements. For removal of significant or core features, give + users at least an extra minor version if not longer.** + + *Reason*: there needs to be time for users to see these messages, and to give + them time to adjust. Not all users will update their version of Qiskit + immediately, and some may skip minor versions. + +When a feature is marked as deprecated it is slated for removal, but users +should still be able to rely on it to work correctly. We consider a feature +marked "deprecated" as frozen; we commit to maintaining it with critical bug +fixes until it is removed, but we won't merge new functionality to it. + + +## Changing behavior + + +Changing behavior without a removal is particularly difficult to manage, because +we need to have both options available for two versions, and be able to issue +warnings. For example, changing the type of the return value from a function +will almost invariably involve making an API break, which is frustrating for +users and makes it difficult for them to use Qiskit. + +The best solution here is often to make a new function, and then use [the procedures for removal](#removing-features) above. + +If you absolutely must change the behavior of existing code (other than fixing +bugs), you will need to use your best judgment to apply the guiding principles +at the top of this document. The most appropriate warning for behavioral +changes is usually `FutureWarning`. Some possibilities for how to effect a +change: + +- If you are changing the default behavior of a function, consider adding a + keyword argument to select between old and new behaviors. When it comes time, + you can issue a `FutureWarning` if the keyword argument is not given + (*e.g.* if it is `None`), saying that the new value will soon become the + default. You will need to go through the normal deprecation period for + removing this keyword argument after you have made the behavior change. This + will take at least six months to go through both cycles. + +- If you need to change the return type of a function, consider adding a new + function that returns the new type, and then follow the procedures for + deprecating the old function. + +- If you need to accept a new input that you cannot distinguish from an existing + possibility because of its type, consider letting it be passed by a different + keyword argument, or add a second function that only accepts the new form. + + + +## Issuing deprecation warnings + +The proper way to raise a deprecation warning is to use the decorators `@deprecate_arg` and +`@deprecate_func` from `qiskit.utils.deprecation`. These will generate a standardized message and +and add the deprecation to that function's docstring so that it shows up in the docs. + + +```python +from qiskit.utils.deprecation import deprecate_arg, deprecate_func + +@deprecate_func(since="0.24.0", additional_msg="No replacement is provided.") +def deprecated_func(): + pass + +@deprecate_arg("bad_arg", new_alias="new_name", since="0.24.0") +def another_func(bad_arg: str, new_name: str): + pass +``` + +Usually, you should set `additional_msg: str` with the format `"Instead, use ..."` so that +people know how to migrate. Read those functions' docstrings for additional arguments like +`pending: bool` and `predicate`. + +If you are deprecating outside the main Qiskit repo, set `package_name` to match your package. +Alternatively, if you prefer to use your own decorator helpers, then have them call +`add_deprecation_to_docstring` from `qiskit.utils.deprecation`. + +If `@deprecate_func` and `@deprecate_arg` cannot handle your use case, consider improving +them. Otherwise, you can directly call the `warn` function +from the [warnings module in the Python standard library](https://docs.python.org/3/library/warnings.html), +using the category `DeprecationWarning`. For example: + +```python +import warnings + +def deprecated_function(): + warnings.warn( + "The function qiskit.deprecated_function() is deprecated since " + "Qiskit 0.44.0, and will be removed 3 months or more later. " + "Instead, you should use qiskit.other_function().", + category=DeprecationWarning, + stacklevel=2, + ) + # ... the rest of the function ... + +``` + +Make sure you include the version of the package that introduced the deprecation +warning (so maintainers can easily see when it is valid to remove it), and what +the alternative path is. + +Take note of the `stacklevel` argument. This controls which function is +accused of being deprecated. Setting `stacklevel=1` (the default) means the +warning will blame the `warn` function itself, while `stacklevel=2` will +correctly blame the containing function. It is unusual to set this to anything +other than `2`, but can be useful if you use a helper function to issue the +same warning in multiple places. + + +## Testing deprecated functionality + +Whenever you add deprecation warnings, you will need to update tests involving +the functionality. The test suite should fail otherwise, because of the new +warnings. We must continue to test deprecated functionality throughout the +deprecation period, to ensure that it still works. + +To update the tests, you need to wrap each call of deprecated behavior in its +own assertion block. For subclasses of `unittest.TestCase` (which all Qiskit +test cases are), this is done by: + + +```python +class MyTestSuite(QiskitTestCase): + def test_deprecated_function(self): + with self.assertWarns(DeprecationWarning): + output = deprecated_function() + # ... do some things with output ... + self.assertEqual(output, expected) +``` + +## Documenting deprecations and breaking changes + +It is important to warn the user when your breaking changes are coming. + +`@deprecate_arg` and `@deprecate_func` will automatically add the deprecation to the docstring +for the function so that it shows up in docs. + +If you are not using those decorators, you should directly add a [Sphinx deprecated directive](https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-deprecated): + + +```python +def deprecated_function(): + """ + Short description of the deprecated function. + + .. deprecated:: 0.44.0 + The function qiskit.deprecated_function() is deprecated since + Qiskit 0.44.0, and will be removed 3 months or more later. + Instead, you should use qiskit.other_function(). + + + """ + # ... the rest of the function ... +``` + + +You should also document the deprecation in the changelog by using Reno. Explain the deprecation +and how to migrate. + +In particular situations where a deprecation or change might be a major disruptor for users, a +*migration guide* might be needed. Please write these guides in Qiskit's documentation at +https://github.com/Qiskit/documentation/tree/main/docs/api/migration-guides. Once +the migration guide is written and published, deprecation +messages and documentation should link to it (use the `additional_msg` argument for +`@deprecate_arg` and `@deprecate_func`). diff --git a/docs/maintainers_guide.rst b/MAINTAINING.md similarity index 63% rename from docs/maintainers_guide.rst rename to MAINTAINING.md index e8c77125395f..bf51e67fe79d 100644 --- a/docs/maintainers_guide.rst +++ b/MAINTAINING.md @@ -1,17 +1,13 @@ -################# -Maintainers Guide -################# +# Maintainers Guide This document defines a *maintainer* as a contributor with merge privileges. The information detailed here is mostly related to Qiskit releases and other internal processes. -.. _stable_branch_policy: -Stable Branch Policy -==================== +## Stable Branch Policy The stable branch is intended to be a safe source of fixes for high-impact -bugs and security issues that have been fixed on master since a +bugs and security issues that have been fixed on `main` since a release. When reviewing a stable branch PR, we must balance the risk of any given patch with the value that it will provide to users of the stable branch. Only a limited class of changes are appropriate for @@ -28,27 +24,30 @@ change: - How self-contained the fix is: if it fixes a significant issue but also refactors a lot of code, it's probably worth thinking about what a less risky fix might look like. -- Whether the fix is already on ``main``: a change must be a backport of - a change already merged onto master, unless the change simply does - not make sense on master. +- Whether the fix is already on `main`: a change must be a backport of + a change already merged onto `main`, unless the change simply does + not make sense on `main`. -Backporting ------------ +### Backporting -When a PR tagged with ``stable backport potential`` is merged, or when a -merged PR is given that tag, the `Mergify bot `__ will +When a PR tagged with `stable backport potential` is merged, or when a +merged PR is given that tag, the [Mergify bot](https://mergify.com) will open a PR to the current stable branch. You can review and merge this PR like normal. -Documentation Structure -======================= +## Documentation Structure The way documentation is structured in Qiskit is to push as much of the actual documentation into the docstrings as possible. This makes it easier for additions and corrections to be made during development, because the majority -of the documentation lives near the code being changed. +of the documentation lives near the code being changed. These docstrings are then pulled into +the API Reference section of https://docs.quantum-computing.ibm.com. Refer to https://qiskit.github.io/qiskit_sphinx_theme/apidocs/index.html for how to create and write effective API documentation, such as setting up the RST files and docstrings. + +If changes you are making affect non-API reference content in https://docs.quantum-computing.ibm.com +you can open an issue (or better yet a PR) to update the relevant page in https://github.com/Qiskit/documentation. +You can also use this repo to suggest or contribute brand new content beyond updates to the API reference. \ No newline at end of file diff --git a/README.md b/README.md index c54f433b4846..a153957cb221 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It also contains a transpiler that supports optimizing quantum circuits and a qu For more details on how to use Qiskit, refer to the documentation located here: - + ## Installation @@ -51,7 +51,7 @@ qc_example.cx(0,1) # 0th-qubit-Controlled-NOT gate on 1st qubit qc_example.cx(0,2) # 0th-qubit-Controlled-NOT gate on 2nd qubit ``` -This simple example makes an entangled state known as a [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) $(|000\rangle + |111\rangle)/\sqrt{2}$. It uses the standard quantum gates: Hadamard gate (`h`), Phase gate (`p`), and CNOT gate (`cx`). +This simple example makes an entangled state known as a [GHZ state](https://en.wikipedia.org/wiki/Greenberger%E2%80%93Horne%E2%80%93Zeilinger_state) $(|000\rangle + i|111\rangle)/\sqrt{2}$. It uses the standard quantum gates: Hadamard gate (`h`), Phase gate (`p`), and CNOT gate (`cx`). Once you've made your first quantum circuit, choose which primitive function you will use. Starting with `sampler`, we use `measure_all(inplace=False)` to get a copy of the circuit in which all the qubits are measured: diff --git a/asv.conf.json b/asv.conf.json index 70e736e26f75..9725442b6e9a 100644 --- a/asv.conf.json +++ b/asv.conf.json @@ -4,21 +4,20 @@ "project_url": "https://qiskit.org", "repo": ".", "install_command": [ - "in-dir={env_dir} python -mpip install {wheel_file}[all] python-constraint qiskit-experiments==0.3.0" + "in-dir={env_dir} python -m pip install {wheel_file}[all] python-constraint qiskit-experiments==0.3.0" ], "uninstall_command": [ - "return-code=any python -mpip uninstall -y {project}" + "return-code=any python -m pip uninstall -y qiskit qiskit-terra" ], "build_command": [ - "pip install -U setuptools-rust", - "python setup.py build_rust --release", - "PIP_NO_BUILD_ISOLATION=false python -mpip wheel --no-deps --no-index -w {build_cache_dir} {build_dir}" + "python -m pip install -U build", + "python -m build --outdir {build_cache_dir} --wheel {build_dir}" ], "branches": ["main"], "dvcs": "git", "environment_type": "virtualenv", - "show_commit_url": "http://github.com/Qiskit/qiskit-terra/commit/", - "pythons": ["3.8", "3.9", "3.10", "3.11"], + "show_commit_url": "http://github.com/Qiskit/qiskit/commit/", + "pythons": ["3.8", "3.9", "3.10", "3.11", "3.12"], "benchmark_dir": "test/benchmarks", "env_dir": ".asv/env", "results_dir": ".asv/results" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1c85874e9f10..58370138160a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -31,7 +31,7 @@ parameters: - name: "supportedPythonVersions" displayName: "All supported versions of Python" type: object - default: ["3.8", "3.9", "3.10", "3.11"] + default: ["3.8", "3.9", "3.10", "3.11", "3.12"] - name: "minimumPythonVersion" displayName: "Minimum supported version of Python" @@ -41,7 +41,7 @@ parameters: - name: "maximumPythonVersion" displayName: "Maximum supported version of Python" type: string - default: "3.11" + default: "3.12" # These two versions of Python can be chosen somewhat arbitrarily, but we get # slightly better coverage per PR if they're neither the maximum nor minimum @@ -52,11 +52,6 @@ parameters: type: string default: "3.9" - - name: "tutorialsPythonVersion" - displayName: "Version of Python to use to run the tutorials job" - type: string - default: "3.8" - # Sync with 'python-version' in '.github/workflows/docs_deploy.yml'. - name: "documentationPythonVersion" displayName: "Version of Python to use to build Sphinx documentation" @@ -162,18 +157,13 @@ stages: testImages: true # The rest of the PR pipeline is to test the oldest and newest supported - # versions of Python, along with the integration tests (via the tutorials). - # It's very rare for a failure to be specific to an intermediate version of + # versions of Python. It's very rare for a failure to be specific to an intermediate version of # Python, so we just catch those in the cron-job pipeline to reduce the # amount of resources used. - - stage: "Tutorials_and_Tests" + - stage: "Tests" displayName: "Main tests" dependsOn: "Lint_Docs_Prelim_Tests" jobs: - - template: ".azure/tutorials-linux.yml" - parameters: - pythonVersion: ${{ parameters.tutorialsPythonVersion }} - - template: ".azure/test-linux.yml" parameters: pythonVersion: ${{ parameters.maximumPythonVersion }} diff --git a/constraints.txt b/constraints.txt index 07b9862e426c..a879e5945844 100644 --- a/constraints.txt +++ b/constraints.txt @@ -2,17 +2,7 @@ # 4.0+. The pin can be removed after nbformat is updated. jsonschema==3.2.0 -# Numpy 1.25 deprecated some behaviours that we used, and caused the isometry -# tests to flake. See https://github.com/Qiskit/qiskit-terra/issues/10305, -# remove pin when resolving that. -numpy<1.25 - # Scipy 1.11 seems to have caused an instability in the Weyl coordinates # eigensystem code for one of the test cases. See # https://github.com/Qiskit/qiskit-terra/issues/10345 for current details. -scipy<1.11 - -# Aer 0.13 causes several randomised tests to begin failing, and some -# `QuantumInstance` use of noise models to raise exceptions. These need fixes -# on Terra. -qiskit-aer==0.12.2 +scipy<1.11; python_version<'3.12' diff --git a/crates/accelerate/Cargo.toml b/crates/accelerate/Cargo.toml index b716bbc7f0b4..a1bcb7ed0532 100644 --- a/crates/accelerate/Cargo.toml +++ b/crates/accelerate/Cargo.toml @@ -43,5 +43,5 @@ version = "0.14.0" features = ["rayon"] [dependencies.indexmap] -version = "2.0.2" +version = "2.1.0" features = ["rayon"] diff --git a/crates/accelerate/src/euler_one_qubit_decomposer.rs b/crates/accelerate/src/euler_one_qubit_decomposer.rs index 1a37528dbc8e..8fe0eaf87b45 100644 --- a/crates/accelerate/src/euler_one_qubit_decomposer.rs +++ b/crates/accelerate/src/euler_one_qubit_decomposer.rs @@ -380,22 +380,26 @@ fn circuit_rr( if !simplify { atol = -1.0; } - if theta.abs() < atol && phi.abs() < atol && lam.abs() < atol { - return OneQubitGateSequence { - gates: circuit, - global_phase: phase, - }; - } - if (theta - PI).abs() > atol { + + if mod_2pi((phi + lam) / 2., atol).abs() < atol { + // This can be expressed as a single R gate + if theta.abs() > atol { + circuit.push((String::from("r"), vec![theta, mod_2pi(PI / 2. + phi, atol)])); + } + } else { + // General case: use two R gates + if (theta - PI).abs() > atol { + circuit.push(( + String::from("r"), + vec![theta - PI, mod_2pi(PI / 2. - lam, atol)], + )); + } circuit.push(( String::from("r"), - vec![theta - PI, mod_2pi(PI / 2. - lam, atol)], + vec![PI, mod_2pi(0.5 * (phi - lam + PI), atol)], )); } - circuit.push(( - String::from("r"), - vec![PI, mod_2pi(0.5 * (phi - lam + PI), atol)], - )); + OneQubitGateSequence { gates: circuit, global_phase: phase, diff --git a/crates/accelerate/src/lib.rs b/crates/accelerate/src/lib.rs index d00d17644b83..9e7d4cbddf72 100644 --- a/crates/accelerate/src/lib.rs +++ b/crates/accelerate/src/lib.rs @@ -24,6 +24,7 @@ mod euler_one_qubit_decomposer; mod nlayout; mod optimize_1q_gates; mod pauli_exp_val; +mod quantum_circuit; mod results; mod sabre_layout; mod sabre_swap; @@ -52,6 +53,7 @@ fn _accelerate(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pymodule!(sabre_swap::sabre_swap))?; m.add_wrapped(wrap_pymodule!(pauli_exp_val::pauli_expval))?; m.add_wrapped(wrap_pymodule!(dense_layout::dense_layout))?; + m.add_wrapped(wrap_pymodule!(quantum_circuit::quantum_circuit))?; m.add_wrapped(wrap_pymodule!(error_map::error_map))?; m.add_wrapped(wrap_pymodule!(sparse_pauli_op::sparse_pauli_op))?; m.add_wrapped(wrap_pymodule!(results::results))?; diff --git a/crates/accelerate/src/quantum_circuit/circuit_data.rs b/crates/accelerate/src/quantum_circuit/circuit_data.rs new file mode 100644 index 000000000000..ebc0c5a3ddb6 --- /dev/null +++ b/crates/accelerate/src/quantum_circuit/circuit_data.rs @@ -0,0 +1,657 @@ +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +use crate::quantum_circuit::circuit_instruction::CircuitInstruction; +use crate::quantum_circuit::intern_context::{BitType, IndexType, InternContext}; +use crate::quantum_circuit::py_ext; +use hashbrown::HashMap; +use pyo3::exceptions::{PyIndexError, PyKeyError, PyRuntimeError, PyValueError}; +use pyo3::prelude::*; +use pyo3::types::{PyIterator, PyList, PySlice, PyTuple, PyType}; +use pyo3::{PyObject, PyResult, PyTraverseError, PyVisit}; +use std::hash::{Hash, Hasher}; + +/// Private type used to store instructions with interned arg lists. +#[derive(Clone, Debug)] +struct PackedInstruction { + /// The Python-side operation instance. + op: PyObject, + /// The index under which the interner has stored `qubits`. + qubits_id: IndexType, + /// The index under which the interner has stored `clbits`. + clbits_id: IndexType, +} + +/// Private wrapper for Python-side Bit instances that implements +/// [Hash] and [Eq], allowing them to be used in Rust hash-based +/// sets and maps. +/// +/// Python's `hash()` is called on the wrapped Bit instance during +/// construction and returned from Rust's [Hash] trait impl. +/// The impl of [PartialEq] first compares the native Py pointers +/// to determine equality. If these are not equal, only then does +/// it call `repr()` on both sides, which has a significant +/// performance advantage. +#[derive(Clone, Debug)] +struct BitAsKey { + /// Python's `hash()` of the wrapped instance. + hash: isize, + /// The wrapped instance. + bit: PyObject, +} + +impl BitAsKey { + fn new(bit: &PyAny) -> PyResult { + Ok(BitAsKey { + hash: bit.hash()?, + bit: bit.into_py(bit.py()), + }) + } +} + +impl Hash for BitAsKey { + fn hash(&self, state: &mut H) { + state.write_isize(self.hash); + } +} + +impl PartialEq for BitAsKey { + fn eq(&self, other: &Self) -> bool { + self.bit.is(&other.bit) + || Python::with_gil(|py| { + self.bit + .as_ref(py) + .repr() + .unwrap() + .eq(other.bit.as_ref(py).repr().unwrap()) + .unwrap() + }) + } +} + +impl Eq for BitAsKey {} + +/// A container for :class:`.QuantumCircuit` instruction listings that stores +/// :class:`.CircuitInstruction` instances in a packed form by interning +/// their :attr:`~.CircuitInstruction.qubits` and +/// :attr:`~.CircuitInstruction.clbits` to native vectors of indices. +/// +/// Before adding a :class:`.CircuitInstruction` to this container, its +/// :class:`.Qubit` and :class:`.Clbit` instances MUST be registered via the +/// constructor or via :meth:`.CircuitData.add_qubit` and +/// :meth:`.CircuitData.add_clbit`. This is because the order in which +/// bits of the same type are added to the container determines their +/// associated indices used for storage and retrieval. +/// +/// Once constructed, this container behaves like a Python list of +/// :class:`.CircuitInstruction` instances. However, these instances are +/// created and destroyed on the fly, and thus should be treated as ephemeral. +/// +/// For example, +/// +/// .. code-block:: +/// +/// qubits = [Qubit()] +/// data = CircuitData(qubits) +/// data.append(CircuitInstruction(XGate(), (qubits[0],), ())) +/// assert(data[0] == data[0]) # => Ok. +/// assert(data[0] is data[0]) # => PANICS! +/// +/// .. warning:: +/// +/// This is an internal interface and no part of it should be relied upon +/// outside of Qiskit. +/// +/// Args: +/// qubits (Iterable[:class:`.Qubit`] | None): The initial sequence of +/// qubits, used to map :class:`.Qubit` instances to and from its +/// indices. +/// clbits (Iterable[:class:`.Clbit`] | None): The initial sequence of +/// clbits, used to map :class:`.Clbit` instances to and from its +/// indices. +/// data (Iterable[:class:`.CircuitInstruction`]): An initial instruction +/// listing to add to this container. All bits appearing in the +/// instructions in this iterable must also exist in ``qubits`` and +/// ``clbits``. +/// reserve (int): The container's initial capacity. This is reserved +/// before copying instructions into the container when ``data`` +/// is provided, so the initialized container's unused capacity will +/// be ``max(0, reserve - len(data))``. +/// +/// Raises: +/// KeyError: if ``data`` contains a reference to a bit that is not present +/// in ``qubits`` or ``clbits``. +#[pyclass(sequence, module = "qiskit._accelerate.quantum_circuit")] +#[derive(Clone, Debug)] +pub struct CircuitData { + /// The packed instruction listing. + data: Vec, + /// The intern context used to intern instruction bits. + intern_context: InternContext, + /// The qubits registered (e.g. through :meth:`~.CircuitData.add_qubit`). + qubits_native: Vec, + /// The clbits registered (e.g. through :meth:`~.CircuitData.add_clbit`). + clbits_native: Vec, + /// Map of :class:`.Qubit` instances to their index in + /// :attr:`.CircuitData.qubits`. + qubit_indices_native: HashMap, + /// Map of :class:`.Clbit` instances to their index in + /// :attr:`.CircuitData.clbits`. + clbit_indices_native: HashMap, + /// The qubits registered, cached as a ``list[Qubit]``. + qubits: Py, + /// The clbits registered, cached as a ``list[Clbit]``. + clbits: Py, +} + +/// A private enumeration type used to extract arguments to pymethods +/// that may be either an index or a slice. +#[derive(FromPyObject)] +pub enum SliceOrInt<'a> { + Slice(&'a PySlice), + Int(isize), +} + +#[pymethods] +impl CircuitData { + #[new] + #[pyo3(signature = (qubits=None, clbits=None, data=None, reserve=0))] + pub fn new( + py: Python<'_>, + qubits: Option<&PyAny>, + clbits: Option<&PyAny>, + data: Option<&PyAny>, + reserve: usize, + ) -> PyResult { + let mut self_ = CircuitData { + data: Vec::new(), + intern_context: InternContext::new(), + qubits_native: Vec::new(), + clbits_native: Vec::new(), + qubit_indices_native: HashMap::new(), + clbit_indices_native: HashMap::new(), + qubits: PyList::empty(py).into_py(py), + clbits: PyList::empty(py).into_py(py), + }; + if let Some(qubits) = qubits { + for bit in qubits.iter()? { + self_.add_qubit(py, bit?)?; + } + } + if let Some(clbits) = clbits { + for bit in clbits.iter()? { + self_.add_clbit(py, bit?)?; + } + } + if let Some(data) = data { + self_.reserve(py, reserve); + self_.extend(py, data)?; + } + Ok(self_) + } + + pub fn __reduce__(self_: &PyCell, py: Python<'_>) -> PyResult { + let ty: &PyType = self_.get_type(); + let args = { + let self_ = self_.borrow(); + ( + self_.qubits.clone_ref(py), + self_.clbits.clone_ref(py), + None::<()>, + self_.data.len(), + ) + }; + Ok((ty, args, None::<()>, self_.iter()?).into_py(py)) + } + + /// Returns the current sequence of registered :class:`.Qubit` + /// instances as a list. + /// + /// .. note:: + /// + /// This list is not kept in sync with the container. + /// + /// Returns: + /// list(:class:`.Qubit`): The current sequence of registered qubits. + #[getter] + pub fn qubits(&self, py: Python<'_>) -> PyObject { + PyList::new(py, self.qubits.as_ref(py)).into_py(py) + } + + /// Returns the current sequence of registered :class:`.Clbit` + /// instances as a list. + /// + /// .. note:: + /// + /// This list is not kept in sync with the container. + /// + /// Returns: + /// list(:class:`.Clbit`): The current sequence of registered clbits. + #[getter] + pub fn clbits(&self, py: Python<'_>) -> PyObject { + PyList::new(py, self.clbits.as_ref(py)).into_py(py) + } + + /// Registers a :class:`.Qubit` instance. + /// + /// Args: + /// bit (:class:`.Qubit`): The qubit to register. + pub fn add_qubit(&mut self, py: Python<'_>, bit: &PyAny) -> PyResult<()> { + let idx: BitType = self.qubits_native.len().try_into().map_err(|_| { + PyRuntimeError::new_err( + "The number of qubits in the circuit has exceeded the maximum capacity", + ) + })?; + self.qubit_indices_native.insert(BitAsKey::new(bit)?, idx); + self.qubits_native.push(bit.into_py(py)); + self.qubits = PyList::new(py, &self.qubits_native).into_py(py); + Ok(()) + } + + /// Registers a :class:`.Clbit` instance. + /// + /// Args: + /// bit (:class:`.Clbit`): The clbit to register. + pub fn add_clbit(&mut self, py: Python<'_>, bit: &PyAny) -> PyResult<()> { + let idx: BitType = self.clbits_native.len().try_into().map_err(|_| { + PyRuntimeError::new_err( + "The number of clbits in the circuit has exceeded the maximum capacity", + ) + })?; + self.clbit_indices_native.insert(BitAsKey::new(bit)?, idx); + self.clbits_native.push(bit.into_py(py)); + self.clbits = PyList::new(py, &self.clbits_native).into_py(py); + Ok(()) + } + + /// Performs a shallow copy. + /// + /// Returns: + /// CircuitData: The shallow copy. + pub fn copy(&self, py: Python<'_>) -> PyResult { + Ok(CircuitData { + data: self.data.clone(), + intern_context: self.intern_context.clone(), + qubits_native: self.qubits_native.clone(), + clbits_native: self.clbits_native.clone(), + qubit_indices_native: self.qubit_indices_native.clone(), + clbit_indices_native: self.clbit_indices_native.clone(), + qubits: PyList::new(py, &self.qubits_native).into_py(py), + clbits: PyList::new(py, &self.clbits_native).into_py(py), + }) + } + + /// Reserves capacity for at least ``additional`` more + /// :class:`.CircuitInstruction` instances to be added to this container. + /// + /// Args: + /// additional (int): The additional capacity to reserve. If the + /// capacity is already sufficient, does nothing. + pub fn reserve(&mut self, _py: Python<'_>, additional: usize) { + self.data.reserve(additional); + } + + pub fn __len__(&self) -> usize { + self.data.len() + } + + // Note: we also rely on this to make us iterable! + pub fn __getitem__(&self, py: Python, index: &PyAny) -> PyResult { + // Internal helper function to get a specific + // instruction by index. + fn get_at( + self_: &CircuitData, + py: Python<'_>, + index: isize, + ) -> PyResult> { + let index = self_.convert_py_index(index)?; + if let Some(inst) = self_.data.get(index) { + self_.unpack(py, inst) + } else { + Err(PyIndexError::new_err(format!( + "No element at index {:?} in circuit data", + index + ))) + } + } + + if index.is_exact_instance_of::() { + let slice = self.convert_py_slice(index.downcast_exact::()?)?; + let result = slice + .into_iter() + .map(|i| get_at(self, py, i)) + .collect::>>()?; + Ok(result.into_py(py)) + } else { + Ok(get_at(self, py, index.extract()?)?.into_py(py)) + } + } + + pub fn __delitem__(&mut self, index: SliceOrInt) -> PyResult<()> { + match index { + SliceOrInt::Slice(slice) => { + let slice = { + let mut s = self.convert_py_slice(slice)?; + if s.len() > 1 && s.first().unwrap() < s.last().unwrap() { + // Reverse the order so we're sure to delete items + // at the back first (avoids messing up indices). + s.reverse() + } + s + }; + for i in slice.into_iter() { + self.__delitem__(SliceOrInt::Int(i))?; + } + Ok(()) + } + SliceOrInt::Int(index) => { + let index = self.convert_py_index(index)?; + if self.data.get(index).is_some() { + self.data.remove(index); + Ok(()) + } else { + Err(PyIndexError::new_err(format!( + "No element at index {:?} in circuit data", + index + ))) + } + } + } + } + + pub fn __setitem__( + &mut self, + py: Python<'_>, + index: SliceOrInt, + value: &PyAny, + ) -> PyResult<()> { + match index { + SliceOrInt::Slice(slice) => { + let indices = slice.indices(self.data.len().try_into().unwrap())?; + let slice = self.convert_py_slice(slice)?; + let values = value.iter()?.collect::>>()?; + if indices.step != 1 && slice.len() != values.len() { + // A replacement of a different length when step isn't exactly '1' + // would result in holes. + return Err(PyValueError::new_err(format!( + "attempt to assign sequence of size {:?} to extended slice of size {:?}", + values.len(), + slice.len(), + ))); + } + + for (i, v) in slice.iter().zip(values.iter()) { + self.__setitem__(py, SliceOrInt::Int(*i), v)?; + } + + if slice.len() > values.len() { + // Delete any extras. + let slice = PySlice::new( + py, + indices.start + values.len() as isize, + indices.stop, + 1isize, + ); + self.__delitem__(SliceOrInt::Slice(slice))?; + } else { + // Insert any extra values. + for v in values.iter().skip(slice.len()).rev() { + let v: PyRef = v.extract()?; + self.insert(py, indices.stop, v)?; + } + } + + Ok(()) + } + SliceOrInt::Int(index) => { + let index = self.convert_py_index(index)?; + let value: PyRef = value.extract()?; + let mut packed = self.pack(py, value)?; + std::mem::swap(&mut packed, &mut self.data[index]); + Ok(()) + } + } + } + + pub fn insert( + &mut self, + py: Python<'_>, + index: isize, + value: PyRef, + ) -> PyResult<()> { + let index = self.convert_py_index_clamped(index); + let packed = self.pack(py, value)?; + self.data.insert(index, packed); + Ok(()) + } + + pub fn pop(&mut self, py: Python<'_>, index: Option) -> PyResult { + let index = + index.unwrap_or_else(|| std::cmp::max(0, self.data.len() as isize - 1).into_py(py)); + let item = self.__getitem__(py, index.as_ref(py))?; + self.__delitem__(index.as_ref(py).extract()?)?; + Ok(item) + } + + pub fn append(&mut self, py: Python<'_>, value: PyRef) -> PyResult<()> { + let packed = self.pack(py, value)?; + self.data.push(packed); + Ok(()) + } + + // To prevent the entire iterator from being loaded into memory, + // we create a `GILPool` for each iteration of the loop, which + // ensures that the `CircuitInstruction` returned by the call + // to `next` is dropped before the next iteration. + pub fn extend(&mut self, py: Python<'_>, itr: &PyAny) -> PyResult<()> { + // To ensure proper lifetime management, we explicitly store + // the result of calling `iter(itr)` as a GIL-independent + // reference that we access only with the most recent GILPool. + // It would be dangerous to access the original `itr` or any + // GIL-dependent derivatives of it after creating the new pool. + let itr: Py = itr.iter()?.into_py(py); + loop { + // Create a new pool, so that PyO3 can clear memory at + // the end of the loop. + let pool = unsafe { py.new_pool() }; + + // It is recommended to *always* immediately set py to the pool's + // Python, to help avoid creating references with invalid lifetimes. + let py = pool.python(); + + // Access the iterator using the new pool. + match itr.as_ref(py).next() { + None => { + break; + } + Some(v) => { + self.append(py, v?.extract()?)?; + } + } + // The GILPool is dropped here, which cleans up the ref + // returned from `next` as well as any resources used by + // `self.append`. + } + Ok(()) + } + + pub fn clear(&mut self, _py: Python<'_>) -> PyResult<()> { + std::mem::take(&mut self.data); + Ok(()) + } + + // Marks this pyclass as NOT hashable. + #[classattr] + const __hash__: Option> = None; + + fn __eq__(slf: &PyCell, other: &PyAny) -> PyResult { + let slf: &PyAny = slf; + if slf.is(other) { + return Ok(true); + } + if slf.len()? != other.len()? { + return Ok(false); + } + // Implemented using generic iterators on both sides + // for simplicity. + let mut ours_itr = slf.iter()?; + let mut theirs_itr = other.iter()?; + loop { + match (ours_itr.next(), theirs_itr.next()) { + (Some(ours), Some(theirs)) => { + if !ours?.eq(theirs?)? { + return Ok(false); + } + } + (None, None) => { + return Ok(true); + } + _ => { + return Ok(false); + } + } + } + } + + fn __traverse__(&self, visit: PyVisit<'_>) -> Result<(), PyTraverseError> { + for packed in self.data.iter() { + visit.call(&packed.op)?; + } + for bit in self.qubits_native.iter().chain(self.clbits_native.iter()) { + visit.call(bit)?; + } + + // Note: + // There's no need to visit the native Rust data + // structures used for internal tracking: the only Python + // references they contain are to the bits in these lists! + visit.call(&self.qubits)?; + visit.call(&self.clbits)?; + Ok(()) + } + + fn __clear__(&mut self) { + // Clear anything that could have a reference cycle. + self.data.clear(); + self.qubits_native.clear(); + self.clbits_native.clear(); + self.qubit_indices_native.clear(); + self.clbit_indices_native.clear(); + } +} + +impl CircuitData { + /// Converts a Python slice to a `Vec` of indices into + /// the instruction listing, [CircuitData.data]. + fn convert_py_slice(&self, slice: &PySlice) -> PyResult> { + let indices = slice.indices(self.data.len().try_into().unwrap())?; + if indices.step > 0 { + Ok((indices.start..indices.stop) + .step_by(indices.step as usize) + .collect()) + } else { + let mut out = Vec::with_capacity(indices.slicelength as usize); + let mut x = indices.start; + while x > indices.stop { + out.push(x); + x += indices.step; + } + Ok(out) + } + } + + /// Converts a Python index to an index into the instruction listing, + /// or one past its end. + /// If the resulting index would be < 0, clamps to 0. + /// If the resulting index would be > len(data), clamps to len(data). + fn convert_py_index_clamped(&self, index: isize) -> usize { + let index = if index < 0 { + index + self.data.len() as isize + } else { + index + }; + std::cmp::min(std::cmp::max(0, index), self.data.len() as isize) as usize + } + + /// Converts a Python index to an index into the instruction listing. + fn convert_py_index(&self, index: isize) -> PyResult { + let index = if index < 0 { + index + self.data.len() as isize + } else { + index + }; + + if index < 0 || index >= self.data.len() as isize { + return Err(PyIndexError::new_err(format!( + "Index {:?} is out of bounds.", + index, + ))); + } + Ok(index as usize) + } + + /// Returns a [PackedInstruction] containing the original operation + /// of `elem` and [InternContext] indices of its `qubits` and `clbits` + /// fields. + fn pack( + &mut self, + py: Python<'_>, + inst: PyRef, + ) -> PyResult { + let mut interned_bits = + |indices: &HashMap, bits: &PyTuple| -> PyResult { + let args = bits + .into_iter() + .map(|b| { + let key = BitAsKey::new(b)?; + indices.get(&key).copied().ok_or_else(|| { + PyKeyError::new_err(format!( + "Bit {:?} has not been added to this circuit.", + b + )) + }) + }) + .collect::>>()?; + self.intern_context.intern(args) + }; + Ok(PackedInstruction { + op: inst.operation.clone_ref(py), + qubits_id: interned_bits(&self.qubit_indices_native, inst.qubits.as_ref(py))?, + clbits_id: interned_bits(&self.clbit_indices_native, inst.clbits.as_ref(py))?, + }) + } + + fn unpack(&self, py: Python<'_>, inst: &PackedInstruction) -> PyResult> { + Py::new( + py, + CircuitInstruction { + operation: inst.op.clone_ref(py), + qubits: py_ext::tuple_new( + py, + self.intern_context + .lookup(inst.qubits_id) + .iter() + .map(|i| self.qubits_native[*i as usize].clone_ref(py)) + .collect(), + ), + clbits: py_ext::tuple_new( + py, + self.intern_context + .lookup(inst.clbits_id) + .iter() + .map(|i| self.clbits_native[*i as usize].clone_ref(py)) + .collect(), + ), + }, + ) + } +} diff --git a/crates/accelerate/src/quantum_circuit/circuit_instruction.rs b/crates/accelerate/src/quantum_circuit/circuit_instruction.rs new file mode 100644 index 000000000000..0bf84a362c3f --- /dev/null +++ b/crates/accelerate/src/quantum_circuit/circuit_instruction.rs @@ -0,0 +1,254 @@ +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +use crate::quantum_circuit::py_ext; +use pyo3::basic::CompareOp; +use pyo3::prelude::*; +use pyo3::types::{PyList, PyTuple}; +use pyo3::{PyObject, PyResult}; + +/// A single instruction in a :class:`.QuantumCircuit`, comprised of the :attr:`operation` and +/// various operands. +/// +/// .. note:: +/// +/// There is some possible confusion in the names of this class, :class:`~.circuit.Instruction`, +/// and :class:`~.circuit.Operation`, and this class's attribute :attr:`operation`. Our +/// preferred terminology is by analogy to assembly languages, where an "instruction" is made up +/// of an "operation" and its "operands". +/// +/// Historically, :class:`~.circuit.Instruction` came first, and originally contained the qubits +/// it operated on and any parameters, so it was a true "instruction". Over time, +/// :class:`.QuantumCircuit` became responsible for tracking qubits and clbits, and the class +/// became better described as an "operation". Changing the name of such a core object would be +/// a very unpleasant API break for users, and so we have stuck with it. +/// +/// This class was created to provide a formal "instruction" context object in +/// :class:`.QuantumCircuit.data`, which had long been made of ad-hoc tuples. With this, and +/// the advent of the :class:`~.circuit.Operation` interface for adding more complex objects to +/// circuits, we took the opportunity to correct the historical naming. For the time being, +/// this leads to an awkward case where :attr:`.CircuitInstruction.operation` is often an +/// :class:`~.circuit.Instruction` instance (:class:`~.circuit.Instruction` implements the +/// :class:`.Operation` interface), but as the :class:`.Operation` interface gains more use, +/// this confusion will hopefully abate. +/// +/// .. warning:: +/// +/// This is a lightweight internal class and there is minimal error checking; you must respect +/// the type hints when using it. It is the user's responsibility to ensure that direct +/// mutations of the object do not invalidate the types, nor the restrictions placed on it by +/// its context. Typically this will mean, for example, that :attr:`qubits` must be a sequence +/// of distinct items, with no duplicates. +#[pyclass( + freelist = 20, + sequence, + get_all, + module = "qiskit._accelerate.quantum_circuit" +)] +#[derive(Clone, Debug)] +pub struct CircuitInstruction { + /// The logical operation that this instruction represents an execution of. + pub operation: PyObject, + /// A sequence of the qubits that the operation is applied to. + pub qubits: Py, + /// A sequence of the classical bits that this operation reads from or writes to. + pub clbits: Py, +} + +#[pymethods] +impl CircuitInstruction { + #[new] + pub fn new( + py: Python<'_>, + operation: PyObject, + qubits: Option<&PyAny>, + clbits: Option<&PyAny>, + ) -> PyResult { + fn as_tuple(py: Python<'_>, seq: Option<&PyAny>) -> PyResult> { + match seq { + None => Ok(py_ext::tuple_new_empty(py)), + Some(seq) => { + if seq.is_instance_of::() { + Ok(seq.downcast_exact::()?.into_py(py)) + } else if seq.is_instance_of::() { + let seq = seq.downcast_exact::()?; + Ok(py_ext::tuple_from_list(seq)) + } else { + // New tuple from iterable. + Ok(py_ext::tuple_new( + py, + seq.iter()? + .map(|o| Ok(o?.into_py(py))) + .collect::>>()?, + )) + } + } + } + } + + Ok(CircuitInstruction { + operation, + qubits: as_tuple(py, qubits)?, + clbits: as_tuple(py, clbits)?, + }) + } + + /// Returns a shallow copy. + /// + /// Returns: + /// CircuitInstruction: The shallow copy. + pub fn copy(&self) -> Self { + self.clone() + } + + /// Creates a shallow copy with the given fields replaced. + /// + /// Returns: + /// CircuitInstruction: A new instance with the given fields replaced. + pub fn replace( + &self, + py: Python<'_>, + operation: Option, + qubits: Option<&PyAny>, + clbits: Option<&PyAny>, + ) -> PyResult { + CircuitInstruction::new( + py, + operation.unwrap_or_else(|| self.operation.clone_ref(py)), + Some(qubits.unwrap_or_else(|| self.qubits.as_ref(py))), + Some(clbits.unwrap_or_else(|| self.clbits.as_ref(py))), + ) + } + + fn __getstate__(&self, py: Python<'_>) -> PyObject { + ( + self.operation.as_ref(py), + self.qubits.as_ref(py), + self.clbits.as_ref(py), + ) + .into_py(py) + } + + fn __setstate__(&mut self, _py: Python<'_>, state: &PyTuple) -> PyResult<()> { + self.operation = state.get_item(0)?.extract()?; + self.qubits = state.get_item(1)?.extract()?; + self.clbits = state.get_item(2)?.extract()?; + Ok(()) + } + + pub fn __getnewargs__(&self, py: Python<'_>) -> PyResult { + Ok(( + self.operation.as_ref(py), + self.qubits.as_ref(py), + self.clbits.as_ref(py), + ) + .into_py(py)) + } + + pub fn __repr__(self_: &PyCell, py: Python<'_>) -> PyResult { + let type_name = self_.get_type().name()?; + let r = self_.try_borrow()?; + Ok(format!( + "{}(\ + operation={}\ + , qubits={}\ + , clbits={}\ + )", + type_name, + r.operation.as_ref(py).repr()?, + r.qubits.as_ref(py).repr()?, + r.clbits.as_ref(py).repr()? + )) + } + + // Legacy tuple-like interface support. + // + // For a best attempt at API compatibility during the transition to using this new class, we need + // the interface to behave exactly like the old 3-tuple `(inst, qargs, cargs)` if it's treated + // like that via unpacking or similar. That means that the `parameters` field is completely + // absent, and the qubits and clbits must be converted to lists. + pub fn _legacy_format(&self, py: Python<'_>) -> PyObject { + PyTuple::new( + py, + [ + self.operation.as_ref(py), + self.qubits.as_ref(py).to_list(), + self.clbits.as_ref(py).to_list(), + ], + ) + .into_py(py) + } + + pub fn __getitem__(&self, py: Python<'_>, key: &PyAny) -> PyResult { + Ok(self + ._legacy_format(py) + .as_ref(py) + .get_item(key)? + .into_py(py)) + } + + pub fn __iter__(&self, py: Python<'_>) -> PyResult { + Ok(self._legacy_format(py).as_ref(py).iter()?.into_py(py)) + } + + pub fn __len__(&self) -> usize { + 3 + } + + pub fn __richcmp__( + self_: &PyCell, + other: &PyAny, + op: CompareOp, + py: Python<'_>, + ) -> PyResult { + fn eq( + py: Python<'_>, + self_: &PyCell, + other: &PyAny, + ) -> PyResult> { + if self_.is(other) { + return Ok(Some(true)); + } + + let self_ = self_.try_borrow()?; + if other.is_instance_of::() { + let other: PyResult<&PyCell> = other.extract(); + return other.map_or(Ok(Some(false)), |v| { + let v = v.try_borrow()?; + Ok(Some( + self_.clbits.as_ref(py).eq(v.clbits.as_ref(py))? + && self_.qubits.as_ref(py).eq(v.qubits.as_ref(py))? + && self_.operation.as_ref(py).eq(v.operation.as_ref(py))?, + )) + }); + } + + if other.is_instance_of::() { + return Ok(Some(self_._legacy_format(py).as_ref(py).eq(other)?)); + } + + Ok(None) + } + + match op { + CompareOp::Eq => eq(py, self_, other).map(|r| { + r.map(|b| b.into_py(py)) + .unwrap_or_else(|| py.NotImplemented()) + }), + CompareOp::Ne => eq(py, self_, other).map(|r| { + r.map(|b| (!b).into_py(py)) + .unwrap_or_else(|| py.NotImplemented()) + }), + _ => Ok(py.NotImplemented()), + } + } +} diff --git a/crates/accelerate/src/quantum_circuit/intern_context.rs b/crates/accelerate/src/quantum_circuit/intern_context.rs new file mode 100644 index 000000000000..0c8b596e6dd8 --- /dev/null +++ b/crates/accelerate/src/quantum_circuit/intern_context.rs @@ -0,0 +1,71 @@ +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +use hashbrown::HashMap; +use pyo3::exceptions::PyRuntimeError; +use pyo3::PyResult; +use std::sync::Arc; + +pub type IndexType = u32; +pub type BitType = u32; + +/// A Rust-only data structure (not a pyclass!) for interning +/// `Vec`. +/// +/// Takes ownership of vectors given to [InternContext.intern] +/// and returns an [IndexType] index that can be used to look up +/// an _equivalent_ sequence by reference via [InternContext.lookup]. +#[derive(Clone, Debug)] +pub struct InternContext { + slots: Vec>>, + slot_lookup: HashMap>, IndexType>, +} + +impl InternContext { + pub fn new() -> Self { + InternContext { + slots: Vec::new(), + slot_lookup: HashMap::new(), + } + } + + /// Takes `args` by reference and returns an index that can be used + /// to obtain a reference to an equivalent sequence of `BitType` by + /// calling [CircuitData.lookup]. + pub fn intern(&mut self, args: Vec) -> PyResult { + if let Some(slot_idx) = self.slot_lookup.get(&args) { + return Ok(*slot_idx); + } + + let args = Arc::new(args); + let slot_idx: IndexType = self + .slots + .len() + .try_into() + .map_err(|_| PyRuntimeError::new_err("InternContext capacity exceeded!"))?; + self.slots.push(args.clone()); + self.slot_lookup.insert_unique_unchecked(args, slot_idx); + Ok(slot_idx) + } + + /// Returns the sequence corresponding to `slot_idx`, which must + /// be a value returned by [InternContext.intern]. + pub fn lookup(&self, slot_idx: IndexType) -> &[BitType] { + self.slots.get(slot_idx as usize).unwrap() + } +} + +impl Default for InternContext { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/accelerate/src/quantum_circuit/mod.rs b/crates/accelerate/src/quantum_circuit/mod.rs new file mode 100644 index 000000000000..4f4b56865034 --- /dev/null +++ b/crates/accelerate/src/quantum_circuit/mod.rs @@ -0,0 +1,25 @@ +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. + +pub mod circuit_data; +pub mod circuit_instruction; +pub mod intern_context; +mod py_ext; + +use pyo3::prelude::*; + +#[pymodule] +pub fn quantum_circuit(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + Ok(()) +} diff --git a/crates/accelerate/src/quantum_circuit/py_ext.rs b/crates/accelerate/src/quantum_circuit/py_ext.rs new file mode 100644 index 000000000000..da27764a7f4e --- /dev/null +++ b/crates/accelerate/src/quantum_circuit/py_ext.rs @@ -0,0 +1,45 @@ +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023 +// +// This code is licensed under the Apache License, Version 2.0. You may +// obtain a copy of this license in the LICENSE.txt file in the root directory +// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +//! Contains helper functions for creating [Py] (GIL-independent) +//! objects without creating an intermediate owned reference. These functions +//! are faster than PyO3's list and tuple factory methods when the caller +//! doesn't need to dereference the newly constructed object (i.e. if the +//! resulting [Py] will simply be stored in a Rust struct). +//! +//! The reason this is faster is because PyO3 tracks owned references and +//! will perform deallocation when the active [GILPool] goes out of scope. +//! If we don't need to dereference the [Py], then we can skip the +//! tracking and deallocation. + +use pyo3::ffi::Py_ssize_t; +use pyo3::prelude::*; +use pyo3::types::{PyList, PyTuple}; +use pyo3::{ffi, AsPyPointer, PyNativeType}; + +pub fn tuple_new(py: Python<'_>, items: Vec) -> Py { + unsafe { + let ptr = ffi::PyTuple_New(items.len() as Py_ssize_t); + let tup: Py = Py::from_owned_ptr(py, ptr); + for (i, obj) in items.into_iter().enumerate() { + ffi::PyTuple_SetItem(ptr, i as Py_ssize_t, obj.into_ptr()); + } + tup + } +} + +pub fn tuple_new_empty(py: Python<'_>) -> Py { + unsafe { Py::from_owned_ptr(py, ffi::PyTuple_New(0)) } +} + +pub fn tuple_from_list(list: &PyList) -> Py { + unsafe { Py::from_owned_ptr(list.py(), ffi::PyList_AsTuple(list.as_ptr())) } +} diff --git a/crates/accelerate/src/sabre_swap/mod.rs b/crates/accelerate/src/sabre_swap/mod.rs index d4345a02e2c1..2d4dc40481e1 100644 --- a/crates/accelerate/src/sabre_swap/mod.rs +++ b/crates/accelerate/src/sabre_swap/mod.rs @@ -171,21 +171,35 @@ fn populate_extended_set( let mut decremented: IndexMap = IndexMap::with_hasher(ahash::RandomState::default()); let mut i = 0; + let mut visit_now: Vec = Vec::new(); while i < to_visit.len() && extended_set.len() < EXTENDED_SET_SIZE { - for edge in dag.dag.edges_directed(to_visit[i], Direction::Outgoing) { - let successor_node = edge.target(); - let successor_index = successor_node.index(); - *decremented.entry(successor_index).or_insert(0) += 1; - required_predecessors[successor_index] -= 1; - if required_predecessors[successor_index] == 0 { - if !dag.node_blocks.contains_key(&successor_index) { - if let [a, b] = dag.dag[successor_node].qubits[..] { - extended_set.push([a.to_phys(layout), b.to_phys(layout)]); + // Visit runs of non-2Q gates fully before moving on to children + // of 2Q gates. This way, traversal order is a BFS of 2Q gates rather + // than of all gates. + visit_now.push(to_visit[i]); + let mut j = 0; + while let Some(node) = visit_now.get(j) { + for edge in dag.dag.edges_directed(*node, Direction::Outgoing) { + let successor_node = edge.target(); + let successor_index = successor_node.index(); + *decremented.entry(successor_index).or_insert(0) += 1; + required_predecessors[successor_index] -= 1; + if required_predecessors[successor_index] == 0 { + if !dag.dag[successor_node].directive + && !dag.node_blocks.contains_key(&successor_index) + { + if let [a, b] = dag.dag[successor_node].qubits[..] { + extended_set.push([a.to_phys(layout), b.to_phys(layout)]); + to_visit.push(successor_node); + continue; + } } + visit_now.push(successor_node); } - to_visit.push(successor_node); } + j += 1; } + visit_now.clear(); i += 1; } for (node, amount) in decremented.iter() { @@ -582,41 +596,44 @@ fn route_reachable_nodes( let node_id = to_visit[i]; let node = &dag.dag[node_id]; i += 1; - - match dag.node_blocks.get(&node.py_node_id) { - Some(blocks) => { - // Control flow op. Route all blocks for current layout. - let mut block_results: Vec = Vec::with_capacity(blocks.len()); - for inner_dag in blocks { - let (inner_dag_routed, inner_final_layout) = route_block_dag(inner_dag, layout); - // For now, we always append a swap circuit that gets the inner block - // back to the parent's layout. - let swap_epilogue = - gen_swap_epilogue(coupling, inner_final_layout, layout, seed); - let block_result = BlockResult { - result: inner_dag_routed, - swap_epilogue, - }; - block_results.push(block_result); + // If the node is a directive that means it can be placed anywhere + if !node.directive { + match dag.node_blocks.get(&node.py_node_id) { + Some(blocks) => { + // Control flow op. Route all blocks for current layout. + let mut block_results: Vec = Vec::with_capacity(blocks.len()); + for inner_dag in blocks { + let (inner_dag_routed, inner_final_layout) = + route_block_dag(inner_dag, layout); + // For now, we always append a swap circuit that gets the inner block + // back to the parent's layout. + let swap_epilogue = + gen_swap_epilogue(coupling, inner_final_layout, layout, seed); + let block_result = BlockResult { + result: inner_dag_routed, + swap_epilogue, + }; + block_results.push(block_result); + } + node_block_results.insert_unique_unchecked(node.py_node_id, block_results); } - node_block_results.insert_unique_unchecked(node.py_node_id, block_results); + None => match node.qubits[..] { + // A gate op whose connectivity must match the device to be + // placed in the gate order. + [a, b] + if !coupling.contains_edge( + NodeIndex::new(a.to_phys(layout).index()), + NodeIndex::new(b.to_phys(layout).index()), + ) => + { + // 2Q op that cannot be placed. Add it to the front layer + // and move on. + front_layer.insert(node_id, [a.to_phys(layout), b.to_phys(layout)]); + continue; + } + _ => {} + }, } - None => match node.qubits[..] { - // A gate op whose connectivity must match the device to be - // placed in the gate order. - [a, b] - if !coupling.contains_edge( - NodeIndex::new(a.to_phys(layout).index()), - NodeIndex::new(b.to_phys(layout).index()), - ) => - { - // 2Q op that cannot be placed. Add it to the front layer - // and move on. - front_layer.insert(node_id, [a.to_phys(layout), b.to_phys(layout)]); - continue; - } - _ => {} - }, } gate_order.push(node.py_node_id); diff --git a/crates/accelerate/src/sabre_swap/sabre_dag.rs b/crates/accelerate/src/sabre_swap/sabre_dag.rs index 7178c8570aea..037470b9b79d 100644 --- a/crates/accelerate/src/sabre_swap/sabre_dag.rs +++ b/crates/accelerate/src/sabre_swap/sabre_dag.rs @@ -23,6 +23,7 @@ use crate::nlayout::VirtualQubit; pub struct DAGNode { pub py_node_id: usize, pub qubits: Vec, + pub directive: bool, } /// A DAG representation of the logical circuit to be routed. This represents the same dataflow @@ -41,7 +42,7 @@ pub struct SabreDAG { pub num_clbits: usize, pub dag: DiGraph, pub first_layer: Vec, - pub nodes: Vec<(usize, Vec, HashSet)>, + pub nodes: Vec<(usize, Vec, HashSet, bool)>, pub node_blocks: HashMap>, } @@ -52,7 +53,7 @@ impl SabreDAG { pub fn new( num_qubits: usize, num_clbits: usize, - nodes: Vec<(usize, Vec, HashSet)>, + nodes: Vec<(usize, Vec, HashSet, bool)>, node_blocks: HashMap>, ) -> PyResult { let mut qubit_pos: Vec> = vec![None; num_qubits]; @@ -65,6 +66,7 @@ impl SabreDAG { let gate_index = dag.add_node(DAGNode { py_node_id: node.0, qubits: qargs.clone(), + directive: node.3, }); let mut is_front = true; for x in qargs { @@ -118,12 +120,24 @@ mod test { #[test] fn no_panic_on_bad_qubits() { let bad_qubits = vec![VirtualQubit::new(0), VirtualQubit::new(2)]; - assert!(SabreDAG::new(2, 0, vec![(0, bad_qubits, HashSet::new())], HashMap::new()).is_err()) + assert!(SabreDAG::new( + 2, + 0, + vec![(0, bad_qubits, HashSet::new(), false)], + HashMap::new() + ) + .is_err()) } #[test] fn no_panic_on_bad_clbits() { let good_qubits = vec![VirtualQubit::new(0), VirtualQubit::new(1)]; - assert!(SabreDAG::new(2, 1, vec![(0, good_qubits, [0, 1].into())], HashMap::new()).is_err()) + assert!(SabreDAG::new( + 2, + 1, + vec![(0, good_qubits, [0, 1].into(), false)], + HashMap::new() + ) + .is_err()) } } diff --git a/crates/accelerate/src/vf2_layout.rs b/crates/accelerate/src/vf2_layout.rs index 65817f4ac477..fe361b079410 100644 --- a/crates/accelerate/src/vf2_layout.rs +++ b/crates/accelerate/src/vf2_layout.rs @@ -10,8 +10,6 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. -use indexmap::IndexMap; - use numpy::PyReadonlyArray1; use pyo3::prelude::*; use pyo3::wrap_pyfunction; @@ -22,6 +20,19 @@ use crate::nlayout::{NLayout, VirtualQubit}; const PARALLEL_THRESHOLD: usize = 50; +#[pyclass] +pub struct EdgeList { + pub edge_list: Vec<([VirtualQubit; 2], i32)>, +} + +#[pymethods] +impl EdgeList { + #[new] + pub fn new(edge_list: Vec<([VirtualQubit; 2], i32)>) -> Self { + EdgeList { edge_list } + } +} + /// Score a given circuit with a layout applied #[pyfunction] #[pyo3( @@ -29,14 +40,14 @@ const PARALLEL_THRESHOLD: usize = 50; )] pub fn score_layout( bit_list: PyReadonlyArray1, - edge_list: IndexMap<[VirtualQubit; 2], i32>, + edge_list: &EdgeList, error_map: &ErrorMap, layout: &NLayout, strict_direction: bool, run_in_parallel: bool, ) -> PyResult { let bit_counts = bit_list.as_slice()?; - let edge_filter_map = |(index_arr, gate_count): (&[VirtualQubit; 2], &i32)| -> Option { + let edge_filter_map = |(index_arr, gate_count): &([VirtualQubit; 2], i32)| -> Option { let mut error = error_map .error_map .get(&[index_arr[0].to_phys(layout), index_arr[1].to_phys(layout)]); @@ -66,10 +77,18 @@ pub fn score_layout( }) }; - let mut fidelity: f64 = if edge_list.len() < PARALLEL_THRESHOLD || !run_in_parallel { - edge_list.iter().filter_map(edge_filter_map).product() + let mut fidelity: f64 = if edge_list.edge_list.len() < PARALLEL_THRESHOLD || !run_in_parallel { + edge_list + .edge_list + .iter() + .filter_map(edge_filter_map) + .product() } else { - edge_list.par_iter().filter_map(edge_filter_map).product() + edge_list + .edge_list + .par_iter() + .filter_map(edge_filter_map) + .product() }; fidelity *= if bit_list.len() < PARALLEL_THRESHOLD || !run_in_parallel { bit_counts @@ -90,5 +109,6 @@ pub fn score_layout( #[pymodule] pub fn vf2_layout(_py: Python, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(score_layout))?; + m.add_class::()?; Ok(()) } diff --git a/docs/_static/images/1xp.png b/docs/_static/images/1xp.png deleted file mode 100644 index c7ee75a81bd1..000000000000 Binary files a/docs/_static/images/1xp.png and /dev/null differ diff --git a/docs/_static/images/ibm_qlab.png b/docs/_static/images/ibm_qlab.png deleted file mode 100644 index a9fae895d8bc..000000000000 Binary files a/docs/_static/images/ibm_qlab.png and /dev/null differ diff --git a/docs/_static/images/strangeworks.png b/docs/_static/images/strangeworks.png deleted file mode 100644 index 4fc76f092052..000000000000 Binary files a/docs/_static/images/strangeworks.png and /dev/null differ diff --git a/docs/api_redirects.txt b/docs/api_redirects.txt deleted file mode 100644 index dd0bf35b1a9b..000000000000 --- a/docs/api_redirects.txt +++ /dev/null @@ -1,286 +0,0 @@ -qiskit.algorithms.AlgorithmError algorithms -qiskit.algorithms.eval_observables algorithms -qiskit.algorithms.estimate_observables algorithms - -qiskit.assembler.assemble_circuits assembler -qiskit.assembler.assemble_schedules assembler -qiskit.assembler.disassemble assembler - -qiskit.circuit.random.random_circuit circuit - -qiskit.circuit.library.templates.nct.template_nct_2a_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_2a_2 circuit_library -qiskit.circuit.library.templates.nct.template_nct_2a_3 circuit_library -qiskit.circuit.library.templates.nct.template_nct_4a_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_4a_2 circuit_library -qiskit.circuit.library.templates.nct.template_nct_4a_3 circuit_library -qiskit.circuit.library.templates.nct.template_nct_4b_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_4b_2 circuit_library -qiskit.circuit.library.templates.nct.template_nct_5a_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_5a_2 circuit_library -qiskit.circuit.library.templates.nct.template_nct_5a_3 circuit_library -qiskit.circuit.library.templates.nct.template_nct_5a_4 circuit_library -qiskit.circuit.library.templates.nct.template_nct_6a_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_6a_2 circuit_library -qiskit.circuit.library.templates.nct.template_nct_6a_3 circuit_library -qiskit.circuit.library.templates.nct.template_nct_6a_4 circuit_library -qiskit.circuit.library.templates.nct.template_nct_6b_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_6b_2 circuit_library -qiskit.circuit.library.templates.nct.template_nct_6c_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_7a_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_7b_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_7c_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_7d_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_7e_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9a_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_2 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_3 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_4 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_5 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_6 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_7 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_8 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_9 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_10 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_11 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9c_12 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_1 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_2 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_3 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_4 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_5 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_6 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_7 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_8 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_9 circuit_library -qiskit.circuit.library.templates.nct.template_nct_9d_10 circuit_library -qiskit.circuit.library.clifford_2_1 circuit_library -qiskit.circuit.library.clifford_2_2 circuit_library -qiskit.circuit.library.clifford_2_3 circuit_library -qiskit.circuit.library.clifford_2_4 circuit_library -qiskit.circuit.library.clifford_3_1 circuit_library -qiskit.circuit.library.clifford_4_1 circuit_library -qiskit.circuit.library.clifford_4_2 circuit_library -qiskit.circuit.library.clifford_4_3 circuit_library -qiskit.circuit.library.clifford_4_4 circuit_library -qiskit.circuit.library.clifford_5_1 circuit_library -qiskit.circuit.library.clifford_6_1 circuit_library -qiskit.circuit.library.clifford_6_2 circuit_library -qiskit.circuit.library.clifford_6_3 circuit_library -qiskit.circuit.library.clifford_6_4 circuit_library -qiskit.circuit.library.clifford_6_5 circuit_library -qiskit.circuit.library.clifford_8_1 circuit_library -qiskit.circuit.library.clifford_8_2 circuit_library -qiskit.circuit.library.clifford_8_3 circuit_library -qiskit.circuit.library.rzx_yz circuit_library -qiskit.circuit.library.rzx_xz circuit_library -qiskit.circuit.library.rzx_cy circuit_library -qiskit.circuit.library.rzx_zz1 circuit_library -qiskit.circuit.library.rzx_zz2 circuit_library -qiskit.circuit.library.rzx_zz3 circuit_library - -qiskit.compiler.assemble compiler -qiskit.compiler.schedule compiler -qiskit.compiler.transpile compiler -qiskit.compiler.sequence compiler - -qiskit.converters.circuit_to_dag converters -qiskit.converters.dag_to_circuit converters -qiskit.converters.circuit_to_instruction converters -qiskit.converters.circuit_to_gate converters -qiskit.converters.ast_to_dag converters -qiskit.converters.dagdependency_to_circuit converters -qiskit.converters.circuit_to_dagdependency converters -qiskit.converters.dag_to_dagdependency converters -qiskit.converters.dagdependency_to_dag converters - -qiskit.dagcircuit.DAGCircuitError dagcircuit - -qiskit.opflow.commutator opflow -qiskit.opflow.anti_commutator opflow -qiskit.opflow.double_commutator opflow -qiskit.opflow.OpflowError opflow - -qiskit.providers.QiskitBackendNotFoundError providers -qiskit.providers.BackendPropertyError providers -qiskit.providers.JobError providers -qiskit.providers.JobTimeoutError providers - -qiskit.pulse.builder.build pulse -qiskit.pulse.builder.acquire_channel pulse -qiskit.pulse.builder.control_channels pulse -qiskit.pulse.builder.drive_channel pulse -qiskit.pulse.builder.measure_channel pulse -qiskit.pulse.builder.acquire pulse -qiskit.pulse.builder.barrier pulse -qiskit.pulse.builder.call pulse -qiskit.pulse.builder.delay pulse -qiskit.pulse.builder.play pulse -qiskit.pulse.builder.reference pulse -qiskit.pulse.builder.set_frequency pulse -qiskit.pulse.builder.set_phase pulse -qiskit.pulse.builder.shift_frequency pulse -qiskit.pulse.builder.shift_phase pulse -qiskit.pulse.builder.snapshot pulse -qiskit.pulse.builder.align_equispaced pulse -qiskit.pulse.builder.align_func pulse -qiskit.pulse.builder.align_left pulse -qiskit.pulse.builder.align_right pulse -qiskit.pulse.builder.align_sequential pulse -qiskit.pulse.builder.circuit_scheduler_settings pulse -qiskit.pulse.builder.frequency_offset pulse -qiskit.pulse.builder.phase_offset pulse -qiskit.pulse.builder.transpiler_settings pulse -qiskit.pulse.builder.measure pulse -qiskit.pulse.builder.measure_all pulse -qiskit.pulse.builder.delay_qubits pulse -qiskit.pulse.builder.cx pulse -qiskit.pulse.builder.u1 pulse -qiskit.pulse.builder.u2 pulse -qiskit.pulse.builder.u3 pulse -qiskit.pulse.builder.x pulse -qiskit.pulse.builder.active_backend pulse -qiskit.pulse.builder.active_transpiler_settings pulse -qiskit.pulse.builder.active_circuit_scheduler_settings pulse -qiskit.pulse.builder.num_qubits pulse -qiskit.pulse.builder.qubit_channels pulse -qiskit.pulse.builder.samples_to_seconds pulse -qiskit.pulse.builder.seconds_to_samples pulse - -qiskit.pulse.library.constant pulse -qiskit.pulse.library.zero pulse -qiskit.pulse.library.square pulse -qiskit.pulse.library.sawtooth pulse -qiskit.pulse.library.triangle pulse -qiskit.pulse.library.cos pulse -qiskit.pulse.library.sin pulse -qiskit.pulse.library.gaussian pulse -qiskit.pulse.library.gaussian_deriv pulse -qiskit.pulse.library.sech pulse -qiskit.pulse.library.sech_deriv pulse -qiskit.pulse.library.gaussian_square pulse -qiskit.pulse.library.drag pulse - -qiskit.pulse.transforms.add_implicit_acquires pulse -qiskit.pulse.transforms.align_measures pulse -qiskit.pulse.transforms.block_to_schedule pulse -qiskit.pulse.transforms.compress_pulses pulse -qiskit.pulse.transforms.flatten pulse -qiskit.pulse.transforms.inline_subroutines pulse -qiskit.pulse.transforms.pad pulse -qiskit.pulse.transforms.remove_directives pulse -qiskit.pulse.transforms.remove_trivial_barriers pulse -qiskit.pulse.transforms.block_to_dag pulse -qiskit.pulse.transforms.target_qobj_transform pulse - -qiskit.qasm.Qasm qasm - -qiskit.qpy.load qpy -qiskit.qpy.dump qpy - -qiskit.quantum_info.average_gate_fidelity quantum_info -qiskit.quantum_info.process_fidelity quantum_info -qiskit.quantum_info.gate_error quantum_info -qiskit.quantum_info.diamond_norm quantum_info -qiskit.quantum_info.state_fidelity quantum_info -qiskit.quantum_info.purity quantum_info -qiskit.quantum_info.concurrence quantum_info -qiskit.quantum_info.entropy quantum_info -qiskit.quantum_info.entanglement_of_formation quantum_info -qiskit.quantum_info.mutual_information quantum_info -qiskit.quantum_info.partial_trace quantum_info -qiskit.quantum_info.shannon_entropy quantum_info -qiskit.quantum_info.commutator quantum_info -qiskit.quantum_info.anti_commutator quantum_info -qiskit.quantum_info.double_commutator quantum_info -qiskit.quantum_info.random_statevector quantum_info -qiskit.quantum_info.random_density_matrix quantum_info -qiskit.quantum_info.random_unitary quantum_info -qiskit.quantum_info.random_hermitian quantum_info -qiskit.quantum_info.random_pauli quantum_info -qiskit.quantum_info.random_clifford quantum_info -qiskit.quantum_info.random_quantum_channel quantum_info -qiskit.quantum_info.random_cnotdihedral quantum_info -qiskit.quantum_info.random_pauli_table quantum_info -qiskit.quantum_info.random_pauli_list quantum_info -qiskit.quantum_info.random_stabilizer_table quantum_info -qiskit.quantum_info.hellinger_distance quantum_info -qiskit.quantum_info.hellinger_fidelity quantum_info -qiskit.quantum_info.two_qubit_cnot_decompose quantum_info -qiskit.quantum_info.decompose_clifford quantum_info - -qiskit.result.marginal_counts result -qiskit.result.marginal_distribution result -qiskit.result.marginal_memory result -qiskit.result.sampled_expectation_value result - -qiskit.scheduler.ScheduleConfig scheduler -qiskit.scheduler.schedule_circuit scheduler -qiskit.scheduler.methods.basic scheduler - -qiskit.synthesis.synth_cnot_count_full_pmh synthesis -qiskit.synthesis.synth_cnot_depth_line_kms synthesis -qiskit.synthesis.synth_cz_depth_line_mr synthesis -qiskit.synthesis.synth_permutation_depth_lnn_kms synthesis -qiskit.synthesis.synth_permutation_basic synthesis -qiskit.synthesis.synth_permutation_acg synthesis -qiskit.synthesis.synth_clifford_full synthesis -qiskit.synthesis.synth_clifford_ag synthesis -qiskit.synthesis.synth_clifford_bm synthesis -qiskit.synthesis.synth_clifford_greedy synthesis -qiskit.synthesis.synth_clifford_layers synthesis -qiskit.synthesis.synth_clifford_depth_lnn synthesis -qiskit.synthesis.synth_cnotdihedral_full synthesis -qiskit.synthesis.synth_cnotdihedral_two_qubits synthesis -qiskit.synthesis.synth_cnotdihedral_general synthesis -qiskit.synthesis.synth_stabilizer_layers synthesis -qiskit.synthesis.synth_stabilizer_depth_lnn synthesis -qiskit.synthesis.generate_basic_approximations synthesis - -qiskit.tools.parallel_map tools -qiskit.tools.job_monitor tools -qiskit.tools.backend_monitor tools -qiskit.tools.backend_overview tools -qiskit.tools.events.TextProgressBar tools - -qiskit.transpiler.TranspilerError transpiler -qiskit.transpiler.TranspilerAccessError transpiler - -qiskit.transpiler.preset_passmanagers.generate_preset_pass_manager transpiler_preset -qiskit.transpiler.preset_passmanagers.level_0_pass_manager transpiler_preset -qiskit.transpiler.preset_passmanagers.level_1_pass_manager transpiler_preset -qiskit.transpiler.preset_passmanagers.level_2_pass_manager transpiler_preset -qiskit.transpiler.preset_passmanagers.level_3_pass_manager transpiler_preset - -qiskit.transpiler.preset_passmanagers.common.generate_control_flow_options_check transpiler_preset -qiskit.transpiler.preset_passmanagers.common.generate_error_on_control_flow transpiler_preset -qiskit.transpiler.preset_passmanagers.common.generate_unroll_3q transpiler_preset -qiskit.transpiler.preset_passmanagers.common.generate_embed_passmanager transpiler_preset -qiskit.transpiler.preset_passmanagers.common.generate_routing_passmanager transpiler_preset -qiskit.transpiler.preset_passmanagers.common.generate_pre_op_passmanager transpiler_preset -qiskit.transpiler.preset_passmanagers.common.generate_translation_passmanager transpiler_preset -qiskit.transpiler.preset_passmanagers.common.generate_scheduling transpiler_preset - -qiskit.transpiler.preset_passmanagers.plugin.list_stage_plugins transpiler_plugins -qiskit.transpiler.preset_passmanagers.plugin.passmanager_stage_plugins transpiler_plugins - -qiskit.utils.add_deprecation_to_docstring utils -qiskit.utils.deprecate_arg utils -qiskit.utils.deprecate_arguments utils -qiskit.utils.deprecate_func utils -qiskit.utils.deprecate_function utils -qiskit.utils.local_hardware_info utils -qiskit.utils.is_main_process utils -qiskit.utils.apply_prefix utils -qiskit.utils.detach_prefix utils -qiskit.utils.wrap_method utils -qiskit.utils.summarize_circuits utils -qiskit.utils.get_entangler_map utils -qiskit.utils.validate_entangler_map utils -qiskit.utils.has_ibmq utils -qiskit.utils.has_aer utils -qiskit.utils.name_args utils -qiskit.utils.algorithm_globals utils - -qiskit.visualization.VisualizationError visualization diff --git a/docs/apidoc/algorithms.rst b/docs/apidoc/algorithms.rst deleted file mode 100644 index 25d1bd4a412b..000000000000 --- a/docs/apidoc/algorithms.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit-algorithms: - -.. automodule:: qiskit.algorithms - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/docs/apidoc/index.rst b/docs/apidoc/index.rst index 60c30286fa8c..9d1eb2f64960 100644 --- a/docs/apidoc/index.rst +++ b/docs/apidoc/index.rst @@ -30,7 +30,6 @@ API Reference primitives qasm2 qasm3 - qasm qobj qpy quantum_info @@ -44,18 +43,4 @@ API Reference transpiler_synthesis_plugins transpiler_builtin_plugins utils - utils_mitigation exceptions - -Deprecated Modules -================== - -.. warning:: - - These modules are going to be removed in Qiskit 1.0. Consider pinning ``qiskit~=0.45`` in your dependencies if you need them. - -.. toctree:: - :maxdepth: 1 - - algorithms - opflow diff --git a/docs/apidoc/opflow.rst b/docs/apidoc/opflow.rst deleted file mode 100644 index f208e883a6d0..000000000000 --- a/docs/apidoc/opflow.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit-opflow: - -.. automodule:: qiskit.opflow - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/docs/apidoc/qasm.rst b/docs/apidoc/qasm.rst deleted file mode 100644 index c1fea25947bb..000000000000 --- a/docs/apidoc/qasm.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit-qasm: - -.. automodule:: qiskit.qasm - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/docs/apidoc/utils_mitigation.rst b/docs/apidoc/utils_mitigation.rst deleted file mode 100644 index a8af5992fd6a..000000000000 --- a/docs/apidoc/utils_mitigation.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit-utils-mitigation: - -.. automodule:: qiskit.utils.mitigation - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/docs/conf.py b/docs/conf.py index 38b080208f3b..37cc420a9060 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,9 +18,6 @@ import datetime import doctest -import os -import subprocess -from pathlib import Path project = "Qiskit" project_copyright = f"2017-{datetime.date.today().year}, Qiskit Development Team" @@ -44,16 +41,10 @@ "sphinx.ext.autodoc", "sphinx.ext.autosummary", "sphinx.ext.mathjax", - "sphinx.ext.extlinks", "sphinx.ext.intersphinx", "sphinx.ext.doctest", - "nbsphinx", "matplotlib.sphinxext.plot_directive", - "qiskit_sphinx_theme", "reno.sphinxext", - "sphinx_design", - "sphinx_remove_toctrees", - "sphinx_reredirects", ] templates_path = ["_templates"] @@ -63,31 +54,9 @@ # Available keys are 'figure', 'table', 'code-block' and 'section'. '%s' is the number. numfig_format = {"table": "Table %s"} -# Translations configuration. -translations_list = [ - ("en", "English"), - ("bn_BN", "Bengali"), - ("fr_FR", "French"), - ("de_DE", "German"), - ("ja_JP", "Japanese"), - ("ko_KR", "Korean"), - ("pt_UN", "Portuguese"), - ("es_UN", "Spanish"), - ("ta_IN", "Tamil"), -] -locale_dirs = ["locale/"] -gettext_compact = False - # Relative to source directory, affects general discovery, and html_static_path and html_extra_path. exclude_patterns = ["_build", "**.ipynb_checkpoints"] -pygments_style = "colorful" - -panels_css_variables = { - "tabs-color-label-active": "rgb(138, 63, 252)", - "tabs-color-label-inactive": "rgb(221, 225, 230)", -} - # This adds the module name to e.g. function API docs. We use the default of True because our # module pages sometimes have functions from submodules on the page, and we want to make clear @@ -102,17 +71,8 @@ modindex_common_prefix = ["qiskit."] # ---------------------------------------------------------------------------------- -# Extlinks +# Intersphinx # ---------------------------------------------------------------------------------- -# Refer to https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html -extlinks = { - "pull_terra": ("https://github.com/Qiskit/qiskit-terra/pull/%s", "qiskit-terra #%s"), - "pull_aer": ("https://github.com/Qiskit/qiskit-aer/pull/%s", "qiskit-aer #%s"), - "pull_ibmq-provider": ( - "https://github.com/Qiskit/qiskit-ibmq-provider/pull/%s", - "qiskit-ibmq-provider #%s", - ), -} intersphinx_mapping = { "rustworkx": ("https://qiskit.org/ecosystem/rustworkx/", None), @@ -127,22 +87,8 @@ # HTML theme # ---------------------------------------------------------------------------------- -html_theme = "qiskit" -html_favicon = "images/favicon.ico" +html_theme = "alabaster" html_last_updated_fmt = "%Y/%m/%d" -html_context = { - # Enable segment analytics for qiskit.org/documentation - "analytics_enabled": bool(os.getenv("QISKIT_ENABLE_ANALYTICS", "")), - "theme_announcement": "🎉 Starting on December 1, 2023, Qiskit Documentation will only live on IBM Quantum", - "announcement_url": "https://medium.com/qiskit/important-changes-to-qiskit-documentation-and-learning-resources-7f4e346b19ab", - "announcement_url_text": "Learn More", -} -html_static_path = ["_static"] - -# This speeds up the docs build because it works around the Furo theme's slowdown from the left -# sidebar when the site has lots of HTML pages. But, it results in a much worse user experience, -# so we only use it in dev/CI builds. -remove_from_toctrees = ["stubs/*"] # ---------------------------------------------------------------------------------- # Autodoc @@ -211,86 +157,3 @@ # ---------------------------------------------------------------------------------- plot_html_show_formats = False - - -# ---------------------------------------------------------------------------------- -# Nbsphinx -# ---------------------------------------------------------------------------------- - -nbsphinx_timeout = int(os.getenv("QISKIT_CELL_TIMEOUT", "300")) -nbsphinx_execute = os.getenv("QISKIT_DOCS_BUILD_TUTORIALS", "never") -nbsphinx_widgets_path = "" -nbsphinx_thumbnails = {"**": "_static/images/logo.png"} - -nbsphinx_prolog = """ -{% set docname = env.doc2path(env.docname, base=None) %} - -.. only:: html - - .. role:: raw-html(raw) - :format: html - - .. note:: - This page was generated from `{{ docname }}`__. - - __ https://github.com/Qiskit/qiskit-terra/blob/main/{{ docname }} - -""" - - -# ---------------------------------------------------------------------------------- -# Redirects -# ---------------------------------------------------------------------------------- - -def determine_api_redirects() -> dict[str, str]: - """Set up API redirects for functions that we moved to module pages. - - Note that we have redirects in Cloudflare for methods moving to their class page. We - could not do this for functions because some functions still have dedicated - HTML pages, so we cannot use a generic rule. - """ - lines = Path("api_redirects.txt").read_text().splitlines() - result = {} - for line in lines: - if not line: - continue - obj_name, new_module_page_name = line.split(" ") - # E.g. `../apidoc/assembler.html#qiskit.assembler.assemble_circuits - new_url = ( - "https://qiskit.org/documentation/apidoc/" + - f"{new_module_page_name}.html#{obj_name}" - ) - result[f"stubs/{obj_name}"] = new_url - return result - - -redirects = determine_api_redirects() - - -# --------------------------------------------------------------------------------------- -# Prod changes -# --------------------------------------------------------------------------------------- - -if os.getenv("DOCS_PROD_BUILD"): - # `viewcode` slows down docs build by about 14 minutes. - extensions.append("sphinx.ext.viewcode") - # Include all pages in the left sidebar in prod. - remove_from_toctrees = [] - - -# --------------------------------------------------------------------------------------- -# Custom extensions -# --------------------------------------------------------------------------------------- - - -def add_versions_to_config(_app, config): - """Add a list of old documentation versions that should have links generated to them into the - context, so the theme can use them to generate a sidebar.""" - # For qiskit 1.0 the docs won't use this mechanism anymore - # so just build out the historical version list for the 0.x series - versions = ["0.19"] + [f"0.{x}" for x in range(24, 46)] - config.html_context["version_list"] = versions - - -def setup(app): - app.connect("config-inited", add_versions_to_config) diff --git a/docs/configuration.rst b/docs/configuration.rst deleted file mode 100644 index 600f8cc90db2..000000000000 --- a/docs/configuration.rst +++ /dev/null @@ -1,98 +0,0 @@ -Local Configuration -=================== - -Once you have Qiskit installed and running there are some optional configuration -steps you can take to change the default behavior of Qiskit for your specific -use case. - -User Config File ----------------- - -The main location for local configuration of Qiskit is the user config file. -This is an `ini `__ format file that -can be used to change defaults in Qiskit. - -For example: - -.. code-block:: ini - - [default] - circuit_drawer = mpl - circuit_mpl_style = default - circuit_mpl_style_path = ~:~/.qiskit - state_drawer = hinton - transpile_optimization_level = 3 - parallel = False - num_processes = 15 - -By default this file lives in ``~/.qiskit/settings.conf`` but the path used -can be overridden with the ``QISKIT_SETTINGS`` environment variable. If -``QISKIT_SETTINGS`` is set its value will used as the path to the user config -file. - -Available options: - - * ``circuit_drawer``: This is used to change the default backend for - the circuit drawer :meth:`qiskit.circuit.QuantumCircuit.draw` and - :func:`qiskit.visualization.circuit_drawer`. It can be set to ``latex``, - ``mpl``, ``text``, or ``latex_source`` and when the ``ouptut`` kwarg is - not explicitly set that drawer backend will be used. - * ``circuit_mpl_style``: This is the default style sheet used for the - ``mpl`` output backend for the circuit drawer - :meth:`qiskit.circuit.QuantumCircuit.draw` and - :func:`qiskit.visualization.circuit_drawer`. It can be set to ``default`` - or ``bw``. - * ``circuit_mpl_style_path``: This can be used to set the path(s) to have the - circuit drawer, :meth:`qiskit.circuit.QuantumCircuit.draw` or - :func:`qiskit.visualization.circuit_drawer`, use to look for json style - sheets when using the ``mpl`` output mode. - * ``state_drawer``: This is used to change the default backend for the - state visualization draw methods :meth:`qiskit.quantum_info.Statevector.draw` - and :meth:`qiskit.quantum_info.DensityMatrix.draw`. It can be set to - ``repr``, ``text``', ``latex``, ``latex_source``, ``qsphere``, ``hinton``, - or bloch ``bloch`` and when the ``output`` kwarg is not explicitly set on - the :meth:`~qiskit.quantum_info.DensityMatrix.draw` method that output - method will be used. - * ``transpile_optimization_level``: This takes an integer between 0-3 and is - used to change the default optimization level for - :func:`~qiskit.compiler.transpile` and :func:`~qiskit.execute.execute`. - * ``parallel``: This option takes a boolean value (either ``True`` or - ``False``) and is used to configure whether - `Python multiprocessing `__ - is enabled for operations that support running in parallel (for example - transpilation of multiple :class:`~qiskit.circuit.QuantumCircuit` objects). - The default setting in the user config file can be overridden by - the ``QISKIT_PARALLEL`` environment variable. - * ``num_processes``: This option takes an integer value (> 0) that is used - to specify the maximum number of parallel processes to launch for parallel - operations if parallel execution is enabled. The default setting in the - user config file can be overridden by the ``QISKIT_NUM_PROCS`` environment - variable. - -Environment Variables ---------------------- - -There are also a few environment variables that can be set to alter the default -behavior of Qiskit. - - * ``QISKIT_PARALLEL``: if this variable is set to ``TRUE`` it will enable - the use of - `Python multiprocessing `__ - to parallelize certain operations (for example transpilation over multiple - circuits) in Qiskit. - * ``QISKIT_NUM_PROCS``: Specifies the maximum number of parallel processes to - launch for parallel operations if parallel execution is enabled. It takes an - integer > 0 as the expected value. - * ``RAYON_NUM_THREADS``: Specifies the number of threads to run multithreaded - operations in Qiskit. By default this multithreaded code will launch - a thread for each logical CPU, if you'd like to adjust the number of threads - Qiskit will use you can set this to an integer value. For example, setting - ``RAYON_NUM_THREADS=4`` will only launch 4 threads for multithreaded - functions. - * ``QISKIT_FORCE_THREADS``: Specify that multithreaded code should always - execute in multiple threads. By default if you're running multithreaded code - in a section of Qiskit that is already running in parallel processes Qiskit - will not launch multiple threads and instead execute that function serially. - This is done to avoid potentially overloading limited CPU resources. However, - if you would like to force the use of multiple threads even when in a - multiprocess context you can set ``QISKIT_FORCE_THREADS=TRUE`` to do this. diff --git a/docs/contributing_to_qiskit.rst b/docs/contributing_to_qiskit.rst deleted file mode 100644 index bd02628fa87f..000000000000 --- a/docs/contributing_to_qiskit.rst +++ /dev/null @@ -1,189 +0,0 @@ -###################### -Contributing to Qiskit -###################### - -Qiskit is an open-source project committed to bringing quantum computing to -people of all backgrounds. This page describes how you can join the Qiskit -community in this goal. - -**************** -Before You Start -**************** - -If you are new to Qiskit contributing we recommend you do the following before diving into the code: - -#. Read the `Code of Conduct `__ -#. Read the repo-specific :ref:`Contributing Guidelines ` for the repo you have decided to contribute to. -#. :ref:`Set up your development environment ` -#. Familiarize yourself with the Qiskit community (via `Slack `__, - `Stack Exchange `__, `GitHub `__ etc.) - -.. _contributing_links: - -******************************** -Contributing to a Specific Repo -******************************** - -Each Qiskit package has its own set of Contributing Guidelines (kept in the ``CONTRIBUTING.md`` file) which -details specific information on contributing to that repository. Make sure you read through the repo-specific -Contributing Guidelines prior to making your contribution to a specific repo as each project may have -slightly different requirements and processes. For Qiskit Terra, the main repository, the contributing guidelines -may be be found `here `__. Other Qiskit packages that -are able to receive contributions may be found as separate repositories in the official `Qiskit Github `__. - -.. _dev-env-setup: - -Set up Your Development Environment -=================================== - -To get started contributing to the Python-based Qiskit repos you will need to set up a Python Virtual -Development Environment and install the appropriate package **from source**. - -For a quick guide on how to do this for qiskit-terra take a look at the -`How to Install Qiskit - Contributors `__ YouTube video. - -For non-python packages you should check the CONTRIBUTING.md file for specific details on setting up your dev environment. - -Set up Python Virtual Development Environment ---------------------------------------------- - -Virtual environments are used for Qiskit development to isolate the development environment -from system-wide packages. This way, we avoid inadvertently becoming dependent on a -particular system configuration. For developers, this also makes it easy to maintain multiple -environments (e.g. one per supported Python version, for older versions of Qiskit, etc.). - -.. tab-set:: - - .. tab-item:: Python venv - - All Python versions supported by Qiskit include built-in virtual environment module - `venv `__. - - Start by creating a new virtual environment with ``venv``. The resulting - environment will use the same version of Python that created it and will not inherit installed - system-wide packages by default. The specified folder will be created and is used to hold the environment's - installation. It can be placed anywhere. For more detail, see the official Python documentation, - `Creation of virtual environments `__. - - .. code-block:: sh - - python3 -m venv ~/.venvs/qiskit-dev - - Activate the environment by invoking the appropriate activation script for your system, which can - be found within the environment folder. For example, for bash/zsh: - - .. code-block:: sh - - source ~/.venvs/qiskit-dev/bin/activate - - Upgrade pip within the environment to ensure Qiskit dependencies installed in the subsequent sections - can be located for your system. - - .. code-block:: sh - - pip install -U pip - - .. tab-item:: Conda - - For Conda users, a new environment can be created as follows. - - .. code-block:: sh - - conda create -y -n QiskitDevenv python=3 - conda activate QiskitDevenv - - - -.. code:: sh - - pip install -e . - - -Pull Requests -============= - -We use `GitHub pull requests -`__ to accept -contributions. - -While not required, opening a new issue about the bug you're fixing or the -feature you're working on before you open a pull request is an important step -in starting a discussion with the community about your work. The issue gives us -a place to talk about the idea and how we can work together to implement it in -the code. It also lets the community know what you're working on, and if you -need help, you can reference the issue when discussing it with other community -and team members. - -If you've written some code but need help finishing it, want to get initial -feedback on it prior to finishing it, or want to share it and discuss prior -to finishing the implementation, you can open a *Draft* pull request and prepend -the title with the **\[WIP\]** tag (for Work In Progress). This will indicate -to reviewers that the code in the PR isn't in its final state and will change. -It also means that we will not merge the commit until it is finished. You or a -reviewer can remove the [WIP] tag when the code is ready to be fully reviewed for merging. - -Before marking your Pull Request as "ready for review" make sure you have followed the -PR Checklist below. PRs that adhere to this list are more likely to get reviewed and -merged in a timely manner. - -.. _pr-checklist: - -**Pull Request Checklist:** ---------------------------- -- You have followed the requirements in the CONTRIBUTING.md file for the specific repo you are - contributing to. -- All CI checks pass (it's recommended to run tests and lint checks locally before pushing). -- New tests have been added for any new functionality that has been introduced. -- The documentation has been updated accordingly for any new/modified functionality. -- A release note has been added if the change has a user-facing impact. -- Any superfluous comments or print statements have been removed. -- All contributors have signed the :ref:`cla`. -- The PR has a concise and explanatory title (e.g. ``Fixes Issue1234`` is a bad title!). -- If the PR addresses an open issue the PR description includes the ``fixes #issue-number`` - syntax to link the PR to that issue (**you must use the exact phrasing in order for GitHub - to automatically close the issue when the PR merges**) - - - -Code Review -=========== - -Code review is done in the open and is open to anyone. While only maintainers have -access to merge commits, community feedback on pull requests is extremely valuable. -It is also a good mechanism to learn about the code base. - -Response times may vary for your PR, it is not unusual to wait a few weeks for a maintainer -to review your work, due to other internal commitments. If you have been waiting over a week -for a review on your PR feel free to tag the relevant maintainer in a comment to politely remind -them to review your work. - -Please be patient! Maintainers have a number of other priorities to focus on and so it may take -some time for your work to get reviewed and merged. PRs that are in a good shape (i.e. following the :ref:`pr-checklist`) -are easier for maintainers to review and more likely to get merged in a timely manner. Please also make -sure to always be kind and respectful in your interactions with maintainers and other contributors, you can read -`the Qiskit Code of Conduct `__. - - - -.. _cla: - -Contributor License Agreement -============================= - -Before you can submit any code, all contributors must sign a -contributor license agreement (CLA). By signing a CLA, you're attesting -that you are the author of the contribution, and that you're freely -contributing it under the terms of the Apache-2.0 license. - -When you contribute to the Qiskit project with a new pull request, -a bot will evaluate whether you have signed the CLA. If required, the -bot will comment on the pull request, including a link to accept the -agreement. The `individual CLA `__ -document is available for review as a PDF. - -.. note:: - - If your contribution is part of your employment or your contribution - is the property of your employer, then you will more than likely need to sign a - `corporate CLA `__ too and - email it to us at . diff --git a/docs/deprecation_policy.rst b/docs/deprecation_policy.rst deleted file mode 100644 index b25d93ecaf55..000000000000 --- a/docs/deprecation_policy.rst +++ /dev/null @@ -1,237 +0,0 @@ -################## -Deprecation Policy -################## - -Many users and other packages depend on different parts of Qiskit. We must -make sure that whenever we make changes to the code, we give users ample time to -adjust without breaking code that they have already written. - -Most importantly: *do not* change any interface that is public-facing unless we -absolutely have to. Adding things is ok, taking things away is annoying for -users but can be handled reasonably with plenty notice, but changing behavior -generally means users cannot write code that will work with two subsequent -versions of Qiskit, which is not acceptable. - -Beware that users will often be using functions, classes and methods that we, -the Qiskit developers, may consider internal or not widely used. Do not make -assumptions that "this is buried, so nobody will be using it"; if it is public, -it is subject to the policy. The only exceptions here are functions and modules -that are explicitly internal, *i.e.* those whose names begin with a leading -underscore (``_``). - -The guiding principles are: - -- we must not remove or change code without active warnings for least three - months or two complete version cycles; - -- there must always be a way to achieve valid goals that does not issue any - warnings; - -- never assume that a function that isn't explicitly internal isn't in use; - -- all deprecations, changes and removals are considered API changes, and can - only occur in minor releases not patch releases, per the - :ref:`stable branch policy `. - -.. _removing-features: - -Removing a feature -================== - -When removing a feature (for example a class, function or function parameter), -we will follow this procedure: - -#. The alternative path must be in place for one minor version before any - warnings are issued. For example, if we want to replace the function ``foo()`` - with ``bar()``, we must make at least one release with both functions before - issuing any warnings within ``foo()``. You may issue - ``PendingDeprecationWarning``\ s from the old paths immediately. - - *Reason*: we need to give people time to swap over without breaking their - code as soon as they upgrade. - -#. After the alternative path has been in place for at least one minor version, - :ref:`issue the deprecation warnings `. Add a - release note with a ``deprecations`` section listing all deprecated paths, - their alternatives, and the reason for deprecation. :ref:`Update the tests - to test the warnings `. - - *Reason*: removals must be highly visible for at least one version, to - minimize the surprise to users when they actually go. - -#. Set a removal date for the old feature, and remove it (and the warnings) when - reached. This must be at least three months after the version with the - warnings was first released, and cannot be the minor version immediately - after the warnings. Add an ``upgrade`` release note that lists all the - removals. For example, if the alternative path was provided in ``0.19.0`` - and the warnings were added in ``0.20.0``, the earliest version for removal - is ``0.22.0``, even if ``0.21.0`` was released more than three months after - ``0.20.0``. - - .. note:: - - These are *minimum* requirements. For removal of significant or core features, give - users at least an extra minor version if not longer. - - *Reason*: there needs to be time for users to see these messages, and to give - them time to adjust. Not all users will update their version of Qiskit - immediately, and some may skip minor versions. - -When a feature is marked as deprecated it is slated for removal, but users -should still be able to rely on it to work correctly. We consider a feature -marked "deprecated" as frozen; we commit to maintaining it with critical bug -fixes until it is removed, but we won't merge new functionality to it. - - -Changing behavior -================= - -Changing behavior without a removal is particularly difficult to manage, because -we need to have both options available for two versions, and be able to issue -warnings. For example, changing the type of the return value from a function -will almost invariably involve making an API break, which is frustrating for -users and makes it difficult for them to use Qiskit. - -The best solution here is often to make a new function, and then use :ref:`the -procedures for removal ` above. - -If you absolutely must change the behavior of existing code (other than fixing -bugs), you will need to use your best judgment to apply the guiding principles -at the top of this document. The most appropriate warning for behavioral -changes is usually ``FutureWarning``. Some possibilities for how to effect a -change: - -- If you are changing the default behavior of a function, consider adding a - keyword argument to select between old and new behaviors. When it comes time, - you can issue a ``FutureWarning`` if the keyword argument is not given - (*e.g.* if it is ``None``), saying that the new value will soon become the - default. You will need to go through the normal deprecation period for - removing this keyword argument after you have made the behavior change. This - will take at least six months to go through both cycles. - -- If you need to change the return type of a function, consider adding a new - function that returns the new type, and then follow the procedures for - deprecating the old function. - -- If you need to accept a new input that you cannot distinguish from an existing - possibility because of its type, consider letting it be passed by a different - keyword argument, or add a second function that only accepts the new form. - - -.. _issuing-deprecation-warnings: - -Issuing deprecation warnings -============================ - -The proper way to raise a deprecation warning is to use the decorators ``@deprecate_arg`` and -``@deprecate_func`` from ``qiskit.utils.deprecation``. These will generate a standardized message and -and add the deprecation to that function's docstring so that it shows up in the docs. - -.. code-block:: python - - from qiskit.utils.deprecation import deprecate_arg, deprecate_func - - @deprecate_func(since="0.24.0", additional_msg="No replacement is provided.") - def deprecated_func(): - pass - - @deprecate_arg("bad_arg", new_alias="new_name", since="0.24.0") - def another_func(bad_arg: str, new_name: str): - pass - -Usually, you should set ``additional_msg: str `` with the format ``"Instead, use ..."`` so that -people know how to migrate. Read those functions' docstrings for additional arguments like -``pending: bool`` and ``predicate``. - -If you are deprecating outside the main Qiskit repo, set ``package_name`` to match your package. -Alternatively, if you prefer to use your own decorator helpers, then have them call -``add_deprecation_to_docstring`` from ``qiskit.utils.deprecation``. - -If ``@deprecate_func`` and ``@deprecate_arg`` cannot handle your use case, consider improving -them. Otherwise, you can directly call the ``warn`` function -from the `warnings module in the Python standard library -`__, using the category -``DeprecationWarning``. For example: - -.. code-block:: python - - import warnings - - def deprecated_function(): - warnings.warn( - "The function qiskit.deprecated_function() is deprecated since " - "Qiskit Terra 0.20.0, and will be removed 3 months or more later. " - "Instead, you should use qiskit.other_function().", - category=DeprecationWarning, - stacklevel=2, - ) - # ... the rest of the function ... - -Make sure you include the version of the package that introduced the deprecation -warning (so maintainers can easily see when it is valid to remove it), and what -the alternative path is. - -Take note of the ``stacklevel`` argument. This controls which function is -accused of being deprecated. Setting ``stacklevel=1`` (the default) means the -warning will blame the ``warn`` function itself, while ``stacklevel=2`` will -correctly blame the containing function. It is unusual to set this to anything -other than ``2``, but can be useful if you use a helper function to issue the -same warning in multiple places. - - -.. _testing-deprecated-functionality: - -Testing deprecated functionality -================================ - -Whenever you add deprecation warnings, you will need to update tests involving -the functionality. The test suite should fail otherwise, because of the new -warnings. We must continue to test deprecated functionality throughout the -deprecation period, to ensure that it still works. - -To update the tests, you need to wrap each call of deprecated behavior in its -own assertion block. For subclasses of ``unittest.TestCase`` (which all Qiskit -test cases are), this is done by: - -.. code-block:: python - - class MyTestSuite(QiskitTestCase): - def test_deprecated_function(self): - with self.assertWarns(DeprecationWarning): - output = deprecated_function() - # ... do some things with output ... - self.assertEqual(output, expected) - -Documenting deprecations and breaking changes -============================================= - -It is important to warn the user when your breaking changes are coming. - -``@deprecate_arg`` and ``@deprecate_func`` will automatically add the deprecation to the docstring -for the function so that it shows up in docs. - -If you are not using those decorators, you should directly add a `Sphinx deprecated directive -`__ :: - -.. code-block:: python - - def deprecated_function(): - """ - Short description of the deprecated function. - - .. deprecated:: 0.20.0 - The function qiskit.deprecated_function() is deprecated since - Qiskit Terra 0.20.0, and will be removed 3 months or more later. - Instead, you should use qiskit.other_function(). - - - """ - # ... the rest of the function ... - -You should also document the deprecation in the changelog by using Reno. Explain the deprecation -and how to migrate. - -In particular situations where a deprecation or change might be a major disruptor for users, a -*migration guide* might be needed. Once the migration guide is written and published, deprecation -messages and documentation should link to it (use the ``additional_msg: str`` argument for -``@deprecate_arg`` and ``@deprecate_func``). diff --git a/docs/explanation/endianness.rst b/docs/explanation/endianness.rst deleted file mode 100644 index ee78ce4d17e0..000000000000 --- a/docs/explanation/endianness.rst +++ /dev/null @@ -1,47 +0,0 @@ -######################### -Order of qubits in Qiskit -######################### - -While most physics textbooks represent an :math:`n`-qubit system as the tensor product :math:`Q_0\otimes Q_1 \otimes ... \otimes Q_{n-1}`, where :math:`Q_j` is the :math:`j^{\mathrm{th}}` qubit, Qiskit uses the inverse order, that is, :math:`Q_{n-1}\otimes ... \otimes Q_1 \otimes Q_{0}`. As explained in `this video `_ from `Qiskit's YouTube channel `_, this is done to follow the convention in classical computing, in which the :math:`n^{\mathrm{th}}` bit or most significant bit (MSB) is placed on the left (with index 0) while the least significant bit (LSB) is placed on the right (index :math:`n-1`). This ordering convention is called little-endian while the one from the physics textbooks is called big-endian. - -This means that if we have, for example, a 3-qubit system with qubit 0 in state :math:`|1\rangle` and qubits 1 and 2 in state :math:`|0\rangle`, Qiskit would represent this state as :math:`|001\rangle` while most physics textbooks would represent this state as :math:`|100\rangle`. - -The matrix representation of any multi-qubit gate is also affected by this different qubit ordering. For example, if we consider the single-qubit gate - -.. math:: - - U = \begin{pmatrix} u_{00} & u_{01} \\ u_{10} & u_{11} \end{pmatrix} - -And we want a controlled version :math:`C_U` whose control qubit is qubit 0 and whose target is qubit 1, following Qiskit's ordering its matrix representation would be - -.. math:: - - C_U = \begin{pmatrix} 1 & 0 & 0 & 0 \\0 & u_{00} & 0 & u_{01} \\ 0 & 0 & 1 & 0 \\ 0 & u_{10} & 0& u_{11} \end{pmatrix} - -while in a physics textbook it would be written as - -.. math:: - - C_U = \begin{pmatrix} 1 & 0 & 0 & 0 \\0 & 1 & 0 & 0 \\ 0 & 0 & u_{00} & u_{01} \\ 0 & 0 & u_{00} & u_{01} \end{pmatrix} - - -For more details about how this ordering of MSB and LSB affects the matrix representation of any particular gate, check its entry in the circuit :mod:`~qiskit.circuit.library`. - -This different order can also make the circuit corresponding to an algorithm from a textbook a bit more complicated to visualize. Fortunately, Qiskit provides a way to represent a :class:`~.QuantumCircuit` with the most significant qubits on top, just like in the textbooks. This can be done by setting the ``reverse_bits`` argument of the :meth:`~.QuantumCircuit.draw` method to ``True``. - -Let's try this for a 3-qubit Quantum Fourier Transform (:class:`~.QFT`). - -.. plot:: - :include-source: - :context: - - from qiskit.circuit.library import QFT - - qft = QFT(3) - qft.decompose().draw('mpl') - -.. plot:: - :include-source: - :context: close-figs - - qft.decompose().draw('mpl', reverse_bits=True) \ No newline at end of file diff --git a/docs/explanation/index.rst b/docs/explanation/index.rst deleted file mode 100644 index 4a2d55c82ccb..000000000000 --- a/docs/explanation/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _explanation: - -########### -Explanation -########### - - - -.. toctree:: - :maxdepth: 1 - - Order of qubits in Qiskit \ No newline at end of file diff --git a/docs/faq.rst b/docs/faq.rst deleted file mode 100644 index 39624820a26c..000000000000 --- a/docs/faq.rst +++ /dev/null @@ -1,78 +0,0 @@ -.. _faq: - -========================== -Frequently Asked Questions -========================== - -**Q: How should I cite Qiskit in my research?** - -**A:** Please cite Qiskit by using the included `BibTeX file -`__. - -| - -**Q: Why do I receive the error message** ``AttributeError: QuantumCircuit object has no attribute save_state`` -**when using ``save_*``method on a circuit?** - -**A:** The ``save_*`` instructions are part of Qiskit Aer project, -a high performance simulator for quantum circuits. These instructions do not -exist outside of Qiskit Aer and are added dynamically to the -:class:`~.QuantumCircuit` class by Qiskit Aer on import. If you would like to -use these instructions you must first ensure that you have imported -``qiskit_aer`` in your program before trying to call these methods. You -can refer to :mod:`qiskit_aer.library` for the details of these custom -instructions included with Qiskit Aer. - -**Q: Why do my results from real devices differ from my results from the simulator?** - -**A:** The simulator runs jobs as though is was in an ideal environment; one -without noise or decoherence. However, when jobs are run on the real devices -there is noise from the environment and decoherence, which causes the qubits -to behave differently than what is intended. - -| - -**Q: Why do I receive the error message,** ``No Module 'qiskit'`` **when using Jupyter Notebook?** - -**A:** If you used ``pip install qiskit`` and set up your virtual environment in -Anaconda, then you may experience this error when you run a tutorial -in Jupyter Notebook. If you have not installed Qiskit or set up your -virtual environment, you can follow the :ref:`installation` steps. - -The error is caused when trying to import the Qiskit package in an -environment where Qiskit is not installed. If you launched Jupyter Notebook -from the Anaconda-Navigator, it is possible that Jupyter Notebook is running -in the base (root) environment, instead of in your virtual -environment. Choose a virtual environment in the Anaconda-Navigator from the -**Applications on** dropdown menu. In this menu, you can see -all of the virtual environments within Anaconda, and you can -select the environment where you have Qiskit installed to launch Jupyter -Notebook. - -| - -**Q: Why am I getting a compilation error while installing ``qiskit``?** - -**A:** Qiskit depends on a number of other open source Python packages, which -are automatically installed when doing ``pip install qiskit``. Depending on -your system's platform and Python version, it is possible that a particular -package does not provide pre-built binaries for your system. You can refer -to :ref:`platform_support` for a list of platforms supported by Qiskit, some -of which may need an extra compiler. In cases where there are -no precompiled binaries available ``pip`` will attempt to compile the package -from source, which in turn might require some extra dependencies that need to -be installed manually. - -If the output of ``pip install qiskit`` contains similar lines to: - -.. code:: sh - - Failed building wheel for SOME_PACKAGE - ... - build/temp.linux-x86_64-3.5/_openssl.c:498:30: fatal error - compilation terminated. - error: command 'x86_64-linux-gnu-gcc' failed with exit status 1 - -please check the documentation of the package that failed to install (in the -example code, ``SOME_PACKAGE``) for information on how to install the libraries -needed for compiling from source. diff --git a/docs/getting_started.rst b/docs/getting_started.rst deleted file mode 100644 index 2b9f94d280fd..000000000000 --- a/docs/getting_started.rst +++ /dev/null @@ -1,267 +0,0 @@ -:orphan: - -############### -Getting started -############### - -.. _installation: - -Installation -============ - -Let's get started using Qiskit! The first thing to do is choose how you're -going to run and install the packages. There are three main ways to do this: - -.. tab-set:: - - .. tab-item:: Start locally - - Qiskit supports Python 3.7 or later. However, both Python and Qiskit are - evolving ecosystems, and sometimes when new releases occur in one or the other, - there can be problems with compatibility. - - You will need to `download Python `__ - on your local system to get started. `Jupyter `__ is recommended for - interacting with Qiskit. - - We recommend using `Python virtual environments `__ - to cleanly separate Qiskit from other applications and improve your experience. - - Create a minimal environment with only Python installed in it. - - .. code:: text - - python3 -m venv /path/to/virtual/environment - - Activate your new environment. - - .. code:: text - - source /path/to/virtual/environment/bin/activate - - - Note: if you are using Windows, use the following commands in PowerShell. - - .. code:: text - - python3 -m venv c:\path\to\virtual\environment - c:\path\to\virtual\environment\Scripts\Activate.ps1 - - - Next, install the Qiskit package. - - .. code:: text - - pip install qiskit - - If the packages were installed correctly, you can run ``pip list`` to see the active - packages in your virtual environment. - - If you intend to use visualization functionality or Jupyter notebooks it is - recommended to install Qiskit with the extra ``visualization`` support: - - .. code:: text - - pip install qiskit[visualization] - - It is worth pointing out that if you're a zsh user (which is the default shell on newer - versions of macOS), you'll need to put ``qiskit[visualization]`` in quotes: - - .. code:: text - - pip install 'qiskit[visualization]' - - .. tab-item:: Start on the cloud - - The following cloud vendors have Qiskit pre-installed in their environments: - - .. qiskit-card:: - :header: IBM Quantum Lab - :card_description: Build quantum applications and experiments with Qiskit in a cloud programming environment. - :image: _static/images/ibm_qlab.png - :link: https://quantum-computing.ibm.com/ - - .. qiskit-card:: - :header: Strangeworks - :card_description: A platform that enables users and organizations to easily apply quantum computing to their most pressing problems and research. - :image: _static/images/strangeworks.png - :link: https://strangeworks.com/ - - .. tab-item:: Install from source - - Installing Qiskit from source allows you to access the current development - version, instead of using the version in the Python Package - Index (PyPI) repository. This will give you the ability to inspect and extend - the latest version of the Qiskit code more efficiently. - - Begin by making a new virtual environment and activating it: - - .. code-block:: text - - python3 -m venv QiskitDevenv - source QiskitDevenv/bin/activate - - Installing from source requires that you have the Rust compiler on your system. - To install the Rust compiler the recommended path is to use rustup, which is - a cross-platform Rust installer. To use rustup you can go to: - - https://rustup.rs/ - - which will provide instructions for how to install rust on your platform. - Besides rustup there are - `other installation methods `__ available too. - - Once the Rust compiler is installed, you are ready to install Qiskit. - - 1. Clone the Qiskit repository. - - .. code:: text - - git clone https://github.com/Qiskit/qiskit-terra.git - - 2. Cloning the repository creates a local folder called ``qiskit-terra``. - - .. code:: text - - cd qiskit-terra - - 3. If you want to run tests or linting checks, install the developer requirements. - - .. code:: text - - pip install -r requirements-dev.txt - - 4. Install ``qiskit-terra``. - - .. code:: text - - pip install . - - If you want to install it in editable mode, meaning that code changes to the - project don't require a reinstall to be applied, you can do this with: - - .. code:: text - - pip install -e . - - Installing in editable mode will build the compiled extensions in debug mode - without optimizations. This will affect the runtime performance of the compiled - code. If you'd like to use editable mode and build the compiled code in release - with optimizations enabled you can run: - - .. code:: text - - python setup.py build_rust --release --inplace - - after you run pip and that will rebuild the binary in release mode. - If you are working on Rust code in Qiskit you will need to rebuild the extension - code every time you make a local change. ``pip install -e .`` will only build - the Rust extension when it's called, so any local changes you make to the Rust - code after running pip will not be reflected in the installed package unless - you rebuild the extension. You can leverage the above ``build_rust`` command to - do this (with or without ``--release`` based on whether you want to build in - debug mode or release mode). - - You can then run the code examples after installing Qiskit. You can - run the example with the following command. - - .. code:: text - - python examples/python/using_qiskit_terra_level_0.py - -.. _platform_support: - -Platform Support ----------------- - -Qiskit strives to support as many platforms as possible, but due to limitations -in available testing resources and platform availability, not all platforms -can be supported. Platform support for Qiskit is broken into 3 tiers with different -levels of support for each tier. For platforms outside these, Qiskit is probably -still installable, but it's not tested and you will have to build Qiskit (and likely -Qiskit's dependencies) from source. - -Additionally, Qiskit only supports CPython. Running with other Python -interpreters isn't currently supported. - -Tier 1 -'''''' - -Tier 1 supported platforms are fully tested upstream as part of the development -processes to ensure any proposed change will function correctly. Pre-compiled -binaries are built, tested, and published to PyPI as part of the release process. -These platforms are expected to be installable with just a functioning Python -environment as all dependencies are available on these platforms. - -Tier 1 platforms are currently: - - * Linux x86_64 (distributions compatible with the - `manylinux 2014 `__ - packaging specification). - * macOS x86_64 (10.9 or newer) - * Windows 64 bit - -Tier 2 -'''''' - -Tier 2 platforms are not tested upstream as part of development process. However, -pre-compiled binaries are built, tested, and published to PyPI as part of the -release process and these packages can be expected to be installed with just a -functioning Python environment. - -Tier 2 platforms are currently: - - * Linux i686 (distributions compatible with the - `manylinux 2014 `__ packaging - specification) for Python < 3.10 - * Windows 32 bit for Python < 3.10 - * Linux aarch64 (distributions compatible with the - `manylinux 2014 `__ packaging - specification) - -Tier 3 -'''''' - -Tier 3 platforms are not tested upstream as part of the development process. Pre-compiled -binaries are built and published to PyPI as part of the release process, with no -testing at all. They may not be installable with just a functioning Python -environment and may require a C/C++ compiler or additional programs to build -dependencies from source as part of the installation process. Support for these -platforms are best effort only. - -Tier 3 platforms are currently: - - * Linux ppc64le (distributions compatible with the - `manylinux 2014 `__ packaging - specification) - * Linux s390x (distributions compatible with the - `manylinux 2014 `__ packaging - specification) - * macOS arm64 (10.15 or newer) - * Linux i686 (distributions compatible with the - `manylinux 2014 `__ packaging - specification) for Python >= 3.10 - * Windows 32 bit for Python >= 3.10 - -Ready to get going?... -====================== - -.. qiskit-call-to-action-grid:: - - .. qiskit-call-to-action-item:: - :description: Learn how to build, execute, and post-process quantum circuits with Qiskit. - :header: Qiskit from the ground up - :button_link: intro_tutorial1.html - :button_text: Start learning Qiskit - - .. qiskit-call-to-action-item:: - :description: Find out how to leverage Qiskit for everything from single-circuits to full quantum application development. - :header: Dive into the tutorials - :button_link: tutorials.html - :button_text: Qiskit tutorials - - -.. Hiding - Indices and tables - :ref:`genindex` - :ref:`modindex` - :ref:`search` diff --git a/docs/how_to/index.rst b/docs/how_to/index.rst deleted file mode 100644 index a20d20870d99..000000000000 --- a/docs/how_to/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _how_to: - -############# -How-to Guides -############# - - -Use the primitives -================== - -.. toctree:: - :maxdepth: 1 - - use_sampler - use_estimator \ No newline at end of file diff --git a/docs/how_to/use_estimator.rst b/docs/how_to/use_estimator.rst deleted file mode 100644 index 61a69ab5ffbc..000000000000 --- a/docs/how_to/use_estimator.rst +++ /dev/null @@ -1,269 +0,0 @@ -######################################################### -Compute an expectation value with ``Estimator`` primitive -######################################################### - -This guide shows how to get the expected value of an observable for a given quantum circuit with the :class:`~qiskit.primitives.Estimator` primitive. - -.. note:: - - While this guide uses Qiskit’s reference implementation, the ``Estimator`` primitive can be run with any provider using :class:`~qiskit.primitives.BackendEstimator` . - - .. code-block:: - - from qiskit.primitives import BackendEstimator - from import QiskitProvider - - provider = QiskitProvider() - backend = provider.get_backend('backend_name') - estimator = BackendEstimator(backend) - - There are some providers that implement primitives natively (see `this page `_ for more details). - - -Initialize observables -====================== - -The first step is to define the observables whose expected value you want to compute. Each observable can be any ``BaseOperator``, like the operators from :mod:`qiskit.quantum_info`. -Among them it is preferable to use :class:`~qiskit.quantum_info.SparsePauliOp`. - -.. testcode:: - - from qiskit.quantum_info import SparsePauliOp - - observable = SparsePauliOp(["II", "XX", "YY", "ZZ"], coeffs=[1, 1, -1, 1]) - -Initialize quantum circuit -========================== - -Then you need to create the :class:`~qiskit.circuit.QuantumCircuit`\ s for which you want to obtain the expected value. - -.. plot:: - :include-source: - - from qiskit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0,1) - qc.draw("mpl") - -.. testsetup:: - - # This code is repeated (but hidden) because we will need to use the variables with the extension sphinx.ext.doctest (testsetup/testcode/testoutput directives) - # and we can't reuse the variables from the plot directive above because they are incompatible. - # The plot directive is used to draw the circuit with matplotlib and the code is shown because of the include-source flag. - - from qiskit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0,1) - -.. note:: - - The :class:`~qiskit.circuit.QuantumCircuit` you pass to :class:`~qiskit.primitives.Estimator` must not include any measurements. - -Initialize the ``Estimator`` -============================ - -Then, you need to instantiate an :class:`~qiskit.primitives.Estimator`. - -.. testcode:: - - from qiskit.primitives import Estimator - - estimator = Estimator() - -Run and get results -=================== - -Now that you have defined your ``estimator``, you can run your estimation by calling the :meth:`~qiskit.primitives.Estimator.run` method, -which returns an instance of :class:`~.PrimitiveJob` (subclass of :class:`~qiskit.providers.JobV1`). You can get the results from the job (as a :class:`~qiskit.primitives.EstimatorResult` object) -with the :meth:`~qiskit.providers.JobV1.result` method. - -.. testcode:: - - job = estimator.run(qc, observable) - result = job.result() - print(result) - -.. testoutput:: - - EstimatorResult(values=array([4.]), metadata=[{}]) - -While this example only uses one :class:`~qiskit.circuit.QuantumCircuit` and one observable, if you want to get expectation values for multiple circuits and observables you can -pass a ``list`` of :class:`~qiskit.circuit.QuantumCircuit`\ s and a list of ``BaseOperator``\ s to the :meth:`~qiskit.primitives.Estimator.run` method. Both ``list``\ s must have -the same length. - -Get the expected value ----------------------- - -From these results you can extract the expected values with the attribute :attr:`~qiskit.primitives.EstimatorResult.values`. - -:attr:`~qiskit.primitives.EstimatorResult.values` returns a :class:`numpy.ndarray` -whose ``i``-th element is the expectation value corresponding to the ``i``-th circuit and ``i``-th observable. - -.. testcode:: - - exp_value = result.values[0] - print(exp_value) - -.. testoutput:: - - 3.999999999999999 - -Parameterized circuit with ``Estimator`` -======================================== - -The :class:`~qiskit.primitives.Estimator` primitive can be run with unbound parameterized circuits like the one below. -You can also manually bind values to the parameters of the circuit and follow the steps -of the previous example. - -.. testcode:: - - from qiskit.circuit import Parameter - - theta = Parameter('θ') - param_qc = QuantumCircuit(2) - param_qc.ry(theta, 0) - param_qc.cx(0,1) - print(param_qc.draw()) - -.. testoutput:: - - ┌───────┐ - q_0: ┤ Ry(θ) ├──■── - └───────┘┌─┴─┐ - q_1: ─────────┤ X ├ - └───┘ - -The main difference with the previous case is that now you need to specify the sets of parameter values -for which you want to evaluate the expectation value as a ``list`` of ``list``\ s of ``float``\ s. -The ``i``-th element of the outer``list`` is the set of parameter values -that corresponds to the ``i``-th circuit and observable. - -.. testcode:: - - import numpy as np - - parameter_values = [[0], [np.pi/6], [np.pi/2]] - - job = estimator.run([param_qc]*3, [observable]*3, parameter_values=parameter_values) - values = job.result().values - - for i in range(3): - print(f"Parameter: {parameter_values[i][0]:.5f}\t Expectation value: {values[i]}") - -.. testoutput:: - - Parameter: 0.00000 Expectation value: 2.0 - Parameter: 0.52360 Expectation value: 3.0 - Parameter: 1.57080 Expectation value: 4.0 - -Change run options -================== - -Your workflow might require tuning primitive run options, such as the amount of shots. - -By default, the reference :class:`~qiskit.primitives.Estimator` class performs an exact statevector -calculation based on the :class:`~qiskit.quantum_info.Statevector` class. However, this can be -modified to include shot noise if the number of ``shots`` is set. -For reproducibility purposes, a ``seed`` will also be set in the following examples. - -There are two main ways of setting options in the :class:`~qiskit.primitives.Estimator`: - -* Set keyword arguments in the :meth:`~qiskit.primitives.Estimator.run` method. -* Modify :class:`~qiskit.primitives.Estimator` options. - -Set keyword arguments for :meth:`~qiskit.primitives.Estimator.run` ------------------------------------------------------------------- - -If you only want to change the settings for a specific run, it can be more convenient to -set the options inside the :meth:`~qiskit.primitives.Estimator.run` method. You can do this by -passing them as keyword arguments. - -.. testcode:: - - job = estimator.run(qc, observable, shots=2048, seed=123) - result = job.result() - print(result) - -.. testoutput:: - - EstimatorResult(values=array([4.]), metadata=[{'variance': 3.552713678800501e-15, 'shots': 2048}]) - -.. testcode:: - - print(result.values[0]) - -.. testoutput:: - - 3.999999998697238 - -Modify :class:`~qiskit.primitives.Estimator` options ------------------------------------------------------ - -If you want to keep some configuration values for several runs, it can be better to -change the :class:`~qiskit.primitives.Estimator` options. That way you can use the same -:class:`~qiskit.primitives.Estimator` object as many times as you wish without having to -rewrite the configuration values every time you use :meth:`~qiskit.primitives.Estimator.run`. - -Modify existing :class:`~qiskit.primitives.Estimator` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you prefer to change the options of an already-defined :class:`~qiskit.primitives.Estimator`, you can use -:meth:`~qiskit.primitives.Estimator.set_options` and introduce the new options as keyword arguments. - -.. testcode:: - - estimator.set_options(shots=2048, seed=123) - - job = estimator.run(qc, observable) - result = job.result() - print(result) - -.. testoutput:: - - EstimatorResult(values=array([4.]), metadata=[{'variance': 3.552713678800501e-15, 'shots': 2048}]) - -.. testcode:: - - print(result.values[0]) - -.. testoutput:: - - 3.999999998697238 - - -Define a new :class:`~qiskit.primitives.Estimator` with the options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you prefer to define a new :class:`~qiskit.primitives.Estimator` with new options, you need to -define a ``dict`` like this one: - -.. testcode:: - - options = {"shots": 2048, "seed": 123} - -And then you can introduce it into your new :class:`~qiskit.primitives.Estimator` with the -``options`` argument. - -.. testcode:: - - estimator = Estimator(options=options) - - job = estimator.run(qc, observable) - result = job.result() - print(result) - -.. testoutput:: - - EstimatorResult(values=array([4.]), metadata=[{'variance': 3.552713678800501e-15, 'shots': 2048}]) - -.. testcode:: - - print(result.values[0]) - -.. testoutput:: - - 3.999999998697238 \ No newline at end of file diff --git a/docs/how_to/use_sampler.rst b/docs/how_to/use_sampler.rst deleted file mode 100644 index da7257c28fa1..000000000000 --- a/docs/how_to/use_sampler.rst +++ /dev/null @@ -1,255 +0,0 @@ -############################################################### -Compute circuit output probabilities with ``Sampler`` primitive -############################################################### - -This guide shows how to get the probability distribution of a quantum circuit with the :class:`~qiskit.primitives.Sampler` primitive. - -.. note:: - - While this guide uses Qiskit’s reference implementation, the ``Sampler`` primitive can be run with any provider using :class:`~qiskit.primitives.BackendSampler`. - - .. code-block:: - - from qiskit.primitives import BackendSampler - from import QiskitProvider - - provider = QiskitProvider() - backend = provider.get_backend('backend_name') - sampler = BackendSampler(backend) - - There are some providers that implement primitives natively (see `this page `_ for more details). - -Initialize quantum circuits -=========================== - -The first step is to create the :class:`~qiskit.circuit.QuantumCircuit`\ s from which you want to obtain the probability distribution. - -.. plot:: - :include-source: - - from qiskit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0,1) - qc.measure_all() - qc.draw("mpl") - -.. testsetup:: - - # This code is repeated (but hidden) because we will need to use the variables with the extension sphinx.ext.doctest (testsetup/testcode/testoutput directives) - # and we can't reuse the variables from the plot directive above because they are incompatible. - # The plot directive is used to draw the circuit with matplotlib and the code is shown because of the include-source flag. - - from qiskit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0,1) - qc.measure_all() - -.. note:: - - The :class:`~qiskit.circuit.QuantumCircuit` you pass to :class:`~qiskit.primitives.Sampler` has to include measurements. - -Initialize the ``Sampler`` -========================== - -Then, you need to create a :class:`~qiskit.primitives.Sampler` instance. - -.. testcode:: - - from qiskit.primitives import Sampler - - sampler = Sampler() - -Run and get results -=================== - -Now that you have defined your ``sampler``, you can run it by calling the :meth:`~qiskit.primitives.Sampler.run` method, -which returns an instance of :class:`~.PrimitiveJob` (subclass of :class:`~qiskit.providers.JobV1`). You can get the results from the job (as a :class:`~qiskit.primitives.SamplerResult` object) -with the :meth:`~qiskit.providers.JobV1.result` method. - -.. testcode:: - - job = sampler.run(qc) - result = job.result() - print(result) - -.. testoutput:: - - SamplerResult(quasi_dists=[{0: 0.4999999999999999, 3: 0.4999999999999999}], metadata=[{}]) - -While this example only uses one :class:`~qiskit.circuit.QuantumCircuit`, if you want to sample multiple circuits you can -pass a ``list`` of :class:`~qiskit.circuit.QuantumCircuit` instances to the :meth:`~qiskit.primitives.Sampler.run` method. - -Get the probability distribution --------------------------------- - -From these results you can extract the quasi-probability distributions with the attribute :attr:`~qiskit.primitives.SamplerResult.quasi_dists`. - -Even though there is only one circuit in this example, :attr:`~qiskit.primitives.SamplerResult.quasi_dists` returns a list of :class:`~qiskit.result.QuasiDistribution`\ s. -``result.quasi_dists[i]`` is the quasi-probability distribution of the ``i``-th circuit. - -.. note:: - - A quasi-probability distribution differs from a probability distribution in that negative values are also allowed. - However the quasi-probabilities must sum up to 1 like probabilities. - Negative quasi-probabilities may appear when using error mitigation techniques. - -.. testcode:: - - quasi_dist = result.quasi_dists[0] - print(quasi_dist) - -.. testoutput:: - - {0: 0.4999999999999999, 3: 0.4999999999999999} - -Probability distribution with binary outputs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you prefer to see the output keys as binary strings instead of decimal numbers, you can use the -:meth:`~qiskit.result.QuasiDistribution.binary_probabilities` method. - -.. testcode:: - - print(quasi_dist.binary_probabilities()) - -.. testoutput:: - - {'00': 0.4999999999999999, '11': 0.4999999999999999} - -Parameterized circuit with ``Sampler`` -======================================== - -The :class:`~qiskit.primitives.Sampler` primitive can be run with unbound parameterized circuits like the one below. -You can also manually bind values to the parameters of the circuit and follow the steps -of the previous example. - -.. testcode:: - - from qiskit.circuit import Parameter - - theta = Parameter('θ') - param_qc = QuantumCircuit(2) - param_qc.ry(theta, 0) - param_qc.cx(0,1) - param_qc.measure_all() - print(param_qc.draw()) - -.. testoutput:: - - ┌───────┐ ░ ┌─┐ - q_0: ┤ Ry(θ) ├──■───░─┤M├─── - └───────┘┌─┴─┐ ░ └╥┘┌─┐ - q_1: ─────────┤ X ├─░──╫─┤M├ - └───┘ ░ ║ └╥┘ - meas: 2/══════════════════╩══╩═ - 0 1 - -The main difference from the previous case is that now you need to specify the sets of parameter values -for which you want to evaluate the expectation value as a ``list`` of ``list``\ s of ``float``\ s. -The ``i``-th element of the outer ``list`` is the set of parameter values -that corresponds to the ``i``-th circuit. - -.. testcode:: - - import numpy as np - - parameter_values = [[0], [np.pi/6], [np.pi/2]] - - job = sampler.run([param_qc]*3, parameter_values=parameter_values) - dists = job.result().quasi_dists - - for i in range(3): - print(f"Parameter: {parameter_values[i][0]:.5f}\t Probabilities: {dists[i]}") - -.. testoutput:: - - Parameter: 0.00000 Probabilities: {0: 1.0} - Parameter: 0.52360 Probabilities: {0: 0.9330127018922194, 3: 0.0669872981077807} - Parameter: 1.57080 Probabilities: {0: 0.5000000000000001, 3: 0.4999999999999999} - -Change run options -================== - -Your workflow might require tuning primitive run options, such as the amount of shots. - -By default, the reference :class:`~qiskit.primitives.Sampler` class performs an exact statevector -calculation based on the :class:`~qiskit.quantum_info.Statevector` class. However, this can be -modified to include shot noise if the number of ``shots`` is set. -For reproducibility purposes, a ``seed`` will also be set in the following examples. - -There are two main ways of setting options in the :class:`~qiskit.primitives.Sampler`: - -* Set keyword arguments in the :meth:`~qiskit.primitives.Sampler.run` method. -* Modify :class:`~qiskit.primitives.Sampler` options. - -Set keyword arguments for :meth:`~qiskit.primitives.Sampler.run` ----------------------------------------------------------------- - -If you only want to change the settings for a specific run, it can be more convenient to -set the options inside the :meth:`~qiskit.primitives.Sampler.run` method. You can do this by -passing them as keyword arguments. - -.. testcode:: - - job = sampler.run(qc, shots=2048, seed=123) - result = job.result() - print(result) - -.. testoutput:: - - SamplerResult(quasi_dists=[{0: 0.5205078125, 3: 0.4794921875}], metadata=[{'shots': 2048}]) - -Modify :class:`~qiskit.primitives.Sampler` options ---------------------------------------------------- - -If you want to keep some configuration values for several runs, it can be better to -change the :class:`~qiskit.primitives.Sampler` options. That way you can use the same -:class:`~qiskit.primitives.Sampler` object as many times as you wish without having to -rewrite the configuration values every time you use :meth:`~qiskit.primitives.Sampler.run`. - -Modify existing :class:`~qiskit.primitives.Sampler` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you prefer to change the options of an already-defined :class:`~qiskit.primitives.Sampler`, you can use -:meth:`~qiskit.primitives.Sampler.set_options` and introduce the new options as keyword arguments. - -.. testcode:: - - sampler.set_options(shots=2048, seed=123) - - job = sampler.run(qc) - result = job.result() - print(result) - -.. testoutput:: - - SamplerResult(quasi_dists=[{0: 0.5205078125, 3: 0.4794921875}], metadata=[{'shots': 2048}]) - -Define a new :class:`~qiskit.primitives.Sampler` with the options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you prefer to define a new :class:`~qiskit.primitives.Sampler` with new options, you need to -define a ``dict`` like this one: - -.. testcode:: - - options = {"shots": 2048, "seed": 123} - -And then you can introduce it into your new :class:`~qiskit.primitives.Sampler` with the -``options`` argument. - -.. testcode:: - - sampler = Sampler(options=options) - - job = sampler.run(qc) - result = job.result() - print(result) - -.. testoutput:: - - SamplerResult(quasi_dists=[{0: 0.5205078125, 3: 0.4794921875}], metadata=[{'shots': 2048}]) \ No newline at end of file diff --git a/docs/images/favicon.ico b/docs/images/favicon.ico deleted file mode 100644 index aa99dc043130..000000000000 Binary files a/docs/images/favicon.ico and /dev/null differ diff --git a/docs/images/logo.png b/docs/images/logo.png deleted file mode 100644 index e165b1fa91e9..000000000000 Binary files a/docs/images/logo.png and /dev/null differ diff --git a/docs/images/noise_cancel.png b/docs/images/noise_cancel.png deleted file mode 100644 index f792fbe31901..000000000000 Binary files a/docs/images/noise_cancel.png and /dev/null differ diff --git a/docs/images/qiskit_nutshell.png b/docs/images/qiskit_nutshell.png deleted file mode 100644 index 7bedc088257a..000000000000 Binary files a/docs/images/qiskit_nutshell.png and /dev/null differ diff --git a/docs/images/quantum_interference.png b/docs/images/quantum_interference.png deleted file mode 100644 index 3e3b00be2e58..000000000000 Binary files a/docs/images/quantum_interference.png and /dev/null differ diff --git a/docs/images/system_error.png b/docs/images/system_error.png deleted file mode 100644 index 42ce76b96f11..000000000000 Binary files a/docs/images/system_error.png and /dev/null differ diff --git a/docs/images/system_one.jpeg b/docs/images/system_one.jpeg deleted file mode 100644 index 4880d0839b76..000000000000 Binary files a/docs/images/system_one.jpeg and /dev/null differ diff --git a/docs/images/teleportation_detailed.png b/docs/images/teleportation_detailed.png deleted file mode 100644 index 59eb9bfac9a5..000000000000 Binary files a/docs/images/teleportation_detailed.png and /dev/null differ diff --git a/docs/index.rst b/docs/index.rst index 55ae6cd3b7a5..5105a1b6d7a9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,88 +1,15 @@ -############################## -Qiskit |version| documentation -############################## +################################# +Qiskit |version| API docs preview +################################# -Qiskit is open-source software for working with quantum computers -at the level of circuits, pulses, and algorithms. - -The central goal of Qiskit is to build a software stack -that makes it easy for anyone to use quantum computers, regardless of their skill level or -area of interest; Qiskit allows one to easily design experiments and applications and run -them on real quantum computers or classical simulators. Qiskit is already in use -around the world by beginners, hobbyists, educators, researchers, and commercial companies. - -.. qiskit-card:: - :header: Starting on December 1, 2023, Qiskit Documentation will only live on IBM Quantum. Learn more -> - :card_description: We are reorganizing Qiskit documentation on IBM Quantum to better support your research and development workflows. - :image: _static/images/1xp.png - :link: https://medium.com/qiskit/important-changes-to-qiskit-documentation-and-learning-resources-7f4e346b19ab - -.. qiskit-call-to-action-grid:: - - .. qiskit-call-to-action-item:: - :header: Access to quantum systems - :description: Find out which Qiskit providers support execution on real quantum services. - :button_link: https://qiskit.org/providers - :button_text: Quantum providers - - .. qiskit-call-to-action-item:: - :header: Qiskit ecosystem - :description: The Qiskit ecosystem consists of projects, tools, utilities, libraries and tutorials from a broad community of developers and researchers. - :button_link: https://qiskit.org/ecosystem/ - :button_text: Explore the Qiskit ecosystem - - -Main Qiskit-related projects -############################ - -.. qiskit-call-to-action-grid:: - - .. qiskit-call-to-action-item:: - :header: Qiskit Experiments - :description: Run characterization, calibration, and verification experiments - :button_link: https://qiskit.org/ecosystem/experiments/ - :button_text: Qiskit Experiments documentation - - .. qiskit-call-to-action-item:: - :header: Qiskit Dynamics - :description: Tools for building and solving models of quantum systems in Qiskit - :button_link: https://qiskit.org/ecosystem/dynamics/ - :button_text: Qiskit Dynamics documentation - - .. qiskit-call-to-action-item:: - :header: Qiskit IBM Runtime - :description: Qiskit Runtime is a cloud base implementation of the Qiskit primitives to effectively execute workloads on IBM Quantum systems. - :button_link: https://docs.quantum-computing.ibm.com/api/qiskit-ibm-runtime/runtime_service - :button_text: Qiskit Runtime documentation - - .. qiskit-call-to-action-item:: - :header: IBM Quantum Provider - :description: A Qiskit provider that allows accessing the IBM Quantum systems and cloud simulators. - :button_link: https://docs.quantum-computing.ibm.com/api/qiskit-ibm-provider/ibm_provider - :button_text: Qiskit IBM provider documentation +Qiskit docs live at docs.quantum.ibm.com and come from https://github.com/Qiskit/documentation. +This site is only used to generate our API docs, which then get migrated to +https://github.com/Qiskit/documentation. .. toctree:: :hidden: Documentation Home - qc_intro - getting_started - intro_tutorial1 - tutorials API Reference - How-to Guides - Explanation - Migration Guides Release Notes - configuration - GitHub - faq - -.. toctree:: - :caption: Contributing - :hidden: - - contributing_to_qiskit - deprecation_policy - maintainers_guide diff --git a/docs/intro_tutorial1.rst b/docs/intro_tutorial1.rst deleted file mode 100644 index c2fa5206a6d9..000000000000 --- a/docs/intro_tutorial1.rst +++ /dev/null @@ -1,262 +0,0 @@ -====================== -Introduction to Qiskit -====================== - -When using Qiskit a user workflow nominally consists of -following four high-level steps: - -- **Build**: Design a quantum circuit(s) that represents the problem you are - considering. -- **Compile**: Compile circuits for a specific quantum service, e.g. a quantum - system or classical simulator. -- **Run**: Run the compiled circuits on the specified quantum service(s). These - services can be cloud-based or local. -- **Analyze**: Compute summary statistics and visualize the results of the - experiments. - -Here is an example of the entire workflow, with each step explained in detail in -subsequent sections: - -.. plot:: - :include-source: - :context: - - from qiskit import QuantumCircuit, transpile - from qiskit_aer import AerSimulator - from qiskit.visualization import plot_histogram - - # Use Aer's AerSimulator - simulator = AerSimulator() - - # Create a Quantum Circuit acting on the q register - circuit = QuantumCircuit(2, 2) - - # Add a H gate on qubit 0 - circuit.h(0) - - # Add a CX (CNOT) gate on control qubit 0 and target qubit 1 - circuit.cx(0, 1) - - # Map the quantum measurement to the classical bits - circuit.measure([0, 1], [0, 1]) - - # Compile the circuit for the support instruction set (basis_gates) - # and topology (coupling_map) of the backend - compiled_circuit = transpile(circuit, simulator) - - # Execute the circuit on the aer simulator - job = simulator.run(compiled_circuit, shots=1000) - - # Grab results from the job - result = job.result() - - # Returns counts - counts = result.get_counts(compiled_circuit) - print("\nTotal count for 00 and 11 are:", counts) - - # Draw the circuit - circuit.draw("mpl") - -.. plot:: - :include-source: - :context: close-figs - - # Plot a histogram - plot_histogram(counts) - - - ------------------------ -Workflow Step--by--Step ------------------------ - -The program above can be broken down into six steps: - -1. Import packages -2. Initialize variables -3. Add gates -4. Visualize the circuit -5. Simulate the experiment -6. Visualize the results - - -~~~~~~~~~~~~~~~~~~~~~~~~ -Step 1 : Import Packages -~~~~~~~~~~~~~~~~~~~~~~~~ - -The basic elements needed for your program are imported as follows: - -.. code-block:: python - - from qiskit import QuantumCircuit - from qiskit_aer import AerSimulator - from qiskit.visualization import plot_histogram - -In more detail, the imports are - -- ``QuantumCircuit``: can be thought as the instructions of the quantum system. - It holds all your quantum operations. -- ``AerSimulator``: is the Aer high performance circuit simulator. -- ``plot_histogram``: creates histograms. - - - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Step 2 : Initialize Variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Consider the next line of code - -.. code-block:: python - - circuit = QuantumCircuit(2, 2) - -Here, you are initializing with 2 qubits in the zero state; with 2 -classical bits set to zero; and ``circuit`` is the quantum circuit. - -Syntax: - -- ``QuantumCircuit(int, int)`` - - - -~~~~~~~~~~~~~~~~~~ -Step 3 : Add Gates -~~~~~~~~~~~~~~~~~~ - -You can add gates (operations) to manipulate the registers of your circuit. - -Consider the following three lines of code: - -.. code-block:: python - - circuit.h(0) - circuit.cx(0, 1) - circuit.measure([0, 1], [0, 1]) - -The gates are added to the circuit one-by-one to form the Bell state - -.. math:: \lvert\psi\rangle = \left(\lvert00\rangle+\lvert11\rangle\right)/\sqrt{2}. - -The code above applies the following gates: - -- ``QuantumCircuit.h(0)``: A Hadamard gate :math:`H` on qubit 0, - which puts it into a **superposition state**. -- ``QuantumCircuit.cx(0, 1)``: A controlled-Not operation - (:math:`CNOT`) on control qubit 0 and target qubit 1, putting the qubits in - an **entangled state**. -- ``QuantumCircuit.measure([0,1], [0,1])``: if you pass - the entire quantum and classical registers to ``measure``, the ith qubit’s - measurement result will be stored in the ith classical bit. - - - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Step 4 : Visualize the Circuit -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can use :meth:`qiskit.circuit.QuantumCircuit.draw` to view the circuit that you have designed -in the various forms used in many textbooks and research articles. - -.. plot:: - :include-source: - :context: close-figs - - circuit.draw("mpl") - -In this circuit, the qubits are ordered with qubit zero at the top and -qubit one at the bottom. The circuit is read left-to-right, meaning that gates -which are applied earlier in the circuit show up farther to the left. - -The default backend for ``QuantumCircuit.draw()`` or ``qiskit.visualization.circuit_drawer()`` -is the text backend. However, depending on your local environment you may want to change -these defaults to something better suited for your use case. This is done with the user -config file. By default the user config file should be located in -``~/.qiskit/settings.conf`` and is a ``.ini`` file. - -For example, a ``settings.conf`` file for setting a Matplotlib drawer is: - -.. code-block:: text - - [default] - circuit_drawer = mpl - -You can use any of the valid circuit drawer backends as the value for this config, this includes -text, mpl, latex, and latex_source. - - - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Step 5 : Simulate the Experiment -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`Qiskit Aer `_ is a high performance -simulator framework for quantum circuits. It provides several backends to -achieve different simulation goals. - -If you have issues installing Aer, you can alternatively use the Basic Aer -provider by replacing `Aer` with `BasicAer`. Basic Aer is included in Qiskit. - -.. code-block:: python - - from qiskit import QuantumCircuit, transpile - from qiskit.providers.basicaer import QasmSimulatorPy - ... - -To simulate this circuit, you will use the ``AerSimulator``. Each run of this -circuit will yield either the bit string 00 or 11. - -.. plot:: - :include-source: - :context: close-figs - - simulator = AerSimulator() - compiled_circuit = transpile(circuit, simulator) - job = simulator.run(compiled_circuit, shots=1000) - result = job.result() - counts = result.get_counts(circuit) - print("\nTotal count for 00 and 11 are:",counts) - -As expected, the output bit string is 00 approximately 50 percent of the time. -The number of times the circuit is run can be specified via the ``shots`` -argument of the ``execute`` method. The number of shots of the simulation was -set to be 1000 (the default is 1024). - -Once you have a ``result`` object, you can access the counts via the method -``get_counts(circuit)``. This gives you the aggregate outcomes of the -experiment you ran. - - - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Step 6 : Visualize the Results -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Qiskit provides `many visualizations `__, - -including the function ``plot_histogram``, to view your results. - -.. plot:: - :include-source: - :context: close-figs - - plot_histogram(counts) - -The observed probabilities :math:`Pr(00)` and :math:`Pr(11)` are computed by -taking the respective counts and dividing by the total number of shots. - -.. note:: - - Try changing the ``shots`` keyword in the ``run()`` method to see how - the estimated probabilities change. - - ----------- -Next Steps ----------- - -Now that you have learnt the basics, consider these learning resources: - -- :ref:`Qiskit tutorials ` -- `Textbook: Learn Quantum Computing using Qiskit `_ -- `Video series: Coding with Qiskit `_ diff --git a/docs/legacy_release_notes.rst b/docs/legacy_release_notes.rst deleted file mode 100644 index 13b557bcfef1..000000000000 --- a/docs/legacy_release_notes.rst +++ /dev/null @@ -1,33335 +0,0 @@ -:orphan: - -.. _legacy-release-notes: - -%%%%%%%%%%%%%%%%%%%% -Legacy Release Notes -%%%%%%%%%%%%%%%%%%%% - -This page contains the full history of the release notes from when ``qiskit`` was a "meta-package", -which contained several different "elements". It is maintained here for historical interest. The -main release notes can be found at :ref:`release-notes`. - -What is called "Qiskit Terra" within this document is principally what is now just called "Qiskit". - -############### -Version History -############### - -This table tracks the meta-package versions and the version of each legacy Qiskit element installed: - -========================== ============ ========== ============ ==================== =========== ============ -Qiskit Metapackage Version qiskit-terra qiskit-aer qiskit-ignis qiskit-ibmq-provider qiskit-aqua Release Date -========================== ============ ========== ============ ==================== =========== ============ -0.44.1 0.25.1 2023-08-17 -0.44.0 0.25.0 2023-07-27 -0.43.3 0.24.2 0.12.2 0.20.2 2023-07-19 -0.43.2 0.24.1 0.12.1 0.20.2 2023-06-28 -0.43.1 0.24.1 0.12.0 0.20.2 2023-06-02 -0.43.0 0.24.0 0.12.0 0.20.2 2023-05-04 -0.42.1 0.23.3 0.12.0 0.20.2 2023-03-21 -0.42.0 0.23.2 0.12.0 0.20.2 2023-03-10 -0.41.1 0.23.2 0.11.2 0.20.1 2023-02-23 -0.41.0 0.23.1 0.11.2 0.20.0 2023-01-31 -0.40.0 0.23.0 0.11.2 0.19.2 2023-01-26 -0.39.5 0.22.4 0.11.2 0.19.2 2023-01-17 -0.39.4 0.22.3 0.11.2 0.19.2 2022-12-08 -0.39.3 0.22.3 0.11.1 0.19.2 2022-11-25 -0.39.2 0.22.2 0.11.1 0.19.2 2022-11-03 -0.39.1 0.22.1 0.11.1 0.19.2 2022-11-02 -0.39.0 0.22.0 0.11.0 0.19.2 2022-10-13 -0.38.0 0.21.2 0.11.0 0.19.2 2022-09-14 -0.37.2 0.21.2 0.10.4 0.19.2 2022-08-23 -0.37.1 0.21.1 0.10.4 0.19.2 2022-07-28 -0.37.0 0.21.0 0.10.4 0.19.2 2022-06-30 -0.36.2 0.20.2 0.10.4 0.7.1 0.19.1 2022-05-18 -0.36.1 0.20.1 0.10.4 0.7.0 0.19.1 2022-04-21 -0.36.0 0.20.0 0.10.4 0.7.0 0.19.0 2022-04-06 -0.35.0 0.20.0 0.10.3 0.7.0 0.18.3 2022-03-31 -0.34.2 0.19.2 0.10.3 0.7.0 0.18.3 2022-02-09 -0.34.1 0.19.1 0.10.2 0.7.0 0.18.3 2022-01-05 -0.34.0 0.19.1 0.10.1 0.7.0 0.18.3 2021-12-20 -0.33.1 0.19.1 0.9.1 0.7.0 0.18.2 2021-12-10 -0.33.0 0.19.0 0.9.1 0.7.0 0.18.1 2021-12-06 -0.32.1 0.18.3 0.9.1 0.6.0 0.18.1 0.9.5 2021-11-22 -0.32.0 0.18.3 0.9.1 0.6.0 0.18.0 0.9.5 2021-11-10 -0.31.0 0.18.3 0.9.1 0.6.0 0.17.0 0.9.5 2021-10-12 -0.30.1 0.18.3 0.9.0 0.6.0 0.16.0 0.9.5 2021-09-29 -0.30.0 0.18.2 0.9.0 0.6.0 0.16.0 0.9.5 2021-09-16 -0.29.1 0.18.2 0.8.2 0.6.0 0.16.0 0.9.5 2021-09-10 -0.29.0 0.18.1 0.8.2 0.6.0 0.16.0 0.9.4 2021-08-02 -0.28.0 0.18.0 0.8.2 0.6.0 0.15.0 0.9.4 2021-07-13 -0.27.0 0.17.4 0.8.2 0.6.0 0.14.0 0.9.2 2021-06-15 -0.26.2 0.17.4 0.8.2 0.6.0 0.13.1 0.9.1 2021-05-19 -0.26.1 0.17.4 0.8.2 0.6.0 0.13.1 0.9.1 2021-05-18 -0.26.0 0.17.3 0.8.2 0.6.0 0.13.1 0.9.1 2021-05-11 -0.25.4 0.17.2 0.8.2 0.6.0 0.12.3 0.9.1 2021-05-05 -0.25.3 0.17.1 0.8.2 0.6.0 0.12.3 0.9.1 2021-04-29 -0.25.2 0.17.1 0.8.1 0.6.0 0.12.3 0.9.1 2021-04-21 -0.25.1 0.17.1 0.8.1 0.6.0 0.12.2 0.9.1 2021-04-15 -0.25.0 0.17.0 0.8.0 0.6.0 0.12.2 0.9.0 2021-04-02 -0.24.1 0.16.4 0.7.6 0.5.2 0.12.2 0.8.2 2021-03-24 -0.24.0 0.16.4 0.7.6 0.5.2 0.12.1 0.8.2 2021-03-04 -0.23.6 0.16.4 0.7.5 0.5.2 0.11.1 0.8.2 2021-02-18 -0.23.5 0.16.4 0.7.4 0.5.2 0.11.1 0.8.2 2021-02-08 -0.23.4 0.16.3 0.7.3 0.5.1 0.11.1 0.8.1 2021-01-28 -0.23.3 0.16.2 0.7.3 0.5.1 0.11.1 0.8.1 2021-01-26 -0.23.2 0.16.1 0.7.2 0.5.1 0.11.1 0.8.1 2020-12-15 -0.23.1 0.16.1 0.7.1 0.5.1 0.11.1 0.8.1 2020-11-12 -0.23.0 0.16.0 0.7.0 0.5.0 0.11.0 0.8.0 2020-10-16 -0.22.0 0.15.2 0.6.1 0.4.0 0.10.0 0.7.5 2020-10-05 -0.21.0 0.15.2 0.6.1 0.4.0 0.9.0 0.7.5 2020-09-16 -0.20.1 0.15.2 0.6.1 0.4.0 0.8.0 0.7.5 2020-09-08 -0.20.0 0.15.1 0.6.1 0.4.0 0.8.0 0.7.5 2020-08-10 -0.19.6 0.14.2 0.5.2 0.3.3 0.7.2 0.7.3 2020-06-25 -0.19.5 0.14.2 0.5.2 0.3.2 0.7.2 0.7.3 2020-06-19 -0.19.4 0.14.2 0.5.2 0.3.0 0.7.2 0.7.2 2020-06-16 -0.19.3 0.14.1 0.5.2 0.3.0 0.7.2 0.7.1 2020-06-02 -0.19.2 0.14.1 0.5.1 0.3.0 0.7.1 0.7.1 2020-05-14 -0.19.1 0.14.1 0.5.1 0.3.0 0.7.0 0.7.0 2020-05-01 -0.19.0 0.14.0 0.5.1 0.3.0 0.7.0 0.7.0 2020-04-30 -0.18.3 0.13.0 0.5.1 0.3.0 0.6.1 0.6.6 2020-04-24 -0.18.2 0.13.0 0.5.0 0.3.0 0.6.1 0.6.6 2020-04-23 -0.18.1 0.13.0 0.5.0 0.3.0 0.6.0 0.6.6 2020-04-20 -0.18.0 0.13.0 0.5.0 0.3.0 0.6.0 0.6.5 2020-04-09 -0.17.0 0.12.0 0.4.1 0.2.0 0.6.0 0.6.5 2020-04-01 -0.16.2 0.12.0 0.4.1 0.2.0 0.5.0 0.6.5 2020-03-20 -0.16.1 0.12.0 0.4.1 0.2.0 0.5.0 0.6.4 2020-03-05 -0.16.0 0.12.0 0.4.0 0.2.0 0.5.0 0.6.4 2020-02-27 -0.15.0 0.12.0 0.4.0 0.2.0 0.4.6 0.6.4 2020-02-06 -0.14.1 0.11.1 0.3.4 0.2.0 0.4.5 0.6.2 2020-01-07 -0.14.0 0.11.0 0.3.4 0.2.0 0.4.4 0.6.1 2019-12-10 -0.13.0 0.10.0 0.3.2 0.2.0 0.3.3 0.6.1 2019-10-17 -0.12.2 0.9.1 0.3.0 0.2.0 0.3.3 0.6.0 2019-10-11 -0.12.1 0.9.0 0.3.0 0.2.0 0.3.3 0.6.0 2019-09-30 -0.12.0 0.9.0 0.3.0 0.2.0 0.3.2 0.6.0 2019-08-22 -0.11.2 0.8.2 0.2.3 0.1.1 0.3.2 0.5.5 2019-08-20 -0.11.1 0.8.2 0.2.3 0.1.1 0.3.1 0.5.3 2019-07-24 -0.11.0 0.8.2 0.2.3 0.1.1 0.3.0 0.5.2 2019-07-15 -0.10.5 0.8.2 0.2.1 0.1.1 0.2.2 0.5.2 2019-06-27 -0.10.4 0.8.2 0.2.1 0.1.1 0.2.2 0.5.1 2019-06-17 -0.10.3 0.8.1 0.2.1 0.1.1 0.2.2 0.5.1 2019-05-29 -0.10.2 0.8.0 0.2.1 0.1.1 0.2.2 0.5.1 2019-05-24 -0.10.1 0.8.0 0.2.0 0.1.1 0.2.2 0.5.0 2019-05-07 -0.10.0 0.8.0 0.2.0 0.1.1 0.2.1 0.5.0 2019-05-06 -0.9.0 0.8.0 0.2.0 0.1.1 0.1.1 0.5.0 2019-05-02 -0.8.1 0.7.2 0.1.1 0.1.0 2019-05-01 -0.8.0 0.7.1 0.1.1 0.1.0 2019-03-05 -0.7.3 >=0.7,<0.8 >=0.1,<0.2 2019-02-19 -0.7.2 >=0.7,<0.8 >=0.1,<0.2 2019-01-22 -0.7.1 >=0.7,<0.8 >=0.1,<0.2 2019-01-17 -0.7.0 >=0.7,<0.8 >=0.1,<0.2 2018-12-14 -========================== ============ ========== ============ ==================== =========== ============ - -.. note:: - - For the ``0.7.0``, ``0.7.1``, and ``0.7.2`` meta-package releases the - meta-package versioning strategy was not formalized yet. - -############# -Qiskit 0.44.1 -############# - -.. _Release Notes_0.25.1: - -Terra 0.25.1 -============ - -.. _Release Notes_0.25.1_Prelude: - -Prelude -------- - -Qiskit Terra 0.25.1 is a bugfix release, addressing some issues identified since the 0.25.1 release. - -.. _Release Notes_0.25.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix-qpy-nested-custom-controlled-2a23dfe828bc46c8.yaml @ b'3ba0b74b89d206b99d09fdec2b833f13394b4a36' - -- Fixed a bug in QPY serialization (:mod:`qiskit.qpy`) where multiple controlled custom gates in - a circuit could result in an invalid QPY file that could not be parsed. Fixed `#9746 - `__. - -.. releasenotes/notes/fix_9363-445db8fde1244e57.yaml @ b'c1ee9744e1be10ca2e78958fb91308777a668b44' - -- Fixed `#9363 `__. - by labeling the non-registerless synthesis in the order that Tweedledum - returns. For example, compare this example before and after the fix:: - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.classicalfunction import BooleanExpression - - boolean_exp = BooleanExpression.from_dimacs_file("simple_v3_c2.cnf") - circuit = QuantumCircuit(boolean_exp.num_qubits) - circuit.append(boolean_exp, range(boolean_exp.num_qubits)) - circuit.draw("text") - - from qiskit.circuit.classicalfunction import classical_function - from qiskit.circuit.classicalfunction.types import Int1 - - @classical_function - def grover_oracle(a: Int1, b: Int1, c: Int1) -> Int1: - return (a and b and not c) - - quantum_circuit = grover_oracle.synth(registerless=False) - print(quantum_circuit.draw()) - - Which would print - - .. parsed-literal:: - - Before After - - c: ──■── a: ──■── - │ │ - b: ──■── b: ──■── - │ │ - a: ──o── c: ──o── - ┌─┴─┐ ┌─┴─┐ - return: ┤ X ├ return: ┤ X ├ - └───┘ └───┘ - -.. releasenotes/notes/paulivecplot-normalization-5dd3cf3393c75afb.yaml @ b'91ca2c408b4c4f1d02060c859dbca2f6d6a8bfe8' - -- Fixed :func:`plot_state_paulivec`, which previously damped the state coefficients by a factor of - :math:`2^n`, where :math:`n` is the number of qubits. Now the bar graph correctly displays - the coefficients as :math:`\mathrm{Tr}(\sigma\rho)`, where :math:`\rho` is the state to - be plotted and :math:`\sigma` iterates over all possible tensor products of single-qubit Paulis. - -.. releasenotes/notes/qasm2-float-decimal-76b44281d9249f7a.yaml @ b'f42e881cff435fffb83c65eb442924e2aec17aab' - -- Angles in the OpenQASM 2 exporter (:func:`.QuantumCircuit.qasm`) will now always include a - decimal point, for example in the case of ``1.e-5``. This is required by a strict interpretation of the - floating-point-literal specification in OpenQASM 2. Qiskit's OpenQASM 2 parser - (:func:`.qasm2.load` and :func:`~.qasm2.loads`) is more permissive by default, and will allow - ``1e-5`` without the decimal point unless in ``strict`` mode. - -.. releasenotes/notes/sparse-pauli-op-constraint-pauli-setter-52f6f89627d1937c.yaml @ b'48a7b821e00fd61f94a0ac878cb3a7b41eb4a8dc' - -- The setter for :attr:`.SparsePauliOp.paulis` will now correctly reject attempts to set the - attribute with incorrectly shaped data, rather than silently allowing an invalid object to be - created. See `#10384 `__. - -- Fixed a performance regression in the :class:`~.SabreLayout` and :class:`~.SabreSwap` transpiler passes. - Fixed `#10650 `__ - -############# -Qiskit 0.44.0 -############# - -This release officially marks the end of support for the Qiskit IBMQ Provider -package and the removal of Qiskit Aer from the Qiskit metapackage. After this -release the metapackage only contains Qiskit Terra, so this is the final -release we will refer to the Qiskit metapackage and Qiskit Terra as separate -things. Starting in the next release Qiskit 0.45.0 the Qiskit package will -just be what was previously Qiskit Terra and there will no longer be a -separation between them. - -If you're still using the ``qiskit-ibmq-provider`` package it has now been -retired and is no longer supported. You should follow the links to the migration -guides in the README for the package on how to switch over to the new replacement -packages ``qiskit-ibm-provider``, ``qiskit-ibm-runtime``, and -``qiskit-ibm-experiment``: - -https://github.com/Qiskit/qiskit-ibmq-provider#migration-guides - -The Qiskit Aer project is still active and maintained moving forward it is -just no longer included as part of the ``qiskit`` package. To continue using -``qiskit-aer`` you will need to explicitly install ``qiskit-aer`` and import the -package from ``qiskit_aer``. - -As this is the final release of the Qiskit metapackage the following setuptools -extras used to install optional dependencies will no longer work in the next -release Qiskit 0.45.0: - - * ``nature`` - * ``machine-learning`` - * ``finance`` - * ``optimization`` - * ``experiments`` - -If you're using the extras to install any packages you should migrate to using -the packages directly instead of the extra. For example if you were using -``pip install qiskit[experiments]`` previously you should switch to -``pip install qiskit qiskit-experiments`` to install both packages. -Similarly the ``all`` extra (what gets installed via -``pip install "qiskit[all]"``) will no longer include these packages in Qiskit -0.45.0. - -.. _Release Notes_0.25.0: - -Terra 0.25.0 -============ - -.. _Release Notes_0.25.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.25-2efd7230b0ae0719.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -The Qiskit Terra 0.25.0 release highlights are: - -* Control-flow operations are now supported through the transpiler at - all optimization levels, including levels 2 and 3 (e.g. calling - :func:`.transpile` or :func:`.generate_preset_pass_manager` with - keyword argument ``optimization_level`` specified as 2 or 3 is now - supported). - -* The fields :attr:`.IfElseOp.condition`, :attr:`.WhileLoopOp.condition` and - :attr:`.SwitchCaseOp.target` can now be instances of the new runtime classical-expression type - :class:`.expr.Expr`. This is distinct from :class:`.ParameterExpression` because it is - evaluated *at runtime* for backends that support such operations. - - These new expressions have significantly more power than the old two-tuple form of supplying - classical conditions. For example, one can now represent equality constraints between two - different classical registers, or the logic "or" of two classical bits. These two examples - would look like:: - - from qiskit.circuit import QuantumCircuit, ClassicalRegister, QuantumRegister - from qiskit.circuit.classical import expr - - qr = QuantumRegister(4) - cr1 = ClassicalRegister(2) - cr2 = ClassicalRegister(2) - qc = QuantumCircuit(qr, cr1, cr2) - qc.h(0) - qc.cx(0, 1) - qc.h(2) - qc.cx(2, 3) - qc.measure([0, 1, 2, 3], [0, 1, 2, 3]) - - # If the two registers are equal to each other. - with qc.if_test(expr.equal(cr1, cr2)): - qc.x(0) - - # While either of two bits are set. - with qc.while_loop(expr.logic_or(cr1[0], cr1[1])): - qc.reset(0) - qc.reset(1) - qc.measure([0, 1], cr1) - - For more examples, see the documentation for :mod:`qiskit.circuit.classical`. - - This feature is new for both Qiskit and the available quantum hardware that - Qiskit works with. As the features are still being developed there are likely - to be places where there are unexpected edge cases that will need some time to - be worked out. If you encounter any issue around classical expression support - or usage please open an issue with Qiskit or your hardware vendor. - - In this initial release, Qiskit has added the operations: - - * :func:`~.expr.bit_not` - * :func:`~.expr.logic_not` - * :func:`~.expr.bit_and` - * :func:`~.expr.bit_or` - * :func:`~.expr.bit_xor` - * :func:`~.expr.logic_and` - * :func:`~.expr.logic_or` - * :func:`~.expr.equal` - * :func:`~.expr.not_equal` - * :func:`~.expr.less` - * :func:`~.expr.less_equal` - * :func:`~.expr.greater` - * :func:`~.expr.greater_equal` - - These can act on Python integer and Boolean literals, or on :class:`.ClassicalRegister` - and :class:`.Clbit` instances. - - All these classical expressions are fully supported through the Qiskit transpiler stack, through - QPY serialisation (:mod:`qiskit.qpy`) and for export to OpenQASM 3 (:mod:`qiskit.qasm3`). Import - from OpenQASM 3 is currently managed by `a separate package `__ - (which is re-exposed via :mod:`qiskit.qasm3`), which we hope will be extended to match the new - features in Qiskit. - -* The :mod:`qiskit.algorithms` module has been deprecated and will be removed - in a future release. It has been superseded by a new standalone library - ``qiskit-algorithms`` which can be found on PyPi or on Github here: - - https://github.com/qiskit-community/qiskit-algorithms - - The :mod:`qiskit.algorithms` module will continue to work as before and bug fixes - will be made to it until its future removal, but active development - of new features has moved to the new package. - If you're relying on :mod:`qiskit.algorithms` you should update your - Python requirements to also include ``qiskit-algorithms`` and update the imports - from ``qiskit.algorithms`` to ``qiskit_algorithms``. Please note that this - new package does not include already deprecated algorithms code, including - ``opflow`` and ``QuantumInstance``-based algorithms. If you have not yet - migrated from ``QuantumInstance``-based to primitives-based algorithms, - you should follow the migration guidelines in https://qisk.it/algo_migration. - The decision to migrate the :mod:`~.algorithms` module to a - separate package was made to clarify the purpose Qiskit and - make a distinction between the tools and libraries built on top of it. - -Qiskit Terra 0.25 has dropped support for Python 3.7 following -deprecation warnings started in Qiskit Terra 0.23. This is consistent -with Python 3.7’s end-of-life on the 27th of June, 2023. To continue -using Qiskit, you must upgrade to a more recent version of Python. - -.. _Release Notes_0.25.0_New Features: - -New Features ------------- - -.. releasenotes/notes/0.25/add-abs-to-parameterexpression-347ffef62946b38b.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The following features have been added in this release. - - -.. _Release Notes_0.25.0_Transpiler Features: - -Transpiler Features -^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/add-block-collection-options-359d5e496313acdb.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added two new options to :class:`~qiskit.dagcircuit.BlockCollector`. - - The first new option ``split_layers`` allows collected blocks to be split into sub-blocks - over disjoint qubit subsets, i.e. into depth-1 sub-blocks. - - The second new option ``collect_from_back`` allows blocks to be greedily collected starting - from the outputs of the circuit. This is important in combination with ALAP-scheduling passes - where we may prefer to put gates in the later rather than earlier blocks. - -.. releasenotes/notes/0.25/add-block-collection-options-359d5e496313acdb.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added new options ``split_layers`` and ``collect_from_back`` to - :class:`~qiskit.transpiler.passes.CollectLinearFunctions` and - :class:`~qiskit.transpiler.passes.CollectCliffords` transpiler passes. - - When ``split_layers`` is `True`, the collected blocks are split into - into sub-blocks over disjoint qubit subsets, i.e. into depth-1 sub-blocks. - Consider the following example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.transpiler.passes import CollectLinearFunctions - - circuit = QuantumCircuit(5) - circuit.cx(0, 2) - circuit.cx(1, 4) - circuit.cx(2, 0) - circuit.cx(0, 3) - circuit.swap(3, 2) - circuit.swap(4, 1) - - # Collect all linear gates, without splitting into layers - qct = CollectLinearFunctions(split_blocks=False, min_block_size=1, split_layers=False)(circuit) - assert qct.count_ops()["linear_function"] == 1 - - # Collect all linear gates, with splitting into layers - qct = CollectLinearFunctions(split_blocks=False, min_block_size=1, split_layers=True)(circuit) - assert qct.count_ops()["linear_function"] == 4 - - The original circuit is linear. When collecting linear gates without splitting into layers, - we should end up with a single linear function. However, when collecting linear gates and - splitting into layers, we should end up with 4 linear functions. - - When ``collect_from_back`` is `True`, the blocks are greedily collected from the outputs towards - the inputs of the circuit. Consider the following example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.transpiler.passes import CollectLinearFunctions - - circuit = QuantumCircuit(3) - circuit.cx(1, 2) - circuit.cx(1, 0) - circuit.h(2) - circuit.swap(1, 2) - - # This combines the CX(1, 2) and CX(1, 0) gates into a single linear function - qct = CollectLinearFunctions(collect_from_back=False)(circuit) - - # This combines the CX(1, 0) and SWAP(1, 2) gates into a single linear function - qct = CollectLinearFunctions(collect_from_back=True)(circuit) - - The original circuit contains a Hadamard gate, so that the `CX(1, 0)` gate can be - combined either with `CX(1, 2)` or with `SWAP(1, 2)`, but not with both. When - ``collect_from_back`` is `False`, the linear blocks are greedily collected from the start - of the circuit, and thus `CX(1, 0)` is combined with `CX(1, 2)`. When - ``collect_from_back`` is `True`, the linear blocks are greedily collected from the end - of the circuit, and thus `CX(1, 0)` is combined with `SWAP(1, 2)`. - -.. releasenotes/notes/0.25/add-classical-predecessors-9ecef0561822e934.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added :meth:`.DAGCircuit.classical_predecessors` and - :meth:`.DAGCircuit.classical_successors`, an alternative to selecting classical - wires that doesn't require accessing the inner graph of a DAG node directly. - The following example illustrates the new functionality:: - - from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister - from qiskit.converters import circuit_to_dag - from qiskit.circuit.library import RZGate - - q = QuantumRegister(3, 'q') - c = ClassicalRegister(3, 'c') - circ = QuantumCircuit(q, c) - circ.h(q[0]) - circ.cx(q[0], q[1]) - circ.measure(q[0], c[0]) - circ.rz(0.5, q[1]).c_if(c, 2) - circ.measure(q[1], c[0]) - dag = circuit_to_dag(circ) - - rz_node = dag.op_nodes(RZGate)[0] - # Contains the "measure" on clbit 0, and the "wire start" nodes for clbits 1 and 2. - classical_predecessors = list(dag.classical_predecessors(rz_node)) - # Contains the "measure" on clbit 0, and the "wire end" nodes for clbits 1 and 2. - classical_successors = list(dag.classical_successors(rz_node)) - -.. releasenotes/notes/0.25/add-control-flow-to-commutative-cancellation-pass-85fe310d911d9a00.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Enabled support for :class:`~qiskit.circuit.ControlFlowOp` operations in the - :class:`~qiskit.transpiler.passes.CommutativeCancellation` pass. - Previously, the blocks in control flow operations were skipped by this pass. - -.. releasenotes/notes/0.25/add-control-flow-to-consolidate-blocks-e013e28007170377.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Enabled support for :class:`.ControlFlowOp` operations in the :class:`.ConsolidateBlocks` pass. - -.. releasenotes/notes/0.25/add-dag-causal-cone-5a19311e40fbb3af.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added :meth:`.DAGCircuit.quantum_causal_cone` to obtain the causal cone of a qubit - in a :class:`~.DAGCircuit`. - The following example shows its correct usage:: - - from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister - from qiskit.circuit.library import CXGate, CZGate - from qiskit.dagcircuit import DAGCircuit - - # Build a DAGCircuit - dag = DAGCircuit() - qreg = QuantumRegister(5) - creg = ClassicalRegister(5) - dag.add_qreg(qreg) - dag.add_creg(creg) - dag.apply_operation_back(CXGate(), qreg[[1, 2]], []) - dag.apply_operation_back(CXGate(), qreg[[0, 3]], []) - dag.apply_operation_back(CZGate(), qreg[[1, 4]], []) - dag.apply_operation_back(CZGate(), qreg[[2, 4]], []) - dag.apply_operation_back(CXGate(), qreg[[3, 4]], []) - - # Get the causal cone of qubit at index 0 - result = dag.quantum_causal_cone(qreg[0]) - -.. releasenotes/notes/0.25/add-method-for-mapping-qubit-clbit-to-positional-index-6cd43a42f56eb549.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- A new method :meth:`~qiskit.dagcircuit.DAGCircuit.find_bit` has - been added to the :class:`~qiskit.dagcircuit.DAGCircuit` class, - which returns the bit locations of the given :class:`~.circuit.Qubit` or - :class:`.Clbit` as a tuple of the positional index of the bit within - the circuit and a list of tuples which locate the bit in the circuit's - registers. - -.. releasenotes/notes/0.25/add-pauli-equivalences-74c635ec5c23ee33.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The transpiler's built-in :class:`.EquivalenceLibrary` - (``qiskit.circuit.equivalence_library.SessionEquivalenceLibrary``) - has been taught the circular Pauli - relations :math:`X = iYZ`, :math:`Y = iZX` and :math:`Z = iXY`. This should make transpiling - to constrained, and potentially incomplete, basis sets more reliable. - See `#10293 `__ for more detail. - -.. releasenotes/notes/0.25/ctrl-flow-o2-o3-83f660d704226848.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Control-flow operations are now supported through the transpiler at - all optimization levels, including levels 2 and 3 (e.g. calling - :func:`.transpile` or :func:`.generate_preset_pass_manager` with - keyword argument ``optimization_level=3``). - -.. releasenotes/notes/0.25/dag-substitute-node-propagate-condition-898052b53edb1f17.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- :meth:`.DAGCircuit.substitute_node` gained a ``propagate_condition`` keyword argument that is - analogous to the same argument in :meth:`~.DAGCircuit.substitute_node_with_dag`. Setting this - to ``False`` opts out of the legacy behaviour of copying a condition on the ``node`` onto the - new ``op`` that is replacing it. - - This option is ignored for general control-flow operations, which will never propagate their - condition, nor accept a condition from another node. - -.. releasenotes/notes/0.25/dagcircuit-separable-circuits-142853e69f530a16.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Introduced a new method, :meth:`.DAGCircuit.separable_circuits`, which returns a - list of :class:`.DAGCircuit` objects, one for each set of connected qubits - which have no gates connecting them to another set. - - Each :class:`.DAGCircuit` instance returned by this method will contain the same - number of clbits as ``self``. This method will not return :class:`.DAGCircuit` - instances consisting solely of clbits. - -.. releasenotes/notes/0.25/enable_target_aware_meas_map-0d8542402a74e9d8.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added the attribute :attr:`.Target.concurrent_measurements` which represents a hardware - constraint of qubits measured concurrently. This constraint is provided in a nested list form, - in which each element represents a qubit group to be measured together. - In an example below:: - - [[0, 1], [2, 3, 4]] - - qubits 0 and 1, and 2, 3 and 4 are measured together on the device. - This constraint doesn't block measuring an individual qubit, but you may - need to consider the alignment of measure operations for these qubits when - working with the - `Qiskit Pulse scheduler `__ - and when authoring new transpiler passes that are timing-aware (i.e. passes - that perform scheduling). - -.. releasenotes/notes/0.25/fixes_8060-ae91e0da9d53a288.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The transpiler pass :class:`~qiskit.transpiler.passes.SetLayout` can now - be constructed with a list of integers that represent the physical qubits - on which the quantum circuit will be mapped on. That is, the first qubit - in the circuit will be allocated to the physical qubit in position zero - of the list, and so on. - -.. releasenotes/notes/0.25/pauli-rotation-equivalences-6b2449c93c042dc9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The transpiler's built-in :class:`.EquivalenceLibrary` has been taught more Pauli-rotation - equivalences between the one-qubit :math:`R_X`, :math:`R_Y` and :math:`R_Z` gates, and between - the two-qubit :math:`R_{XX}`, :math:`R_{YY}` and :math:`R_{ZZ}` gates. This should make - simple basis translations more reliable, especially circuits that use :math:`Y` rotations. - See `#7332 `__. - -.. releasenotes/notes/0.25/sabre-control-flow-3772af2c5b02c6d5.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Control-flow operations are now supported by the Sabre family - of transpiler passes, namely layout pass :class:`.SabreLayout` - and routing pass :class:`.SabreSwap`. Function :func:`.transpile` - keyword arguments ``layout_method`` and ``routing_method`` now - accept the option ``"sabre"`` for circuits with control flow, - which was previously unsupported. - -.. _Release Notes_0.25.0_Circuits Features: - -Circuits Features -^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/expr-rvalue-conditions-8b5d5f7c015658c0.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The fields :attr:`.IfElseOp.condition`, :attr:`.WhileLoopOp.condition` and - :attr:`.SwitchCaseOp.target` can now be instances of the new runtime classical-expression type - :class:`.expr.Expr`. This is distinct from :class:`.ParameterExpression` because it is - evaluated *at runtime* for backends that support such operations. - - These new expressions have significantly more power than the old two-tuple form of supplying - classical conditions. For example, one can now represent equality constraints between two - different classical registers, or the logic "or" of two classical bits. These two examples - would look like:: - - from qiskit.circuit import QuantumCircuit, ClassicalRegister, QuantumRegister - from qiskit.circuit.classical import expr - - qr = QuantumRegister(4) - cr1 = ClassicalRegister(2) - cr2 = ClassicalRegister(2) - qc = QuantumCircuit(qr, cr1, cr2) - qc.h(0) - qc.cx(0, 1) - qc.h(2) - qc.cx(2, 3) - qc.measure([0, 1, 2, 3], [0, 1, 2, 3]) - - # If the two registers are equal to each other. - with qc.if_test(expr.equal(cr1, cr2)): - qc.x(0) - - # While either of two bits are set. - with qc.while_loop(expr.logic_or(cr1[0], cr1[1])): - qc.reset(0) - qc.reset(1) - qc.measure([0, 1], cr1) - - For more examples, see the documentation for :mod:`qiskit.circuit.classical`. - - This feature is new for both Qiskit and the available quantum hardware that - Qiskit works with. As the features are still being developed there are likely - to be places where there are unexpected edge cases that will need some time to - be worked out. If you encounter any issue around classical expression support - or usage please open an issue with Qiskit or your hardware vendor. - - In this initial release, Qiskit has added the operations: - - * :func:`~.expr.bit_not` - * :func:`~.expr.logic_not` - * :func:`~.expr.bit_and` - * :func:`~.expr.bit_or` - * :func:`~.expr.bit_xor` - * :func:`~.expr.logic_and` - * :func:`~.expr.logic_or` - * :func:`~.expr.equal` - * :func:`~.expr.not_equal` - * :func:`~.expr.less` - * :func:`~.expr.less_equal` - * :func:`~.expr.greater` - * :func:`~.expr.greater_equal` - - These can act on Python integer and Boolean literals, or on :class:`.ClassicalRegister` - and :class:`.Clbit` instances. - - All these classical expressions are fully supported through the Qiskit transpiler stack, through - QPY serialisation (:mod:`qiskit.qpy`) and for export to OpenQASM 3 (:mod:`qiskit.qasm3`). Import - from OpenQASM 3 is currently managed by `a separate package `__ - (which is re-exposed via :mod:`qiskit.qasm3`), which we hope will be extended to match the new - features in Qiskit. - -.. releasenotes/notes/expr-rvalue-conditions-8b5d5f7c015658c0.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Tooling for working with the new representations of classical runtime expressions - has been added. - A general :class:`~.expr.ExprVisitor` is provided for - consumers of these expressions to subclass. Two utilities based on this structure, - :func:`~.expr.iter_vars` and :func:`~.expr.structurally_equivalent`, are also provided, which - respectively produce an iterator through the :class:`~.expr.Var` nodes and check whether two - :class:`~.expr.Expr` instances are structurally the same, up to some mapping of the - :class:`~.expr.Var` nodes contained. - -.. releasenotes/notes/expr-rvalue-conditions-8b5d5f7c015658c0.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added function :func:`~.expr.lift_legacy_condition` which can be used to convert old-style - conditions into new-style :class:`~.expr.Expr` nodes. - Note that these expression nodes are not permitted in old-style :attr:`.Instruction.condition` - fields, which are due to be replaced by more advanced classical handling such as :class:`.IfElseOp`. - -.. releasenotes/notes/0.25/add-abs-to-parameterexpression-347ffef62946b38b.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added support for taking absolute values of :class:`.ParameterExpression`\s. For example, - the following is now possible:: - - from qiskit.circuit import QuantumCircuit, Parameter - - x = Parameter("x") - circuit = QuantumCircuit(1) - circuit.rx(abs(x), 0) - - bound = circuit.bind_parameters({x: -1}) - - -.. releasenotes/notes/0.25/faster-parameter-rebind-3c799e74456469d9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The performance of :meth:`.QuantumCircuit.assign_parameters` and :meth:`~.QuantumCircuit.bind_parameters` - has significantly increased for large circuits with structures typical of applications uses. - This includes most circuits based on the :class:`.NLocal` structure, such as - :class:`.EfficientSU2`. See `#10282 `__ for more - detail. - -.. releasenotes/notes/0.25/faster-parameter-rebind-3c799e74456469d9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The method :meth:`.QuantumCircuit.assign_parameters` has gained two new keywords arguments: ``flat_input`` - and ``strict``. These are advanced options that can be used to speed up the method when passing the - parameter bindings as a dictionary; ``flat_input=True`` is a guarantee that the dictionary keys contain - only :class:`.Parameter` instances (not :class:`.ParameterVector`\ s), and ``strict=False`` allows the - dictionary to contain parameters that are not present in the circuit. Using these two options can - reduce the overhead of input normalisation in this function. - -.. releasenotes/notes/0.25/flatten-nlocal-family-292b23b99947f3c9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added a new keyword argument ``flatten`` to the constructor for the - following classes: - - * :class:`~.EfficientSU2` - * :class:`~.ExcitationPreserving` - * :class:`~.NLocal` - * :class:`~.RealAmplitudes` - * :class:`~.TwoLocal` - * :class:`~.EvolvedOperatorAnsatz` - * :class:`~.QAOAAnsatz` - - If this argument is set to ``True`` the :class:`~.QuantumCircuit` subclass - generated will not wrap the implementation into :class:`~.Gate` or - :class:`~.circuit.Instruction` objects. While this isn't optimal for visualization - it typically results in much better runtime performance, especially with - :meth:`.QuantumCircuit.bind_parameters` and - :meth:`.QuantumCircuit.assign_parameters` which can see a substatial - runtime improvement with a flattened output compared to the nested - wrapped default output. - -.. releasenotes/notes/0.25/linear-functions-usability-45265f293a80a6e5.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added support for constructing :class:`.LinearFunction`\ s from more general quantum circuits, - that may contain: - - * Barriers (of type :class:`.Barrier`) and delays (:class:`~qiskit.circuit.Delay`), - which are simply ignored - * Permutations (of type :class:`~qiskit.circuit.library.PermutationGate`) - * Other linear functions - * Cliffords (of type :class:`.Clifford`), when the Clifford represents a linear function - (and a ``CircuitError`` exception is raised if not) - * Nested quantum circuits of this form - -.. releasenotes/notes/0.25/linear-functions-usability-45265f293a80a6e5.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added :meth:`.LinearFunction.__eq__` method. Two objects of type :class:`.LinearFunction` - are considered equal when their representations as binary invertible matrices are equal. - -.. releasenotes/notes/0.25/linear-functions-usability-45265f293a80a6e5.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added :meth:`.LinearFunction.extend_with_identity` method, which allows to extend - a linear function over ``k`` qubits to a linear function over ``n >= k`` qubits, - specifying the new positions of the original qubits and padding with identities on the - remaining qubits. - -.. releasenotes/notes/0.25/linear-functions-usability-45265f293a80a6e5.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added two methods for pretty-printing :class:`.LinearFunction` objects: - :meth:`.LinearFunction.mat_str`, which returns the string representation of the linear - function viewed as a matrix with 0/1 entries, and - :meth:`.LinearFunction.function_str`, which returns the string representation of the - linear function viewed as a linear transformation. - -.. releasenotes/notes/0.25/normalize-stateprep-e21972dce8695509.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The instructions :class:`.StatePreparation` and :class:`~.extensions.Initialize`, - and their associated circuit methods :meth:`.QuantumCircuit.prepare_state` and :meth:`~.QuantumCircuit.initialize`, - gained a keyword argument ``normalize``, which can be set to ``True`` to automatically normalize - an array target. By default this is ``False``, which retains the current behaviour of - raising an exception when given non-normalized input. - - -.. _Release Notes_0.25.0_Algorithms Features: - -Algorithms Features -^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/umda-callback-eb644a49c5a9ad37.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added the option to pass a callback to the :class:`.UMDA` optimizer, which allows - keeping track of the number of function evaluations, the current parameters, and the - best achieved function value. - - -.. _Release Notes_0.25.0_OpenQASM Features: - -OpenQASM Features -^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/qasm3-alias-refactor-3389bfce3e29e4cf.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The OpenQASM 3 exporters (:func:`.qasm3.dump`, :func:`~.qasm3.dumps` and :class:`~.qasm3.Exporter`) - have a new ``allow_aliasing`` argument, which will eventually replace the ``alias_classical_registers`` - argument. This controls whether aliasing is permitted for either classical bits or qubits, rather - than the option only being available for classical bits. - - -.. _Release Notes_0.25.0_Quantum Information Features: - -Quantum Information Features -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/add-feature-negativity-logarithmic-negativity-fce5d8392460a0e9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added a new function :func:`~qiskit.quantum_info.negativity` that calculates - the entanglement measure of negativity of a quantum state. - Example usage of the above function is given below:: - - from qiskit.quantum_info.states.densitymatrix import DensityMatrix - from qiskit.quantum_info.states.statevector import Statevector - from qiskit.quantum_info import negativity - import numpy as np - - # Constructing a two-qubit bell state vector - state = np.array([0, 1/np.sqrt(2), -1/np.sqrt(2), 0]) - # Calculating negativity of statevector - negv = negativity(Statevector(state), [1]) - - # Creating the Density Matrix (DM) - rho = DensityMatrix.from_label("10+") - # Calculating negativity of DM - negv2 = negativity(rho, [0, 1]) - -.. releasenotes/notes/0.25/add-schmidt-decomposition-c196cff16381b305.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added the function :func:`~qiskit.quantum_info.schmidt_decomposition`. - This function works with the :class:`~qiskit.quantum_info.Statevector` - and :class:`~qiskit.quantum_info.DensityMatrix` classes for bipartite - pure states. - -.. releasenotes/notes/0.25/support-SparsePauliOp-Parameter-multiplication-245173f0b232f59b.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Adds support for multiplication of :class:`.SparsePauliOp` objects - with :class:`.Parameter` objects by using the * operator, for example:: - - from qiskit.circuit import Parameter - from qiskit.quantum_info import SparsePauliOp - - param = Parameter("a") - op = SparsePauliOp("X") - param * op - - -.. _Release Notes_0.25.0_Pulse Features: - -Pulse Features -^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/discrete-pulse-library-deprecation-3a95eba7e29d8d49.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- - The :class:`~qiskit.pulse.library.SymbolicPulse` library was extended. The new pulse functions - in the library are: - - * :func:`~qiskit.pulse.library.GaussianDeriv` - * :func:`~qiskit.pulse.library.Sech` - * :func:`~qiskit.pulse.library.SechDeriv` - * :func:`~qiskit.pulse.library.Square` - - The new functions return a :class:`~qiskit.pulse.library.ScalableSymbolicPulse` instance, and match the functionality - of the corresponding functions in the discrete pulse library, with the exception of - :func:`~qiskit.pulse.library.Square` for which a phase of :math:`2\pi` shifts by a full cycle (contrary to the - discrete :func:`~qiskit.pulse.library.square` where such a shift was induced by a :math:`\pi` phase). - -.. releasenotes/notes/0.25/filter-schedule-block-29d392ca351f1fb1.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The method :meth:`~qiskit.pulse.schedule.ScheduleBlock.filter` is activated - in the :class:`~qiskit.pulse.schedule.ScheduleBlock` class. - This method enables users to retain only :class:`~qiskit.pulse.instructions.Instruction` - objects which pass through all the provided filters. - As builtin filter conditions, pulse :class:`~qiskit.pulse.channels.Channel` - subclass instance and :class:`~qiskit.pulse.instructions.Instruction` - subclass type can be specified. - User-defined callbacks taking :class:`~qiskit.pulse.instructions.Instruction` instance - can be added to the filters, too. - -.. releasenotes/notes/0.25/filter-schedule-block-29d392ca351f1fb1.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The method :meth:`~qiskit.pulse.schedule.ScheduleBlock.exclude` is activated - in the :class:`~qiskit.pulse.schedule.ScheduleBlock` class. - This method enables users to retain only :class:`~qiskit.pulse.instructions.Instruction` - objects which do not pass at least one of all the provided filters. - As builtin filter conditions, pulse :class:`~qiskit.pulse.channels.Channel` - subclass instance and :class:`~qiskit.pulse.instructions.Instruction` - subclass type can be specified. - User-defined callbacks taking :class:`~qiskit.pulse.instructions.Instruction` instance - can be added to the filters, too. - This method is the complement of :meth:`~qiskit.pulse.schedule.ScheduleBlock.filter`, so - the following condition is always satisfied: - ``block.filter(*filters) + block.exclude(*filters) == block`` in terms of - instructions included, where ``block`` is a :class:`~qiskit.pulse.schedule.ScheduleBlock` - instance. - -.. releasenotes/notes/0.25/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added a new function :func:`~qiskit.pulse.library.gaussian_square_echo` to the - pulse library. The returned pulse - is composed of three :class:`~qiskit.pulse.library.GaussianSquare` pulses. The - first two are echo pulses with duration half of the total duration and - implement rotary tones. The third pulse is a cancellation tone that lasts - the full duration of the pulse and implements correcting single qubit - rotations. - -.. releasenotes/notes/0.25/qpy_supports_discriminator_and_kernel-3b6048bf1499f9d3.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- QPY supports the :class:`~qiskit.pulse.configuration.Discriminator` and - :class:`~qiskit.pulse.configuration.Kernel` objects. - This feature enables users to serialize and deserialize the - :class:`~qiskit.pulse.instructions.Acquire` instructions with these objects - using QPY. - - -.. _Release Notes_0.25.0_Synthesis Features: - -Synthesis Features -^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/cx_cz_synthesis-3d5ec98372ce1608.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Added a new synthesis function :func:`~qiskit.synthesis.synth_cx_cz_depth_line_my` - which produces the circuit form of a CX circuit followed by a CZ circuit for linear - nearest neighbor (LNN) connectivity in 2-qubit depth of at most 5n, using CX and - phase gates (S, Sdg or Z). The synthesis algorithm is based on the paper of Maslov - and Yang, `arXiv:2210.16195 `__. - - The algorithm accepts a binary invertible matrix ``mat_x`` representing the CX-circuit, - a binary symmetric matrix ``mat_z`` representing the CZ-circuit, and returns a quantum circuit - with 2-qubit depth of at most 5n computing the composition of the CX and CZ circuits. - The following example illustrates the new functionality:: - - import numpy as np - from qiskit.synthesis.linear_phase import synth_cx_cz_depth_line_my - mat_x = np.array([[0, 1], [1, 1]]) - mat_z = np.array([[0, 1], [1, 0]]) - qc = synth_cx_cz_depth_line_my(mat_x, mat_z) - - This function is now used by default in the Clifford synthesis algorithm - :func:`~qiskit.synthesis.synth_clifford_depth_lnn` that optimizes 2-qubit depth - for LNN connectivity, improving the 2-qubit depth from 9n+4 to 7n+2. - The clifford synthesis algorithm can be used as follows:: - - from qiskit.quantum_info import random_clifford - from qiskit.synthesis import synth_clifford_depth_lnn - - cliff = random_clifford(3) - qc = synth_clifford_depth_lnn(cliff) - - The above synthesis can be further improved as described in the paper by Maslov and Yang, - using local optimization between 2-qubit layers. This improvement is left for follow-up - work. - - -.. _Release Notes_0.25.0_Visualization Features: - -Visualization Features -^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/display-control-flow-mpl-drawer-2dbc7b57ac52d138.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- :meth:`.QuantumCircuit.draw` and function :func:`~qiskit.visualization.circuit_drawer` - when using option ``output='mpl'`` now support drawing the nested circuit blocks of - :class:`~qiskit.circuit.ControlFlowOp` operations, including - ``if``, ``else``, ``while``, ``for``, and ``switch/case``. Circuit blocks are - wrapped with boxes to delineate the circuits. - -.. releasenotes/notes/0.25/relax_wire_order_restrictions-ffc0cfeacd7b8d4b.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Some restrictions when using ``wire_order`` in the circuit drawers have been relaxed. - Now, ``wire_order`` can list just qubits and, in that case, it can be used - with ``cregbundle=True``, since it will not affect the classical bits. - - .. code-block:: - - from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister - - qr = QuantumRegister(4, "q") - cr = ClassicalRegister(4, "c") - cr2 = ClassicalRegister(2, "ca") - circuit = QuantumCircuit(qr, cr, cr2) - circuit.h(0) - circuit.h(3) - circuit.x(1) - circuit.x(3).c_if(cr, 10) - circuit.draw('text', wire_order=[2, 3, 0, 1], cregbundle=True) - - .. parsed-literal:: - - q_2: ──────────── - ┌───┐ ┌───┐ - q_3: ┤ H ├─┤ X ├─ - ├───┤ └─╥─┘ - q_0: ┤ H ├───╫─── - ├───┤ ║ - q_1: ┤ X ├───╫─── - └───┘┌──╨──┐ - c: 4/═════╡ 0xa ╞ - └─────┘ - ca: 2/════════════ - - -.. _Release Notes_0.25.0_Misc. Features: - -Misc. Features -^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/has-pygments-tester-3fb9f9c34907d45d.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- A new lazy import tester, :data:`.HAS_PYGMENTS`, is available for testing for the presence of - `the Pygments syntax highlighting library `__. - -.. releasenotes/notes/0.25/qiskit_version-956916f7b8d7bbb9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The magic ``%qiskit_version_table`` from ``qiskit.tools.jupyter`` now includes all - imported modules with ``qiskit`` in their name. - -.. _Release Notes_0.25.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.25/drop-python3.7-8689e1fa349a49df.yaml @ b'a8faffb120d2b08968bf444acbe6b55ad0c37f39' - -- Qiskit Terra 0.25 has dropped support for Python 3.7 following deprecation warnings started in - Qiskit Terra 0.23. This is consistent with Python 3.7's end-of-life on the 27th of June, 2023. - To continue using Qiskit, you must upgrade to a more recent version of Python. - -.. releasenotes/notes/0.25/token-swapper-rustworkx-9e02c0ab67a59fe8.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Qiskit Terra 0.25 now requires versison 0.13.0 of ``rustworkx``. - -.. releasenotes/notes/0.25/use-abi3-4a935e0557d3833b.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- By default Qiskit builds its compiled extensions using the - `Python Stable ABI `__ - with support back to the oldest version of Python supported by Qiskit - (currently 3.8). This means that moving forward there - will be a single precompiled wheel that is shipped on release that - works with all of Qiskit's supported Python versions. There isn't any - expected runtime performance difference using the limited API so it is - enabled by default for all builds now. - Previously, the compiled extensions were built using the version specific API and - would only work with a single Python version. This change was made - to reduce the number of package files we need to build and publish in each - release. When building Qiskit from source, there should be no changes - necessary to the build process except that the default tags in the output - filenames will be different to reflect the use of the limited API. - - -.. _Release Notes_0.25.0_Transpiler Upgrade Notes: - -Transpiler Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/remove-transpile-broadcast-1dfde28d508efa0d.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Support for passing in lists of argument values to the :func:`~.transpile` - function is removed. This functionality was deprecated as part of the - 0.23.0 release. You are still able to pass in a - list of :class:`~.QuantumCircuit` objects for the first positional argument. - What has been removed is list broadcasting of the other arguments to - each circuit in that input list. Removing this functionality was necessary - to greatly reduce the overhead for parallel execution for transpiling - multiple circuits at once. If you’re using this functionality - currently you can call :func:`~.transpile` multiple times instead. For - example if you were previously doing something like:: - - from qiskit.transpiler import CouplingMap - from qiskit import QuantumCircuit - from qiskit import transpile - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - - cmaps = [CouplingMap.from_heavy_hex(d) for d in range(3, 15, 2)] - results = transpile([qc] * 6, coupling_map=cmaps) - - instead you should now run something like:: - - from qiskit.transpiler import CouplingMap - from qiskit import QuantumCircuit - from qiskit import transpile - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - - cmaps = [CouplingMap.from_heavy_hex(d) for d in range(3, 15, 2)] - results = [transpile(qc, coupling_map=cm) for cm in cmap] - - You can also leverage :func:`~.parallel_map` or ``multiprocessing`` from - the Python standard library if you want to run this in parallel. - -.. releasenotes/notes/0.25/sabre-ctrl-flow-o1-431cd25a19adbcdc.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The Sabre family of transpiler passes (namely :class:`.SabreLayout` - and :class:`.SabreSwap`) are now used by default for all circuits - when invoking the transpiler at optimization level 1 (e.g. calling - :func:`.transpile` or :func:`.generate_preset_pass_manager` with - keyword argument ``optimization_level=1``). Previously, circuits - with control flow operations used :class:`.DenseLayout` and - :class:`.StochasticSwap` with this profile. - - -.. _Release Notes_0.25.0_Circuits Upgrade Notes: - -Circuits Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/new-circuit-qasm2-methods-b1a06ee2859e2cce.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The OpenQASM 2 constructor methods on :class:`.QuantumCircuit` - (:meth:`~.QuantumCircuit.from_qasm_str` and :meth:`~.QuantumCircuit.from_qasm_file`) have been - switched to use the Rust-based parser added in Qiskit Terra 0.24. This should result in - significantly faster parsing times (10 times or more is not uncommon) and massively reduced - intermediate memory usage. - - The :class:`.QuantumCircuit` methods are kept with the same interface for continuity; the - preferred way to access the OpenQASM 2 importer is to use :func:`.qasm2.load` and - :func:`.qasm2.loads`, which offer an expanded interface to control the parsing and construction. - -.. releasenotes/notes/0.25/remove-deprecate-instructionset-circuit-cregs-91617e4b0993db9a.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The deprecated ``circuit_cregs`` argument to the constructor for the - :class:`~.InstructionSet` class has been removed. It was deprecated in the - 0.19.0 release. If you were using this argument and manually constructing - an :class:`~.InstructionSet` object (which should be quite uncommon as it's - mostly used internally) you should pass a callable to the - ``resource_requester`` keyword argument instead. For example:: - - from qiskit.circuit import Clbit, ClassicalRegister, InstructionSet - from qiskit.circuit.exceptions import CircuitError - - def my_requester(bits, registers): - bits_set = set(bits) - bits_flat = tuple(bits) - registers_set = set(registers) - - def requester(specifier): - if isinstance(specifer, Clbit) and specifier in bits_set: - return specifier - if isinstance(specifer, ClassicalRegster) and specifier in register_set: - return specifier - if isinstance(specifier, int) and 0 <= specifier < len(bits_flat): - return bits_flat[specifier] - raise CircuitError(f"Unknown resource: {specifier}") - - return requester - - my_bits = [Clbit() for _ in [None]*5] - my_registers = [ClassicalRegister(n) for n in range(3)] - - InstructionSet(resource_requester=my_requester(my_bits, my_registers)) - - -.. _Release Notes_0.25.0_OpenQASM Upgrade Notes: - -OpenQASM Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/new-circuit-qasm2-methods-b1a06ee2859e2cce.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The OpenQASM 2 constructor methods on :class:`.QuantumCircuit` - (:meth:`~.QuantumCircuit.from_qasm_str` and :meth:`~.QuantumCircuit.from_qasm_file`) have been - switched to use the Rust-based parser added in Qiskit Terra 0.24. This should result in - significantly faster parsing times (10 times or more is not uncommon) and massively reduced - intermediate memory usage. - - The :class:`.QuantumCircuit` methods are kept with the same interface for continuity; the - preferred way to access the OpenQASM 2 importer is to use :func:`.qasm2.load` and - :func:`.qasm2.loads`, which offer an expanded interface to control the parsing and construction. - -.. releasenotes/notes/0.25/qasm3-alias-refactor-3389bfce3e29e4cf.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The OpenQASM 3 exporters (:func:`.qasm3.dump`, :func:`~.qasm3.dumps` and :class:`~.qasm3.Exporter`) - will now use fewer "register alias" definitions in its output. The circuit described will not - change, but it will now preferentially export in terms of direct ``bit``, ``qubit`` and - ``qubit[n]`` types rather than producing a ``_loose_bits`` register and aliasing more registers - off this. This is done to minimise the number of advanced OpenQASM 3 features in use, and to - avoid introducing unnecessary array structure into programmes that do not require it. - - -.. _Release Notes_0.25.0_Quantum Information Upgrade Notes: - -Quantum Information Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/clifford-no-circuly-c7c4a1c9c5472af7.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- :meth:`.Clifford.from_circuit` will no longer attempt to resolve instructions whose - :attr:`~.circuit.Instruction.definition` fields are mutually recursive with some other object. - Such recursive definitions are already a violation of the strictly hierarchical ordering that - the :attr:`~.circuit.Instruction.definition` field requires, and code should not rely on this - being possible at all. If you want to define equivalences that are permitted to have (mutual) - cycles, use an :class:`.EquivalenceLibrary`. - - -.. _Release Notes_0.25.0_Visualization Upgrade Notes: - -Visualization Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/remove-deprecated-mpl-drawer-9d6eaa40d5a86777.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- In the internal ``~qiskit.visualization.circuit.matplotlib.MatplotlibDrawer`` object, the arguments - ``layout``, ``global_phase``, ``qregs`` and ``cregs`` have been removed. They were originally - deprecated in Qiskit Terra 0.20. These objects are simply inferred from the given ``circuit`` - now. - - This is an internal worker class of the visualization routines. It is unlikely you will - need to change any of your code. - - -.. _Release Notes_0.25.0_Misc. Upgrade Notes: - -Misc. Upgrade Notes -^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/remove-util-3cd9eae2efc95176.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The ``qiskit.util`` import location has been removed, as it had - been deprecated since Qiskit Terra 0.17. Users should use the new - import location, ``qiskit.utils``. - - -.. _Release Notes_0.25.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.25/deprecate-namespace-a2ac600f140755e2.yaml @ b'a8faffb120d2b08968bf444acbe6b55ad0c37f39' - -- Extensions of the ``qiskit`` and ``qiskit.providers`` namespaces by external - packages are now deprecated and the hook points enabling this will be - removed in a future release. In the past, the Qiskit project was composed - of elements that extended a shared namespace and these hook points enabled - doing that. However, it was not intended for these interfaces to ever be - used by other packages. Now that the overall Qiskit package is no longer - using that packaging model, leaving the possibility for these extensions - carry more risk than benefits and is therefore being deprecated for - future removal. If you're maintaining a package that extends the Qiskit - namespace (i.e. your users import from ``qiskit.x`` or - ``qiskit.providers.y``) you should transition to using a standalone - Python namespace for your package. No warning will be raised as part of this - because there is no method to inject a warning at the packaging level that - would be required to warn external packages of this change. - -.. releasenotes/notes/0.25/qiskit_version-956916f7b8d7bbb9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The dictionary ``qiskit.__qiskit_version__`` is deprecated, as Qiskit is defined with a single package (``qiskit-terra``). - In the future, ``qiskit.__version__`` will be the single point to query the Qiskit version, as a standard string. - - -.. _Release Notes_0.25.0_Transpiler Deprecations: - -Transpiler Deprecations -^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/deprecate-get_vf2_call_limit-826e0f9212fb27b9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The function ``get_vf2_call_limit`` available via the module - :mod:`qiskit.transpiler.preset_passmanagers.common` has been - deprecated. This will likely affect very few users since this function was - neither explicitly exported nor documented. Its functionality has been - replaced and extended by a function in the same module. - - -.. _Release Notes_0.25.0_Circuits Deprecations: - -Circuits Deprecations -^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/deprecate-instruction-qasm-9380f721e7bdaf6b.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The method :meth:`~qiskit.circuit.Instruction.qasm` and all overriding methods of subclasses - of `:class:~qiskit.circuit.Instruction` are deprecated. There is no replacement for generating - an OpenQASM2 string for an isolated instruction as typically - a single instruction object has insufficient context to completely - generate a valid OpenQASM2 string. If you're relying on this - method currently you'll have to instead rely on the OpenQASM2 - exporter: :meth:`.QuantumCircuit.qasm` to generate the OpenQASM2 - for an entire circuit object. - - -.. _Release Notes_0.25.0_Algorithms Deprecations: - -Algorithms Deprecations -^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/deprecate-algorithms-7149dee2da586549.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The :mod:`qiskit.algorithms` module has been deprecated and will be removed - in a future release. It has been superseded by a new standalone library - ``qiskit-algorithms`` which can be found on PyPi or on Github here: - - https://github.com/qiskit-community/qiskit-algorithms - - The :mod:`qiskit.algorithms` module will continue to work as before and bug fixes - will be made to it until its future removal, but active development - of new features has moved to the new package. - If you're relying on :mod:`qiskit.algorithms` you should update your - Python requirements to also include ``qiskit-algorithms`` and update the imports - from ``qiskit.algorithms`` to ``qiskit_algorithms``. Please note that this - new package does not include already deprecated algorithms code, including - ``opflow`` and ``QuantumInstance``-based algorithms. If you have not yet - migrated from ``QuantumInstance``-based to primitives-based algorithms, - you should follow the migration guidelines in https://qisk.it/algo_migration. - The decision to migrate the :mod:`~.algorithms` module to a - separate package was made to clarify the purpose Qiskit and - make a distinction between the tools and libraries built on top of it. - - -.. _Release Notes_0.25.0_Pulse Deprecations: - -Pulse Deprecations -^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/deprecate-complex-amp-41381bd9722bc878.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Initializing a :class:`~qiskit.pulse.library.ScalableSymbolicPulse` with complex value for ``amp``. - This change also affects the following library pulses: - - * :class:`~qiskit.pulse.library.Gaussian` - * :class:`~qiskit.pulse.library.GaussianSquare` - * :class:`~qiskit.pulse.library.Drag` - * :class:`~qiskit.pulse.library.Constant` - - Initializing ``amp`` for these with a complex value is now deprecated as well. - - Instead, use two floats when specifying the ``amp`` and ``angle`` parameters, where ``amp`` represents the - magnitude of the complex amplitude, and `angle` represents the angle of the complex amplitude. i.e. the - complex amplitude is given by :math:`\texttt{amp} \times \exp(i \times \texttt{angle})`. - -.. releasenotes/notes/0.25/deprecate-pulse-Call-instruction-538802d8fad7e257.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The :class:`~qiskit.pulse.instructions.Call` instruction has been deprecated and will - be removed in a future release. - Instead, use function :func:`~qiskit.pulse.builder.call` from module - :mod:`qiskit.pulse.builder` within an active building context. - - -.. _Release Notes_0.25.0_Misc. Deprecations: - -Misc. Deprecations -^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.25/deprecate-circuit-library-jupyter-629f927e8dd5cc22.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The Jupyter magic ``%circuit_library_info`` and the objects in ``qiskit.tools.jupyter.library`` - it calls in turn: - - - ``circuit_data_table`` - - ``properties_widget`` - - ``qasm_widget`` - - ``circuit_digram_widget`` - - ``circuit_library_widget`` - - are deprecated and will be removed in a future release. These objects were only intended for use in - the documentation build. They are no longer used there, so are no longer supported or maintained. - - -.. _Release Notes_0.25.0_Known Issues: - -Known Issues ------------- - -.. releasenotes/notes/expr-rvalue-conditions-8b5d5f7c015658c0.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Circuits containing classical expressions made with the :mod:`~.classical.expr` module are not - yet supported by the circuit visualizers. - - -.. _Release Notes_0.25.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/channel-validation-bug-fix-c06f8445cecc8478.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Fixed a bug in :class:`~qiskit.pulse.channels.Channel` where index validation was done incorrectly and only - raised an error when the index was both non-integer and negative, instead of either. - -.. releasenotes/notes/fix-final-layout-in-apply-layout-dfbdbde593cf7929.yaml @ b'04c810861f54c06e2a32a67ac42c299d169b91ef' - -- Fixed an issue with the :func:`~.transpile` function and all the preset - pass managers generated via :func:`~.generate_preset_pass_manager` where - the output :class:`~.QuantumCircuit` object's :attr:`~.QuantumCircuit.layout` - attribute would have an invalid :attr:`.TranspileLayout.final_layout` - attribute. This would occur in scenarios when the :class:`~.VF2PostLayout` - pass would run and find an alternative initial layout that has lower - reported error rates. When altering the initial layout the - :attr:`~.TranspileLayout.final_layout` attribute was never updated to - reflect this change. This has been corrected so that the ``final_layout`` - is always correctly reflecting the output permutation caused by the routing - stage. - Fixed `#10457 `__ - -.. releasenotes/notes/qasm2-fix-zero-op-barrier-4af211b119d5b24d.yaml @ b'f1ea299c328a895079550065fafe94b85c705f7c' - -- The OpenQASM 2 parser (:func:`.qasm2.load` and :func:`~.qasm2.loads`) running in ``strict`` mode - will now correctly emit an error if a ``barrier`` statement has no arguments. When running in - the (default) more permissive mode, an argument-less ``barrier`` statement will continue to - cause a barrier on all qubits currently in scope (the qubits a gate definition affects, or all - the qubits defined by a program, if the statement is in a gate body or in the global scope, - respectively). - -.. releasenotes/notes/qasm2-fix-zero-op-barrier-4af211b119d5b24d.yaml @ b'f1ea299c328a895079550065fafe94b85c705f7c' - -- The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now no longer attempt - to output ``barrier`` statements that act on no qubits. Such a barrier statement has no effect - in Qiskit either, but is invalid OpenQASM 2. - -.. releasenotes/notes/qasm_invalid_custom_instruction-7738db7ba1a1a5cf.yaml @ b'97e1808067ac61e42ee6cbf97632c5d540126db2' - -- Qiskit can represent custom instructions that act on zero qubits, or on a non-zero number of - classical bits. These cannot be exported to OpenQASM 2, but previously :meth:`.QuantumCircuit.qasm` - would try, and output invalid OpenQASM 2. Instead, a :exc:`.QASM2ExportError` will now correctly - be raised. See `#7351 `__ and - `#10435 `__. - -.. releasenotes/notes/0.25/ancilla_allocation_no_cmap-ac3ff65b3639988e.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Fixed an issue with using :class:`~.Target`\ s without coupling maps with the :class:`~.FullAncillaAllocation` transpiler pass. - In this case, :class:`~.FullAncillaAllocation` will now add - ancilla qubits so that the number of qubits in the :class:`~.DAGCircuit` matches - that of :attr:`Target.num_qubits`. - -.. releasenotes/notes/0.25/dag-substitute-node-propagate-condition-898052b53edb1f17.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- :meth:`.DAGCircuit.substitute_node` will no longer silently overwrite an existing condition on - the given replacement ``op``. If ``propagate_condition`` is set to ``True`` (the default), a - :exc:`.DAGCircuitError` will be raised instead. - -.. releasenotes/notes/0.25/faster-parameter-rebind-3c799e74456469d9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- A parametrised circuit that contains a custom gate whose definition has a parametrised global phase - can now successfully bind the parameter in the inner global phase. - See `#10283 `__ for more detail. - -.. releasenotes/notes/0.25/fix-0q-operator-statevector-79199c65c24637c4.yaml @ b'a8faffb120d2b08968bf444acbe6b55ad0c37f39' - -- Construction of a :class:`~.quantum_info.Statevector` from a :class:`.QuantumCircuit` containing - zero-qubit operations will no longer raise an error. These operations impart a global phase on - the resulting statevector. - -.. releasenotes/notes/0.25/fix-controlflow-builder-nested-switch-008b8c43b2153a1f.yaml @ b'a8faffb120d2b08968bf444acbe6b55ad0c37f39' - -- The control-flow builder interface will now correctly include :class:`.ClassicalRegister` - resources from nested switch statements in their containing circuit scopes. See `#10398 - `__. - -.. releasenotes/notes/0.25/fix-decompose-name-f83f5e4e64918aa9.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Fixed an issue in :meth:`.QuantumCircuit.decompose` - where passing a circuit name to the function that matched a - composite gate name would not decompose the gate if it had a label - assigned to it as well. - Fixed `#9136 `__ - -.. releasenotes/notes/0.25/fix-plot-legend-not-showing-up-3202bec143529e49.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Fixed an issue with :func:`qiskit.visualization.plot_histogram` where the relative legend - did not show up when the given dataset had a zero value in the first position. - See `#10158 `__ for more details. - -.. releasenotes/notes/0.25/fix-update-from-instruction-schedule-map-d1cba4e4db4b679e.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Fixed a failure with method :meth:`.Target.update_from_instruction_schedule_map` - triggered by the given ``inst_map`` containing a :class:`~qiskit.pulse.Schedule` - with unassigned durations. - -.. releasenotes/notes/0.25/fix_9016-2e8bc2cb10b5e204.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- When the parameter ``conditional=True`` is specified in - :func:`~qiskit.circuit.random.random_circuit`, conditional operations - in the resulting circuit will - now be preceded by a full mid-circuit measurment. - Fixes `#9016 `__ - -.. releasenotes/notes/0.25/improve-quantum-circuit-assign-parameters-typing-70c9623405cbd420.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Improved the type annotations on the - :meth:`.QuantumCircuit.assign_parameters` - method to reflect the change in return type depending on the ``inplace`` - argument. - -.. releasenotes/notes/0.25/new-circuit-qasm2-methods-b1a06ee2859e2cce.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The OpenQASM 2 circuit-constructor methods (:meth:`.QuantumCircuit.from_qasm_str` and - :meth:`~.QuantumCircuit.from_qasm_file`) will no longer error when encountering a ``gate`` - definition that contains ``U`` or ``CX`` instructions. See `#5536 - `__. - -.. releasenotes/notes/0.25/optimize-consolidate-blocks-3ea60c18bc546273.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Reduced overhead of the :class:`.ConsolidateBlocks` pass by performing matrix operations - on all two-qubit blocks instead of creating an instance of :class:`.QuantumCircuit` and - passing it to an :class:`.Operator`. - The speedup will only be applicable when consolidating two-qubit blocks. Anything higher - than that will still be handled by the :class:`.Operator` class. - Check `#8779 `__ for details. - -.. releasenotes/notes/0.25/qasm3-no-subroutine-b69c5ed7c65ce9ac.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- The OpenQASM 3 exporter (:mod:`qiskit.qasm3`) will no longer output invalid OpenQASM 3 for - non-unitary :class:`~.circuit.Instruction` instances, but will instead raise a - :exc:`.QASM3ExporterError` explaining that these are not yet supported. This feature is - slated for a later release of Qiskit, when there are more classical-processing facilities - throughout the library. - -.. releasenotes/notes/0.25/support-SparsePauliOp-Parameter-multiplication-245173f0b232f59b.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Fixes issue `#10185 `__. - -.. releasenotes/notes/0.25/unintended-rounding-with-max-size-1498af5f9a467990.yaml @ b'fa0491b87736f5244c0e604df71cfcd91683aa43' - -- Fixed an issue with function :func:`~qiskit.visualization.state_visualization.state_to_latex`. - Previously, it produced invalid LaTeX with unintended coefficient rounding, which resulted in - errors when calling :func:`~qiskit.visualization.state_visualization.state_drawer`. - Fixed `#9297 `__. - -############# -Qiskit 0.43.3 -############# - -Terra 0.24.2 -============ -.. _Release Notes_0.24.2_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.24.2-b496e2bbaf3b2454.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -Qiskit Terra 0.24.2 is a bugfix release, addressing some minor issues identified since the 0.24.1 release. - -.. _Release Notes_0.24.2_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/qpy-layout-927ab34f2b47f4aa.yaml @ b'a87ee835515f96a0dce6950e4ae21f73825e4f01' - -- The QPY format version emitted by :class:`~.qpy.dump` has increased to 8. - This new format version adds support for serializing the - :attr:`.QuantumCircuit.layout` attribute. - - -.. _Release Notes_0.24.2_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/add-diagonal-to-DiagonalGate-c945e0f8adcd2940.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Fixed the deserialization of :class:`~.DiagonalGate` instances through QPY. - Fixed `#10364 `__ - -.. releasenotes/notes/fix-1q-matrix-bug-in-quantum-shannon-decomposer-c99ce6509f03715b.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Fixed an issue with the :func:`~.qs_decomposition` function, which does - quantum Shannon decomposition, when it was called on trivial numeric - unitaries that do not benefit from this decomposition, an unexpected error - was raised. This error has been fixed so that such unitaries are detected - and the equivalent circuit is returned. - Fixed `#10036 `__ - -.. releasenotes/notes/fix-basicswap-fakerun-7469835327f6c8a1.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Fixed an issue in the the :class:`~.BasicSwap` class that - prevented the :meth:`.BasicSwap.run` method from functioning if the - ``fake_run`` keyword argument was set to ``True`` when the class was - instantiated. - Fixed `#10147 `__ - -.. releasenotes/notes/fix-bit-copy-4b2f7349683f616a.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Fixed an issue with copying circuits with new-style :class:`.Clbit`\ s and - :class:`~.circuit.Qubit`\ s (bits without registers) where references to these bits - from the containing circuit could be broken, causing issues with - serialization and circuit visualization. - Fixed `#10409 `__ - -.. releasenotes/notes/fix-checkmap-nested-condition-1776f952f6c6722a.yaml @ b'c0f02c61866098fd6e54f36bc7fb3a996e234223' - -- The :class:`.CheckMap` transpiler pass will no longer spuriously error when dealing with nested - conditional structures created by the control-flow builder interface. See `#10394 - `__. - -.. releasenotes/notes/fix-dispatching-backends-28aff96f726ca9c5.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Fixed an failure of the :ref:`pulse_builder` when the context is initialized with :class:`.BackendV2`. - -.. releasenotes/notes/fix-outputs-of-measure_v2-8959ebbbf5f87294.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Fixed the output of pulse :func:`~qiskit.pulse.builder.measure` and - :func:`~qiskit.pulse.builder.measure_all` when functions are called - with the :class:`.BackendV2` backend. - -.. releasenotes/notes/fix-partial-transpose-output-dims-3082fcf4147055dc.yaml @ b'd1b8c5de8ccd67ad7efabb9d9f581643fccad4ee' - -- Fixed the dimensions of the output density matrix from :meth:`.DensityMatrix.partial_transpose` - so they match the dimensions of the corresponding input density matrix. - -.. releasenotes/notes/fix-primitives-import-warnings-439e3e237fdb9d7b.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Importing :mod:`qiskit.primitives` will no longer cause deprecation warnings stemming from the - deprecated :mod:`qiskit.opflow` module. These warnings would have been hidden to users by the - default Python filters, but triggered the eager import of :mod:`.opflow`, which meant that a - subsequent import by a user would not trigger the warnings. - Fixed `#10245 `__ - -.. releasenotes/notes/fix-qasm-circuit-export-943394536bc0d292.yaml @ b'e43d5c8da4fe4a071ba08746a7a2ed2dd479b17d' - -- Fixed the OpenQASM 2 output of :meth:`.QuantumCircuit.qasm` when a custom gate object contained - a gate with the same name. Ideally this shouldn't happen for most gates, but complex algorithmic - operations like the :class:`.GroverOperator` class could produce such structures accidentally. - See `#10162 `__. - -.. releasenotes/notes/fix-regression-in-the-LaTeX-drawer-of-QuantumCircuit-7dd3e84e1dea1abd.yaml @ b'4d5f8305bc08b98b5167164ce3e146582cad48a6' - -- Fixed a regression in the LaTeX drawer of :meth:`.QuantumCircuit.draw` - when temporary files are placed on a separate filesystem to the working - directory. See - `#10211 `__. - -.. releasenotes/notes/fix-synthesis-cf-mapping-fe9bd2e5fbd56dfb.yaml @ b'2317c83af0516273231d4a1c20ba1c8863fbde9e' - -- Fixed an issue with :class:`.UnitarySynthesis` when using the ``target`` - parameter where circuits with control flow were not properly mapped - to the target. - -.. releasenotes/notes/fix-vqd-result-27b26f0a6d49e7c7.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Fixed bug in :class:`~qiskit.algorithms.eigensolvers.VQD` where ``result.optimal_values`` was a - copy of ``result.optimal_points``. It now returns the corresponding values. - Fixed `#10263 `__ - -.. releasenotes/notes/parameter-float-cast-48f3731fec5e47cd.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Improved the error messages returned when an attempt to convert a fully bound - :class:`.ParameterExpression` into a concrete ``float`` or ``int`` failed, for example because - the expression was naturally a complex number. - Fixed `#9187 `__ - -.. releasenotes/notes/parameter-float-cast-48f3731fec5e47cd.yaml @ b'163d1bd7835d58eaf8842c594b3696fb99c8442f' - -- Fixed ``float`` conversions for :class:`.ParameterExpression` values which had, at some point in - their construction history, an imaginary component that had subsequently been cancelled. When - using Sympy as a backend, these conversions would usually already have worked. When using - Symengine as the backend, these conversions would often fail with type errors, despite the - result having been symbolically evaluated to be real, and :meth:`.ParameterExpression.is_real` - being true. - Fixed `#10191 `__ - -.. releasenotes/notes/qpy-layout-927ab34f2b47f4aa.yaml @ b'a87ee835515f96a0dce6950e4ae21f73825e4f01' - -- Fixed the :mod:`~qiskit.qpy` serialization of :attr:`.QuantumCircuit.layout` - attribue. Previously, the :attr:`~.QuantumCircuit.layout` attribute would - have been dropped when serializing a circuit to QPY. - Fixed `#10112 `__ - -.. _Release Notes_Aer_0.12.2: - -Aer 0.12.2 -========== - -.. _Release Notes_Aer_0.12.2_Prelude: - -Prelude -------- - -.. releasenotes/notes/release_0122-3a30897b3ac2df2b.yaml @ b'a8bfca9bd219d919d539cf3094cac5633b4f3f6a' - -Qiskit Aer 0.12.2 is the second patch release to 0.12.0. This fixes some bugs that have been discovered since the release of 0.12.1. - - -.. _Release Notes_Aer_0.12.2_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/renew_gpu_binaries-2cf3eba0853b8407.yaml @ b'a8bfca9bd219d919d539cf3094cac5633b4f3f6a' - -- Qiskit Aer now requires CUDA version for GPU simulator to 11.2 or - higher. Previously, CUDA 10.1 was the minimum supported version. - This change was necessary because of changes in the upstream CUDA - ecosystem, including cuQuantum support. To support users running - with different versions of CUDA there is now a separate package available - for running with CUDA 11: ``qiskit-aer-gpu-cu11`` and using the - ``qiskit-aer-gpu`` package now requires CUDA 12. If you're an existing - user of the ``qiskit-aer-gpu`` package and want to use CUDA 11 - you will need to run:: - - pip uninstall qiskit-aer-gpu && pip install -U qiskit-aer-gpu-cu11 - - to go from the previously CUDA 10.x compatible ``qiskit-aer-gpu`` - package's releases to upgrade to the new CUDA 11 compatible - package. If you're running CUDA 12 locally already you can upgrade - the ``qiskit-aer-gpu`` package as normal. - - -.. _Release Notes_Aer_0.12.2_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix_parameter_indexing-f29f19568270d002.yaml @ b'a8bfca9bd219d919d539cf3094cac5633b4f3f6a' - -- If a circuit has conditional and parameters, the circuit was not be - correctly simulated because parameter bindings of Aer used wrong positions - to apply parameters. This is from a lack of consideration of bfunc operations - injected by conditional. With this commit, parameters are set to correct - positions with consideration of injected bfun operations. - -.. releasenotes/notes/fix_parameter_indexing-f29f19568270d002.yaml @ b'a8bfca9bd219d919d539cf3094cac5633b4f3f6a' - -- Parameters for global phases were not correctly set in #1814. - https://github.com/Qiskit/qiskit-aer/pull/1814 - Parameter values for global phases were copied to a template circuit and not to - actual circuits to be simulated. This commit correctly copies parameter values - to circuits to be simulated. - -.. releasenotes/notes/remove_aer_circuit_from_metadata-e4fe09029c1a3a3c.yaml @ b'a8bfca9bd219d919d539cf3094cac5633b4f3f6a' - -- Results of ``backend.run()`` were not serializable because they include :class:`.AerCircuit`\ s. - This commit makes the results serializable by removing :class:`.AerCircuit`\ s from metadata. - -.. releasenotes/notes/save_statevector_for_qasm3_circ-642ade99af3ff0d2.yaml @ b'a8bfca9bd219d919d539cf3094cac5633b4f3f6a' - -- :meth:``QuantumCircuit.save_statevector()`` does not work if the circuit - is generated from OpenQASM3 text because its quantum registers have duplicated - qubit instances. With this commit, :meth:``QuantumCircuit.save_statevector()`` - uses :data:``QuantumCircuit.qubits`` to get qubits to be saved. - -IBM Q Provider 0.20.2 -===================== - -No change. - -############# -Qiskit 0.43.2 -############# - -As a reminder, `Qiskit Aer `__'s inclusion in the ``qiskit`` -package is deprecated. The next minor version of Qiskit Aer (0.13) will not be included in any -release of the ``qiskit`` package, and you should immediately begin installing Aer separately by:: - - pip install qiskit-aer - -and importing it as:: - - import qiskit_aer - -Starting from Qiskit 0.44, the command ``pip install qiskit`` will no longer install Qiskit Aer, or -the obsolete IBM Q Provider that has already been replaced by the new `IBM Provider -__`. - -.. _Release Notes_0.24.2: - -Terra 0.24.1 -============ - -No change - -Aer 0.12.1 -========== - -.. _Release Notes_Aer_0.12.1_Prelude: - -Prelude -------- - -.. releasenotes/notes/release_0121-eeda752822eb0ad3.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -Qiskit Aer 0.12.1 is the first patch release to 0.12.0. This fixes some bugs that have been discovered since the release of 0.12.0. - - -.. _Release Notes_Aer_0.12.1_Known Issues: - -Known Issues ------------- - -.. releasenotes/notes/primitives-grouping-index-bug-56f69afbdc3e86a0.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Fix a bug that returns wrong expectation values in :class:`~Estimator` when - ``abelian_grouping=True``. - - -.. _Release Notes_Aer_0.12.1_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/estimator-performance-da83a59b9fd69086.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Improved performance when the same circuits and multiple parameters are passed to - :class:`~.Estimator` with ``approximation=True``. - - -.. _Release Notes_Aer_0.12.1_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/implicit_cast_for_arguments-a3c671db2fff6f17.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Options of meth:`~.AerSimulator.run` need to use correct types. - - -.. _Release Notes_Aer_0.12.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/avoid_copy_of_config-7f7891864c1a1bd0.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Performance regression due to introduction of ``AER::Config`` is fixed. - This class has many fields but is frequently copied in ``AER::Transpile::CircuitOptimization``. - Originally ``json_t`` (former class for configuration) was also frequently copied but - it does have entries in most cases and then this copy overhead is not a problem. - With this fix, ``AER::Transpile::CircuitOptimization`` does not copy ``AER::Config``. - -.. releasenotes/notes/avoid_kernel_crash_in_mac_from_blas_error-bd5b836a23f2e3ee.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- When BLAS calls are failed, because omp threads do not handle exceptions, - Aer crashes without any error messages. This fix is for omp threads to catch - exceptions correctly and then rethrow them outside of omp loops. - -.. releasenotes/notes/check_param_length-eb69cd92825bbca4.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Previously, parameters for gates are not validate in C++. If parameters are shorter than - expected (due to custom gate), segmentaion faults are thrown. This commit adds checks - whether parameter lenght is expceted. This commit will fix issues reported in #1612. - https://github.com/Qiskit/qiskit-aer/issues/1612 - -.. releasenotes/notes/check_parameter_binds_exist-9d52c665d5f94dde.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Since 0.12.0, parameter values in circuits are temporarily replaced with constant values - and parameter values are assigned in C++ library. Therefore, if `parameter_binds` is specified, - simulator returns results with the constnat values as paramter values. With this commit, - Aer raises an error if `parameter_binds` is not specified though circuits have parameters. - -.. releasenotes/notes/defer-backend-gathering-773d0ed8092c24d9.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Available devices and methods are no longer queried when importing Aer. - -.. releasenotes/notes/do_not_modify_metadata-60bb4b88707bd021.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Previously :class:`~.AerSimulator` modifies circuit metadata to maintain - consistency between input and output of simulation with side effect of - unexpected view of metadata from applicatiln in simiulation. This fix - avoids using circuit metadata to maintain consistency internaly and then - always provides consistent view of metadata to application. - -.. releasenotes/notes/estimator-variance-type-2b04ff7bcd305920.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Fixed a bug where the variance in metadata in EstimatorResult was complex and now returns float. - -.. releasenotes/notes/fix-cuStateVec_enable-0936f2269466e3be.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Fixed a build break to compile Qiskit Aer with cuQuautum support (`AER_ENABLE_CUQUANTUM=true`). - This change does not affect build for CPU and normal GPU binaries. - -.. releasenotes/notes/fix-none-handling-in-noise-model-34fcc9a3e3cbdf6f.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Fixed a bug in :meth:`~.NoiseModel.from_backend` that raised an error when - the backend has no T1 and T2 values (i.e. None) for a qubit in its qubit properties. - This commit updates :meth:`NoiseModel.from_backend` and :func:`basic_device_gate_errors` - so that they add an identity ``QuantumError`` (i.e. effectively no thermal relaxation error) - to a qubit with no T1 and T2 values for all gates acting on qubits including the qubit. - Fixed `#1779 `__ - and `#1815 `__. - -.. releasenotes/notes/fix-number-qubits-a417ca6afa64264f.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Fix an issue even if the number of qubits is set by a coupling map - or device's configuration, when the simulation method is configured, - the number of qubits is overwritten in accordance with the method. - Fixed `#1769 `__ - -.. releasenotes/notes/fix_cuQuantum_libpath-90d24880cd9a9ea8.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- This is fix for library path setting in CMakeLists.txt for cuQuantum SDK. - Because the latest cuQuantum includes libraries for CUDA 11.x and 12.x, - this fix uses CUDA version returned from FindCUDA to the path of libraries - of cuQuantum and cuTENSOR. - -.. releasenotes/notes/fix_cuQuantum_static-ad132d742a64a3d5.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- This is fix for static link libraries of cuQuantum when building with - CUQUANTUM_STATIC=true. - -.. releasenotes/notes/fix_mpi_procs-68b76c11fe7a6b8e.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- MPI parallelization was not enabled since we have not used qobj. - This fix sets the number of processes and MPI rank correctly. - -.. releasenotes/notes/fix_param_binding_for_pram_circuit-50e64efbedaec8fd.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- :class:`~.AerCircuit` is created from a circuit by iterating its operations - while skipping barrier instructions. However, skipping barrier instructions - make wrong positionings of parameter bindings. This fix adds - :meth:`~.AerCircuit.barrier` and keeps parametr bindings correct. - -.. releasenotes/notes/fix_qobj_run-8ea657a93ce9acd2.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Aer still supports Qobj as an argument of :meth:`~.AerSimulator.run` though - it was deprecated. However, since 0.12.0, it always fails if no ``run_options`` - is specified. This fix enables simulation of Qobj without ``run_options``. - -.. releasenotes/notes/implicit_cast_for_arguments-a3c671db2fff6f17.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Since 0.12.0, :class:`AerConfig` is used for simulation configuration while - performing strict type checking for arguments of meth:`~.AerSimulator.run`. - This commit adds casting if argument types are not expected. - -.. releasenotes/notes/support_int_initialize-8491979c4a003908.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- :meth:``QuantumCircuit.initialize()`` with `int` value was not processed - correctly as reported in `#1821 `. - This commit enables such initialization by decomposing initialize instructions. - -.. releasenotes/notes/support_param_for_global_phase-704a97129e7bdbaa.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- :class:`~qiskit.circuit.QuantumCircuit` supports parameterization for its `global_phase`. - However, Aer has not allowed such parameterization and failed when transpiler generates - parameterized global phases. This commit supports parameterization of `global_phase` and - resolve issues related to https://github.com/Qiskit/qiskit-aer/issues/1795, - https://github.com/Qiskit/qiskit-aer/issues/1781, and https://github.com/Qiskit/qiskit-aer/issues/1798. - -.. releasenotes/notes/use_omp_set_max_active_levels-7e6c1d301c4434a6.yaml @ b'462bade1f131c55f25dbcbcad7f6173c91180c07' - -- Aer will now use ``omp_set_max_active_levels()`` instead of the deprecated ``omp_set_nested()`` when compiled against recent versions of OpenMP. - - -IBM Q Provider 0.20.2 -===================== - -No change. - - -############# -Qiskit 0.43.1 -############# - -.. _Release Notes_Terra_0.24.1: - -Terra 0.24.1 -============ - -.. _Release Notes_Terra_0.24.1_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.24.1-388ee1564c4b7888.yaml @ b'e0c061d3f6cd6d5911be1bd1903a67d4a1c2d65a' - -Qiskit Terra 0.24.1 is the first patch release to 0.24.0. This fixes some bugs that have been discovered since the release of 0.24.0. - - -.. _Release Notes_Terra_0.24.1_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml @ b'615e42b41f9107ce0e61fd52a5704ff3d1b11708' - -- Changed :meth:`.QuantumCircuit.assign_parameters` to bind - assigned integer and float values directly into the parameters of - :class:`~qiskit.circuit.Instruction` instances in the circuit rather than - binding the values wrapped within a - :class:`~qiskit.circuit.ParameterExpression`. This change should have - little user impact as ``float(QuantumCircuit.data[i].operation.params[j])`` - still produces a ``float`` (and is the only way to access the value of a - :class:`~qiskit.circuit.ParameterExpression`). Also, - :meth:`~qiskit.circuit.Instruction` parameters could already be ``float`` - as well as a :class:`~qiskit.circuit.ParameterExpression`, so code dealing - with instruction parameters should already handle both cases. The most - likely chance for user impact is in code that uses ``isinstance`` to check - for :class:`~qiskit.circuit.ParameterExpression` and behaves differently - depending on the result. Additionally, qpy serializes the numeric value in - a bound :class:`~qiskit.circuit.ParameterExpression` at a different - precision than a ``float`` (see also the related bug fix note about - :meth:`.QuantumCircuit.assign_parameters`). - - -.. _Release Notes_Terra_0.24.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/433_qubit_coordinates_map-8abc318fefdb99ac.yaml @ b'e0c061d3f6cd6d5911be1bd1903a67d4a1c2d65a' - -- Updated :func:`~qiskit.visualization.plot_gate_map`, :func:`~qiskit.visualization.plot_error_map`, and - :func:`~qiskit.visualization.plot_circuit_layout` to support 433 qubit heavy-hex coupling maps. This - allows coupling map visualizations for IBM Quantum's ``ibm_seattle`` - backend. - -.. releasenotes/notes/circuit-assign-parameter-to-concrete-value-7cad75c97183257f.yaml @ b'615e42b41f9107ce0e61fd52a5704ff3d1b11708' - -- Changed the binding of numeric values with - :meth:`.QuantumCircuit.assign_parameters` to avoid a mismatch between the - values of circuit instruction parameters and corresponding parameter keys - in the circuit's calibration dictionary. Fixed `#9764 - `_ and `#10166 - `_. See also the - related upgrade note regarding :meth:`.QuantumCircuit.assign_parameters`. - -.. releasenotes/notes/fix-collapse-with-clbits-e14766353303d442.yaml @ b'5f39f6bc9e27de6da11a7df636dcb54e2e6d478b' - -- Fixed a bug in :class:`~BlockCollapser` where classical bits were ignored when collapsing - a block of nodes. - -.. releasenotes/notes/fix-collapse-with-clbits-e14766353303d442.yaml @ b'5f39f6bc9e27de6da11a7df636dcb54e2e6d478b' - -- Fixed a bug in :meth:`~qiskit.dagcircuit.DAGCircuit.replace_block_with_op` and - :meth:`~qiskit.dagcircuit.DAGDependency.replace_block_with_op` - that led to ignoring classical bits. - -.. releasenotes/notes/fix-compose-switch-19ada3828d939353.yaml @ b'e0c061d3f6cd6d5911be1bd1903a67d4a1c2d65a' - -- Fixed a bug in :meth:`.QuantumCircuit.compose` where the :attr:`.SwitchCaseOp.target` attribute - in the subcircuit was not correctly mapped to a register in the base circuit. - -.. releasenotes/notes/fix-exception-decription-3ba0b5db82c576cf.yaml @ b'e0c061d3f6cd6d5911be1bd1903a67d4a1c2d65a' - -- Fix a bug in :class:`~.RZXCalibrationBuilder` where calling calibration with wrong parameters would crash instead of raising an exception. - -.. releasenotes/notes/fix-exception-from_dimacs_file-b9338f3c913a9bff.yaml @ b'ab409594400b6a956d26bc67fed9330fff7097f0' - -- Fixed an issue with the :meth:`.BooleanExpression.from_dimacs_file` - constructor method where the exception type raised when tweedledum wasn't - installed was not the expected :class:`~.MissingOptionalLibrary`. - Fixed `#10079 `__ - -.. releasenotes/notes/fix-initial_layout-loose-qubits-0c59b2d6fb99d7e6.yaml @ b'4341de705d2d6b06da934f8ef933c103a7b4f554' - -- Using ``initial_layout`` in calls to :func:`.transpile` will no longer error if the - circuit contains qubits not in any registers, or qubits that exist in more than one - register. See `#10125 `__. - -.. releasenotes/notes/fix-mcrz-relative-phase-6ea81a369f8bda38.yaml @ b'c9656b23bd2f4d9b1ced10bc2be7e006a00e445f' - -- Fixed the gate decomposition of multi-controlled Z rotation gates added via - :meth:`.QuantumCircuit.mcrz`. Previously, this method implemented a multi-controlled - phase gate, which has a relative phase difference to the Z rotation. To obtain the - previous :meth:`.QuantumCircuit.mcrz` behaviour, use :meth:`.QuantumCircuit.mcp`. - -.. releasenotes/notes/fix-pm-config-from-backend-f3b71b11858b4f08.yaml @ b'48f26a0c1eaf52a2c6010dabe5758506ef56d1bc' - -- Fixed an issue with the :meth:`.PassManagerConfig.from_backend` constructor - when building a :class:`~.PassManagerConfig` object from a :class:`~.BackendV1` - instance that didn't have a coupling map attribute defined. Previously, the - constructor would incorrectly create a :class:`~.CouplingMap` object with - 0 qubits instead of using ``None``. - Fixed `#10171 `__ - -.. releasenotes/notes/fix-synth-fail-with-symbolic-angles-a070b9973a16b8c3.yaml @ b'4e71247348955fff02a10794551ebabeae600291' - -- Fixes a bug introduced in Qiskit 0.24.0 where numeric rotation angles were no longer substituted - for symbolic ones before preparing for two-qubit synthesis. This caused an exception to be - raised because the synthesis routines require numberic matrices. - -.. releasenotes/notes/fix-transpile-pickle-4045805b67c0c11b.yaml @ b'99b1569944ced6b7e58ada3c411299b4ef5356dc' - -- Fix a bug in which running :class:`~.Optimize1qGatesDecomposition` in parallel would raise an error due to OneQubitGateErrorMap not being picklable. - -.. releasenotes/notes/fix-vf2-scoring-1q-e2ac29075831d64d.yaml @ b'e0c061d3f6cd6d5911be1bd1903a67d4a1c2d65a' - -- Fix a bug in the :class:`~.VF2Layout` and :class:`~.VF2PostLayout` passes - where the passes were failing to account for the 1 qubit error component when - evaluating a potential layout. - -Aer 0.12.0 -========== - -No change - -IBM Q Provider 0.20.2 -===================== - -No change - -############# -Qiskit 0.43.0 -############# - -.. _release Notes_Terra_0.24.0: - -Terra 0.24.0 -============ - -.. _Release Notes_0.24.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.24-423de82722d2e31a.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -This is a major feature release that includes numerous new features -and bugfixes. - -This release is the final release with support for running Qiskit -with Python 3.7. Starting in the next minor version release Python >=3.8 will -be required to run Qiskit. - -The highlights of this release: - -QuantumInstance, OpFlow, and algorithms usage deprecation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This release officially deprecates the :class:`~.QuantumInstance` class (and -its associated helper methods and classes), the :mod:`qiskit.opflow` module, -and any usage of those in :mod:`qiskit.algorithms`. This deprecation comes -from a long thread of work that started in Qiskit Terra 0.21.0 to refactor -the :mod:`qiskit.algorithms` module to be based on the computational -:mod:`~qiskit.primitives`. There are associated migration guides for any -existing users to migrate to the new workflow: - - * ``QuantumInstance`` migration guide: https://qisk.it/qi_migration - * ``Opflow`` migration guide: https://qisk.it/opflow_migration - * Algorithms migration guide: https://qisk.it/algo_migration - -OpenQASM2 improvements -^^^^^^^^^^^^^^^^^^^^^^ - -This release includes a major refactoring for the OpenQASM 2.0 support -in Qiskit. The first change is the introduction of a new parser for OpenQASM -2.0 in the :mod:`qiskit.qasm2` module. This new module replaces the -existing :mod:`qiskit.qasm` module. The new parser is more explicit and -correct with respect to the language specification. It is also implemented in -Rust and is significantly faster than the previous parser. Paired with the -new parser the OpenQASM 2.0 exporter underwent a large refactor that -improved the correctness of the output when using the -:meth:`.QuantumCircuit.qasm` method to generate QASM output from a -:class:`~.QuantumCircuit` object. - -Transpiler support for devices with disjoint connectivity -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The transpiler now supports targeting backends with disjoint connectivity. -Previously, the transpiler only supported backends which were fully connected -(where there is a path to run operations between all pairs of qubits in the -backend). Now, if a backend has disconnected connectivity the transpiler is -able to reason about how to apply layout (:ref:`layout_stage`) and -routing (:ref:`routing_stage`) for the backend. If the input circuit is -not able to be executed on the hardware given the lack of connectivity between -connected components, a descriptive error will be returned. - -For example, the Heron device outlined in IBM Quantum's -`hardware roadmap `__ -describes a future backend which will have shared control hardware -and real-time classical communication between separate quantum processors. -This support enables the :class:`~.Target` to accurately model these types -of future devices or other hardware with similar constraints. - -Switch Operation -^^^^^^^^^^^^^^^^ - -This release adds a new control flow operation, the switch statement. This is -implemented using a new operation class :class:`~.SwitchCaseOp` and the -:meth:`.QuantumCircuit.switch` method. This allows switching on a numeric -input (such as a classical register or bit) and executing the circuit that -corresponds to the matching value. - -.. _Release Notes_0.24.0_New Features: - -New Features ------------- - -.. releasenotes/notes/0.24/new-deprecation-utilities-066aff05e221d7b1.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added the functions :func:`~.add_deprecation_to_docstring`, - :func:`~.deprecate_arg`, and :func:`~.deprecate_func` to the :mod:`qiskit.utils` module. - - :func:`~.add_deprecation_to_docstring` will rewrite the function's docstring to include a - Sphinx ``.. deprecated::`` directive so that the deprecation shows up in docs and with - ``help()``. The deprecation decorators from :mod:`qiskit.utils` call - :func:`~.add_deprecation_to_docstring` already for you; but you can call it directly if you - are using different mechanisms for deprecations. - - ``@deprecate_func`` replaces ``@deprecate_function`` and is used to deprecate an entire - function. It will auto-generate most of the deprecation message for you. - - ``@deprecate_arg`` replaces ``@deprecate_arguments`` and is used to deprecate an - argument on a function. It will generate a more useful message than the previous function. - It is also more flexible, for example it allows setting a ``predicate`` so that you only - deprecate certain situations, such as using a deprecated value or data type. - - -.. _Release Notes_0.24.0_Transpiler Features: - -Transpiler Features -^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/add-alternative-hls-construction-afec157f7cf15b0b.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added an alternative way to specify in :class:`~.HLSConfig` the list of - synthesis methods used for a given high-level object. - As before, a synthesis method can be specified as a tuple consisting of - the name of the method and additional arguments. Additionally, a synthesis method - can be specified as a tuple consisting of an instance of :class:`.HighLevelSynthesisPlugin` - and additional arguments. Moreover, when there are no additional arguments, a synthesis - method can be specified simply by name or by an instance of :class:`.HighLevelSynthesisPlugin`. - The following example illustrates the new functionality:: - - from qiskit import QuantumCircuit - from qiskit.circuit.library.generalized_gates import PermutationGate - from qiskit.transpiler import PassManager - from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig, HighLevelSynthesis - from qiskit.transpiler.passes.synthesis.high_level_synthesis import ACGSynthesisPermutation - - qc = QuantumCircuit(6) - qc.append(PermutationGate([1, 2, 3, 0]), [1, 2, 3, 4]) - - # All of the ways to specify hls_config are equivalent - hls_config = HLSConfig(permutation=[("acg", {})]) - hls_config = HLSConfig(permutation=["acg"]) - hls_config = HLSConfig(permutation=[(ACGSynthesisPermutation(), {})]) - hls_config = HLSConfig(permutation=[ACGSynthesisPermutation()]) - - # The hls_config can then be passed as an argument to HighLevelSynthesis - pm = PassManager(HighLevelSynthesis(hls_config=hls_config)) - qc_synthesized = pm.run(qc) - -.. releasenotes/notes/0.24/add-cmap-componets-7ed56cdf294150f1.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added support to the :class:`~.CouplingMap` object to have a disjoint - connectivity. Previously, a :class:`~.CouplingMap` could only be - constructed if the graph was connected. This will enable using - :class:`~.CouplingMap` to represent hardware with disjoint qubits, such as hardware - with qubits on multiple separate chips. - -.. releasenotes/notes/0.24/add-cmap-componets-7ed56cdf294150f1.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new method :meth:`.CouplingMap.connected_components` which - is used to get a list of :class:`~.CouplingMap` component subgraphs for - a disjoint :class:`~.CouplingMap`. If the :class:`~.CouplingMap` object - is connected this will just return a single :class:`~.CouplingMap` - equivalent to the original. - -.. releasenotes/notes/0.24/add-ecr-sx-equivalences-5b6fe73ec599d1a4.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added new rules to the built-in :class:`.EquivalenceLibrary` instance: - ``qiskit.circuit.equivalence_library.SessionEquivalenceLibrary``. The - new rules added are: - - * :class:`.CXGate` into :class:`.ECRGate` and 1-qubit Clifford gates - (up to a global phase). - * :class:`.HGate` into :class:`.SXGate` and :class:`.SGate` (up to a - global phase). - * :class:`.HGate` into :class:`.SXdgGate` and :class:`.SdgGate` (up to a - global phase). - -.. releasenotes/notes/0.24/add-hls-plugins-038388970ad43c55.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added high-level-synthesis plugins for :class:`.LinearFunction` and for - :class:`qiskit.quantum_info.Clifford`, extending the set of synthesis - methods that can be called from :class:`~qiskit.transpiler.passes.HighLevelSynthesis` - transpiler pass. - - For :class:`.LinearFunction` the available plugins are listed below: - - .. list-table:: - :header-rows: 1 - - * - Plugin name - - High-level synthesis plugin - * - ``default`` - - :class:`.DefaultSynthesisLinearFunction` - * - ``kms`` - - :class:`.KMSSynthesisLinearFunction` - * - ``pmh`` - - :class:`.PMHSynthesisLinearFunction` - - For :class:`qiskit.quantum_info.Clifford` the available plugins are listed below: - - .. list-table:: - :header-rows: 1 - - * - Plugin name - - High-level synthesis plugin - * - ``default`` - - :class:`.DefaultSynthesisClifford` - * - ``ag`` - - :class:`.AGSynthesisClifford` - * - ``bm`` - - :class:`.BMSynthesisClifford` - * - ``greedy`` - - :class:`.GreedySynthesisClifford` - * - ``layers`` - - :class:`.LayerSynthesisClifford` - * - ``lnn`` - - :class:`.LayerLnnSynthesisClifford` - - Please refer to :mod:`qiskit.synthesis` documentation for more information - about each individual method. - - The following example illustrates some of the new plugins:: - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import LinearFunction - from qiskit.quantum_info import Clifford - from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig, HighLevelSynthesis - - # Create a quantum circuit with one linear function and one clifford - qc1 = QuantumCircuit(3) - qc1.cx(0, 1) - qc1.swap(0, 2) - lin_fun = LinearFunction(qc1) - - qc2 = QuantumCircuit(3) - qc2.h(0) - qc2.cx(0, 2) - cliff = Clifford(qc2) - - qc = QuantumCircuit(4) - qc.append(lin_fun, [0, 1, 2]) - qc.append(cliff, [1, 2, 3]) - - # Choose synthesis methods that adhere to linear-nearest-neighbour connectivity - hls_config = HLSConfig(linear_function=["kms"], clifford=["lnn"]) - - # Synthesize - qct = HighLevelSynthesis(hls_config)(qc) - print(qct.decompose()) - -.. releasenotes/notes/0.24/add-minimum-point-pass-09cf9a9eec86fd48.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new transpiler pass, :class:`~.MinimumPoint` which is used primarily as - a pass to check a loop condition in a :class:`~.PassManager`. This pass will - track the state of fields in the property set over its past executions and set - a boolean field when either a fixed point is reached over the backtracking depth - or selecting the minimum value found if the backtracking depth is reached. This - is an alternative to the :class:`~.FixedPoint` which simply checks for a fixed - value in a property set field between subsequent executions. - -.. releasenotes/notes/0.24/add-swap_nodes-to-dagcircuit-methods-2964426f02251fc4.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new method, :meth:`~.DAGCircuit.swap_nodes`, to the - :class:`~.DAGCircuit` to allow swapping nodes which are partially - connected. Partially connected here means that the two nodes share at - least one edge (which represents a qubit or clbit). If the nodes do not - share any edges a :class:`~.DAGCircuitError` is raised. - -.. releasenotes/notes/0.24/clifford_cz_lnn_synthesis-4b0328b581749df5.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Add a new synthesis algorithm :func:`~.synth_cz_depth_line_mr` of a CZ circuit - for linear nearest neighbor (LNN) connectivity in 2-qubit depth of 2n+2 - using CX and phase gates (S, Sdg or Z). The synthesized circuit reverts - the order of the qubits. The synthesis algorithm is based on the paper of Maslov and Roetteler - (https://arxiv.org/abs/1705.09176). - -.. releasenotes/notes/0.24/clifford_cz_lnn_synthesis-4b0328b581749df5.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Add a new synthesis algorithm :func:`~.synth_clifford_depth_lnn` of a Clifford circuit - for LNN connectivity in 2-qubit depth of 9n+4 (which is still not optimal), - using the layered Clifford synthesis (:func:`~.synth_clifford_layers`), - :func:`~.synth_cnot_depth_line_kms` to synthesize the CX layer in depth 5n, - and :func:`~.synth_cz_depth_line_mr` to synthesize each of the CZ layers in depth 2n+2. - This PR will be followed by another PR based on the recent paper of Maslov and Yang - (https://arxiv.org/abs/2210.16195), that synthesizes the CX-CZ layers in depth 5n - for LNN connectivity and performs further optimization, and hence reduces the depth - of a Clifford circuit to 7n-4 for LNN connectivity. - -.. releasenotes/notes/0.24/crx-equivalences-cc9e5c98bb73fd49.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Equivalences between the controlled Pauli rotations and translations to two-Pauli rotations - are now available in the equivalence library for Qiskit standard gates. This allows, - for example, to translate a :class:`.CRZGate` to a :class:`.RZZGate` plus :class:`.RZGate` - or a :class:`.CRYGate` to a single :class:`.RZXGate` plus single qubit gates:: - - from qiskit.circuit import QuantumCircuit - from qiskit.compiler import transpile - - angle = 0.123 - circuit = QuantumCircuit(2) - circuit.cry(angle, 0, 1) - - basis = ["id", "sx", "x", "rz", "rzx"] - transpiled = transpile(circuit, basis_gates=basis) - print(transpiled.draw()) - -.. releasenotes/notes/0.24/deepcopy-option-circuit_to_dag-1d494b7f9824ec93.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new option, ``copy_operations``, to :func:`~.circuit_to_dag` to - enable optionally disabling deep copying the operations from the input - :class:`~.QuantumCircuit` to the output :class:`~.QuantumCircuit`. In cases - where the input :class`~.QuantumCircuit` is not used anymore after - conversion this deep copying is unnecessary overhead as any shared - references wouldn't have any potential unwanted side effects if the input - :class:`~.QuantumCircuit` is discarded. - -.. releasenotes/notes/0.24/deepcopy-option-dag_to_circuit-2974aa9e66dc7643.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new option, ``copy_operations``, to :func:`~.dag_to_circuit` to - enable optionally disabling deep copying the operations from the input - :class:`~.DAGCircuit` to the output :class:`~.QuantumCircuit`. In cases - where the input :class:`~.DAGCircuit` is not used anymore after conversion - this deep copying is unnecessary overhead as any shared references wouldn't - have any potential unwanted side effects if the input :class:`~.DAGCircuit` - is discarded. - -.. releasenotes/notes/0.24/entry_point_obj-60625d9d797df1d9.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new function :func:`~.passmanager_stage_plugins` to the - :mod:`qiskit.transpiler.preset_passmanagers.plugin` module. This function - is used to obtain a mapping from plugin names to their their class type. - This enables identifying and querying any defined pass manager stage plugin's - documentation. For example:: - - >>> from qiskit.transpiler.preset_passmanagers.plugin import passmanager_stage_plugins - >>> passmanager_stage_plugins('routing')['lookahead'].__class__ - - qiskit.transpiler.preset_passmanagers.builtin_plugins.LookaheadSwapPassManager - - >>> help(passmanager_stage_plugins('routing')['lookahead']) - Help on BasicSwapPassManager in module qiskit.transpiler.preset_passmanagers.builtin_plugins object: - - class BasicSwapPassManager(qiskit.transpiler.preset_passmanagers.plugin.PassManagerStagePlugin) - | Plugin class for routing stage with :class:`~.BasicSwap` - ... - -.. releasenotes/notes/0.24/error-pass-callable-message-3f29f09b9faba736.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The transpiler pass :class:`~.Error` now also accepts callable - inputs for its ``msg`` parameter. If used these input callables will be passed - the ``property_set`` attribute of the pass and are expected to return a string - which will be used for the error message when the pass is run. For example:: - - from qiskit.transpiler.passes import Error - - def error_message(property_set): - - size = property_set["size'] - return f"The circuit size is: {size}" - - error_pass = Error(error_message) - - When ``error_pass`` is included in a pass manager it will error using the - message ``"The circuit size is: n"`` where ``n`` is the circuit size set - in the property set (typically from the previous execution of the - :class:`~.Size` pass). - -.. releasenotes/notes/0.24/filter-idle-qubits-cmap-74ac7711fc7476f3.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :meth:`~.Target.build_coupling_map` method has a new keyword argument, - ``filter_idle_qubits`` which when set to ``True`` will remove any qubits - from the output :class:`~.CouplingMap` that don't support any operations. - -.. releasenotes/notes/0.24/gate-direction-swap-885b6f8ba9779853.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`.GateDirection` transpiler pass can now correctly handle - :class:`~.SwapGate` instances that may be present in the circuit when - executing on a circuit. In these cases if the swap gate's qubit arguments - are on the non-native direction of an edge, the pass will flip the argument - order. - -.. releasenotes/notes/0.24/include-ecr-gates-for-pulse-scaling-8369eb584c6d8fe1.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`~.RZXCalibrationBuilder` and :class:`~.RZXCalibrationBuilderNoEcho` transpiler passes now will correctly use - an :class:`~.ECRGate` for the entangling gate if the backend's native - entangling gate is :class:`~.ECRGate`. Previously, the passes would only - function correctly if the entangling gate was :class:`~.CXGate`. - -.. releasenotes/notes/0.24/new-constructor-target-from-configuration-91f7eb569d95b330.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new constructor for the :class:`~.Target` class, - :meth:`.Target.from_configuration`, which lets you construct a - :class:`~.Target` object from the separate object types for describing - the constraints of a backend (e.g. basis gates, :class:`~.CouplingMap`, - :class:`~.BackendProperties`, etc). For example:: - - target = Target.from_configuration( - basis_gates=["u", "cx", "measure"], - coupling_map=CouplingMap.from_line(25), - ) - - This will construct a :class:`~.Target` object that has :class:`~.UGate`, - :class:`~.CXGate`, and :class:`~.Measure` globally available on 25 qubits - which are connected in a line. - -.. releasenotes/notes/0.24/rename-graysynth-3fa4fcb7d096ab35.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new function :func:`~.synth_cnot_phase_aam` - which is used to synthesize cnot phase circuits for all-to-all architectures - using the Amy, Azimzadeh, and Mosca method. This function is identical to - the available ``qiskit.transpiler.synthesis.graysynth()`` - function but has a more descriptive name and is more logically placed - in the package tree. This new function supersedes the legacy function - which will likely be deprecated in a future release. - -.. releasenotes/notes/0.24/sabre-sort-rng-056f26f205e38bab.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Internal tweaks to the routing algorithm in :class:`.SabreSwap`, used in - transpilation of non-dynamic circuits at all non-zero optimization levels, - have sped up routing for very large circuits. For example, the time to - route a depth-5 :class:`.QuantumVolume` circuit for a 1081-qubit heavy-hex - coupling map is approximately halved. - -.. releasenotes/notes/0.24/speedup-one-qubit-optimize-pass-483429af948a415e.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The runtime performance of the :class:`~.Optimize1qGatesDecomposition` - transpiler pass has been significantly improved. This was done by both - rewriting all the computation for the pass in Rust and also decreasing - the amount of intermediate objects created as part of the pass's - execution. This should also correspond to a similar improvement - in the runtime performance of :func:`~.transpile` with the - ``optimization_level`` keyword argument set to ``1``, ``2``, or ``3``. - -.. releasenotes/notes/0.24/stabilizer_state_synthesis-c48c0389941715a6.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Add a new synthesis method :func:`~.synth_stabilizer_layers` of a stabilizer state into layers. - It provides a similar decomposition to the synthesis described in Lemma 8 of Bravyi and Maslov, - (arxiv:2003.09412) without the initial Hadamard-free sub-circuit which does not affect the stabilizer state. - -.. releasenotes/notes/0.24/stabilizer_state_synthesis-c48c0389941715a6.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Add a new synthesis method :func:`~.synth_stabilizer_lnn` of a stabilizer state - for linear nearest neighbor connectivity in 2-qubit depth of 2n+2 and two distinct CX layers, - using CX and phase gates (S, Sdg or Z). - The synthesis algorithm is based on the paper of Maslov and Roetteler (https://arxiv.org/abs/1705.09176). - -.. releasenotes/notes/0.24/support-disjoint-cmap-sabre-551ae4295131a449.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`~.SabreLayout` pass now supports running against a target - with a disjoint :class:`~.CouplingMap`. When targeting a disjoint coupling - the input :class:`.DAGCircuit` is split into its connected components of - virtual qubits, each component is mapped to the connected components - of the :class:`~.CouplingMap`, layout is run on each connected - component in isolation, and then all layouts are combined and returned. - Note when the ``routing_pass`` argument is set the pass doesn't - support running with disjoint connectivity. - -.. releasenotes/notes/0.24/target-aware-layout-routing-2b39bd87a9f928e7.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The following layout and routing transpiler passes from the - :mod:`.qiskit.transpiler.passes` modules now will support accepting a - :class:`~.Target` object which is used to model the constraints of a target - backend via the first positional argument (currently named either - ``coupling_map`` or ``backend_properties``). - - The list of passes with the new support for :class:`~.Target` input are: - - * :class:`~.CSPLayout` - * :class:`~.FullAncillaAllocation` - * :class:`~.Layout2qDistance` - * :class:`~.NoiseAdaptiveLayout` - * :class:`~.SabreLayout` - * :class:`~.TrivialLayout` - * :class:`~.BasicSwap` - * :class:`~.BIPMapping` - * :class:`~.LayoutTransformation` - * :class:`~.LookaheadSwap` - * :class:`~.SabreSwap` - * :class:`~.StochasticSwap` - * :class:`~.CheckMap` - -.. releasenotes/notes/0.24/target-aware-layout-routing-2b39bd87a9f928e7.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The pass manager construction helper function :func:`~.generate_embed_passmanager` - will now also accept a :class:`~.Target` for it's sole positional argument - (currently named ``coupling_map``). This can be used to construct a layout - embedding :class:`~.PassManager` from a :class:`~.Target` object instead of - from a :class:`~.CouplingMap`. - -.. releasenotes/notes/0.24/target-transpile-d029922a5dbc3a52.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The following layout and routing transpiler passes from the - :mod:`.qiskit.transpiler.passes` modules have a new keyword argument, - ``target`` which takes in a :class:`~.Target` object which is used to model - the constraints of a target backend. If the ``target`` keyword argument is - specified it will be used as the source of truth for any hardware - constraints used in the operation of the transpiler pass. It will - supersede any other arguments for specifying hardware constraints, - typically those arguments which take a :class:`~.CouplingMap`, - :class:`~.InstructionScheduleMap` or a basis gate list. - The list of these passes with the new ``target`` argument are: - - * :class:`~.Unroller` - * :class:`~.PulseGate` - * :class:`~.RZXCalibrationBuilder` - * :class:`~.CommutativeCancellation` - * :class:`~.EchoRZXWeylDecomposition` - * :class:`~.Optimize1qGatesSimpleCommutation` - * :class:`~.Optimize1qGates` - * :class:`~.CheckCXDirection` - * :class:`~.ALAPSchedule` - * :class:`~.ASAPSchedule` - * :class:`~.ALAPScheduleAnalysis` - * :class:`~.ASAPScheduleAnalysis` - * :class:`~.DynamicalDecoupling` - * :class:`~.PadDynamicalDecoupling` - * :class:`~.TimeUnitConversion` - -.. releasenotes/notes/0.24/target-transpile-d029922a5dbc3a52.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The pass manager construction helper function :func:`~.generate_scheduling` - has a new keyword argument ``target`` which is used to specify a :class:`~.Target` - object to model the constraints of the target backend being compiled for - when generating a new :class:`~.PassManager`. If specified this new argument will - supersede the other argument ``inst_map``. - -.. releasenotes/notes/0.24/unitary-synthesis-based-on-errors-8041fcc9584f5db2.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The ``default`` plugin used by the :class:`~.UnitarySynthesis` transpiler - pass now chooses one and two-qubit unitary synthesis based on the error rates - reported in the :class:`~.Target`. In particular, it runs all possible synthesis - methods supported by the plugin and chooses the option which will result in the - lowest error. For a one-qubit decomposition, it can target Pauli basis - (e.g. RZ-RX-RZ or RZ-RY-RZ), generic unitary basis (e.g. U), and a - few others. For a two-qubit decomposition, it can target any supercontrolled basis - (e.g. CNOT, iSWAP, B) or multiple controlled basis - (e.g. CZ, CH, ZZ^.5, ZX^.2, etc.). - -.. releasenotes/notes/0.24/unitary-synthesis-based-on-errors-8041fcc9584f5db2.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The interface for :class:`~.UnitarySynthesisPlugin` has two new - optional properties ``supports_gate_lengths_by_qubit`` and - ``supports_gate_errors_by_qubit`` which when set will add the - fields ``gate_lengths_by_qubit`` and ``gate_errors_by_qubit`` - respectively to the input options to the plugin's ``run()`` method. - These new fields are an alternative view of the data provided by - ``gate_lengths`` and ``gate_errors`` but instead have the form: - ``{(qubits,): [Gate, length]}`` (where ``Gate`` is the instance - of :class:`~.Gate` for that definition). This allows plugins to - reason about working with gates of the same type but but that - have different parameters set. - -.. releasenotes/notes/0.24/unroll-forloops-7bf8000620f738e7.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new transpiler pass, :class:`~.UnrollForLoops`, which is used - to unroll any :class:`~.ForLoopOp` operations in a circuit. This pass - unrolls for-loops when possible, if there are no - :class:`~.ContinueLoopOp` or :class:`~.BreakLoopOp` inside the body - block of the loop. For example: - - .. plot:: - :include-source: - - from qiskit.transpiler.passes import UnrollForLoops - from qiskit import QuantumCircuit - - unroll_pass = UnrollForLoops() - - qc = QuantumCircuit(1) - # For loop over range 5 - with qc.for_loop(range(5)) as i: - qc.rx(i, 0) - # Unroll loop into 5 rx gates - unroll_pass(qc).draw("mpl") - -.. releasenotes/notes/0.24/vf2-post-layout-max-trials-98b1c736e2e33861.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new parameter ``max_trials`` to pass :class:`~.VF2PostLayout` - which, when specified, limits the number of layouts discovered and - compared when searching for the best layout. - This differs from existing parameters ``call_limit`` and ``time_limit`` - (which are used to limit the number of state visits performed by the VF2 - algorithm and the total time spent by pass :class:`~.VF2PostLayout`, - respectively) in that it is used to place an upper bound on the time - spent scoring potential layouts, which may be useful for larger - devices. - -.. releasenotes/notes/0.24/vf2postlayout-fix-16bb54d9bdf3aaf6.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`~.CheckMap` transpiler pass has a new keyword argument on its - constructor, ``property_set_field``. This argument can be used to specify - a field in the property set to store the results of the analysis. - Previously, it was only possible to store the result in the field - ``"is_swap_mapped"`` (which is the default). This enables you to store the - result of multiple instances of the pass in a :class:`~.PassManager` in - different fields. - - -.. _Release Notes_0.24.0_Circuits Features: - -Circuits Features -^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/add-global-phase-gate-b52c5b25ab8a3cf6.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new gate class, :class:`.GlobalPhaseGate`, which can be used to add - a global phase on the :class:`.QuantumCircuit` instance. - -.. releasenotes/notes/0.24/add-layout-attribute-c84e56c08ca93ada.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new attribute, :attr:`~.QuantumCircuit.layout`, to the - :class:`~.QuantumCircuit` class. This attribute is typically populated - by :func:`~.transpile` or :meth:`.PassManager.run` (when the - :ref:`layout_stage` and :ref:`routing_stage` are run in the - :class:`~.PassManager`) and contains a :class:`~.TranspileLayout` which - contains the information about the permutation of the input circuit - during :func:`~.transpile`. - -.. releasenotes/notes/0.24/expression-var-order-d87e9b04fb5d545c.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new argument, ``var_order``, to the :class:`~.PhaseOracle` class's constructor to - enable setting the order in which the variables in the logical expression are being - considered. For example:: - - from qiskit.tools.visualization import plot_histogram - from qiskit.primitives import Sampler - from qiskit.circuit.library import PhaseOracle - from qiskit.algorithms import Grover, AmplificationProblem - - oracle = PhaseOracle('((A & C) | (B & D)) & ~(C & D)', var_order=['A', 'B', 'C', 'D']) - problem = AmplificationProblem(oracle=oracle, is_good_state=oracle.evaluate_bitstring) - grover = Grover(sampler=Sampler()) - result = grover.amplify(problem) - print(result.circuit_results[0]) - -.. releasenotes/notes/0.24/qasm2-parser-rust-ecf6570e2d445a94.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- A new OpenQASM 2 parser is available in :mod:`qiskit.qasm2`. This has two entry points: - :func:`.qasm2.load` and :func:`.qasm2.loads`, for reading the source code from a file and from a - string, respectively:: - - import qiskit.qasm2 - program = """ - OPENQASM 2.0; - include "qelib1.inc"; - qreg q[2]; - h q[0]; - cx q[0], q[1]; - """ - bell = qiskit.qasm2.loads(program) - - This new parser is approximately 10x faster than the existing ones at - :meth:`.QuantumCircuit.from_qasm_file` and :meth:`.QuantumCircuit.from_qasm_str` for large files, - and has less overhead on each call as well. The new parser is more extensible, customisable and - generally also more type-safe; it will not attempt to output custom Qiskit objects when the - definition in the OpenQASM 2 file clashes with the Qiskit object, unlike the current exporter. - See the :mod:`qiskit.qasm2` module documentation for full details and more examples. - -.. releasenotes/notes/0.24/su2-synthesis-295ebf03d9033263.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Improve the decomposition of multi-controlled Pauli-X and Pauli-Y rotations - with :meth:`.QuantumCircuit.mcrx` and :meth:`.QuantumCircuit.mcry on - :math:`n` controls to :math:`16n - 40` CX gates, for :math:`n \geq 4`. This improvement is based - on `arXiv:2302.06377 `__. - -.. releasenotes/notes/0.24/switch-case-9b6611d0603d36c0.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Qiskit now supports the representation of ``switch`` statements, using the new :class:`.SwitchCaseOp` - instruction and the :meth:`.QuantumCircuit.switch` method. This allows switching on a numeric - input (such as a classical register or bit) and executing the circuit that corresponds to the - matching value. Multiple values can point to the same circuit, and :data:`.CASE_DEFAULT` can be - used as an always-matching label. - - You can also use a builder interface, similar to the other control-flow constructs to build up - these switch statements:: - - from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister - - qreg = QuantumRegister(2) - creg = ClassicalRegister(2) - qc = QuantumCircuit(qreg, creg) - - qc.h([0, 1]) - qc.measure([0, 1], [0, 1]) - with qc.switch(creg) as case: - with case(0): # if the register is '00' - qc.z(0) - with case(1, 2): # if the register is '01' or '10' - qc.cx(0, 1) - with case(case.DEFAULT): # the default case - qc.h(0) - - The ``switch`` statement has support throughout the Qiskit compiler stack; you can - :func:`.transpile` circuits containing it (if the backend advertises its support for the - construct), and it will serialize to QPY. - - The ``switch`` statement is not currently a feature of OpenQASM 3, but `it is under active - design and consideration `__, which is - expected to be adopted in the near future. Qiskit Terra has experimental support for - exporting this statement to the OpenQASM 3 syntax proposed in the linked pull request, using - an experimental feature flag. To export a ``switch`` statement circuit (such as the one - created above) to OpenQASM 3 using this speculative support, do:: - - from qiskit import qasm3 - - qasm3.dumps(qc, experimental=qasm3.ExperimentalFeatures.SWITCH_CASE_V1) - - -.. _Release Notes_0.24.0_Algorithms Features: - -Algorithms Features -^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/adapt-vqe-thresholds-239ed9f250c63e71.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new attribute :attr:`~qiskit.algorithms.minimum_eigensolvers.AdaptVQE.eigenvalue_threshold` - to the :class:`~qiskit.algorithms.minimum_eigensolvers.AdaptVQE` class for - configuring a new kind of threshold to terminate the algorithm once - the eigenvalue changes less than a set value. - -.. releasenotes/notes/0.24/adapt-vqe-thresholds-239ed9f250c63e71.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new attribute :attr:`~qiskit.algorithms.minimum_eigensolvers.AdaptVQE.gradient_threshold` - to the :class:`~qiskit.algorithms.minimum_eigensolvers.AdaptVQE` class - which will replace the :attr:`~qiskit.algorithms.minimum_eigensolvers.AdaptVQE.threshold` in the - future. This new attribute behaves the same as the existing ``threshold`` attribute but has a more - accurate name, given the introduction of additional threshold options - in the class. - -.. releasenotes/notes/0.24/ae-warns-on-goodstate-7dbb689ba6a5e5e4.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added the :attr:`.EstimationProblem.has_good_state` attribute, which allows to check - whether an :class:`.EstimationProblem` has a custom :attr:`.EstimationProblem.is_good_state` - or if it is the default. This is useful for checks in amplitude estimators, such as - :class:`.AmplitudeEstimation`, which only support the default implementation. - -.. releasenotes/notes/0.24/computeuncompute-local-fidelity-501fe175762b7593.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Adds a flag ``local`` to the :class:`~.ComputeUncompute` state fidelity class that - allows to compute the local fidelity, which is defined by averaging over - single-qubit projectors. - -.. releasenotes/notes/0.24/rearrange-gradient-result-order-1596e14db01395f5.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Gradient classes rearrange the gradient result according to the order of the input parameters now. - - Example: - - .. code-block:: python - - from qiskit.algorithms.gradients import ParamShiftEstimatorGradient - from qiskit.circuit import QuantumCircuit, Parameter - from qiskit.primitives import Estimator - from qiskit.quantum_info import SparsePauliOp - - # Create a circuit with a parameter - p = {i: Parameter(f'p{i}') for i in range(3)} - qc = QuantumCircuit(1) - qc.rx(p[0], 0) - qc.ry(p[1], 0) - qc.rz(p[2], 0) - op = SparsePauliOp.from_list([("Z", 1)]) - param_values = [0.1, 0.2, 0.3] - - # Create a gradient object - estimator = Estimator() - grad = ParamShiftEstimatorGradient(estimator) - result = grad.run(qc, op, [param_values]).result() - # would produce a gradient of the form [df/dp0, df/dp1, df/dp2] - result = grad.run(qc, op, [param_values], parameters=[[p[2], p[0]]]).result() - # would produce a gradient of the form [df/dp2, df/dp0] - -.. releasenotes/notes/0.24/trotter-time-dep-hamiltonians-8c6c3d5f629e72fb.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added support for handling time-dependent Hamiltonians (i.e. singly - parametrized operators) to the - :class:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE` class. - To facilitate working with this, added the - :attr:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE.num_timesteps` - attribute and a matching keyword argument to the - :class:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE` - constructor to control the number of time steps to divide the full evolution. - -.. releasenotes/notes/0.24/trotter-time-dep-hamiltonians-8c6c3d5f629e72fb.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added support for observable evaluations at every time-step - during the execution of the - :class:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE` class. The - :attr:`.TimeEvolutionProblem.aux_operators` is evaluated at every time - step if the :attr:`.ProductFormula.reps` attribute of the input - ``product_formula`` argument in the constructor is set to 1. - -.. releasenotes/notes/0.24/vqd-list-initial-points-list-optimizers-033d7439f86bbb71.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added extensions to the :class:`~.eigensolvers.VQD` algorithm, which allow - to pass a list of optimizers and initial points for the different - minimization runs. For example, the ``k``-th initial point and - ``k``-th optimizer will be used for the optimization of the - ``k-1``-th exicted state. - - -.. _Release Notes_0.24.0_Quantum Information Features: - -Quantum Information Features -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/add-clifford-from-matrix-3184822cc559e0b7.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added two new constructor methods, :meth:`.Clifford.from_matrix` and - :meth:`.Clifford.from_operator`, that create a :class:`~.Clifford` object - from its unitary matrix and operator representation respectively. - -.. releasenotes/notes/0.24/add-clifford-from-matrix-3184822cc559e0b7.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The constructor of :class:`.Clifford` now can take any Clifford gate object - up to 3 qubits as long it implements a ``to_matrix`` method, - including parameterized gates such as ``Rz(pi/2)``, which were not - convertible before. - -.. releasenotes/notes/0.24/add-commutator-96ef07433e8ca4e7.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added new utility functions: :func:`~qiskit.quantum_info.commutator`, - :func:`~qiskit.quantum_info.anti_commutator`, and - :func:`~qiskit.quantum_info.double_commutator` which are used to compute - commutators for any object implementing the ``LinearOp`` abstract base - class such as :class:`~.QuantumChannel`, :class:`~.SparsePauliOp`, or - :class:`~.ScalarOp`. - -.. releasenotes/notes/0.24/add-equiv-stabilizerstate-6ef8790c765690c1.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added the method :class:`.StabilizerState.equiv`, - that checks if the generating sets of two stabilizer states generate the same stabilizer group. - For example, the stabilizer group of the two-qubit Bell state contains the four elements - :math:`\{II, XX, -YY, ZZ\}` and hence can be generated by either :math:`[XX, ZZ]`, - :math:`[XX, -YY]` or :math:`[-YY, ZZ]`. - -.. releasenotes/notes/0.24/adding-partial-transpose-040a6ff00228841b.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new method, :meth:`~.DensityMatrix.partial_transpose`, to the - :mod:`qiskit.quantum_info` module's :class:`~.DensityMatrix` class. This - method is used to compute the partial transposition of a density matrix, - which is necessary for detecting entanglement between bipartite quantum - systems. - -.. releasenotes/notes/0.24/operator-apply-permutation-c113c05513cb7881.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a method :meth:`qiskit.quantum_info.Operator.apply_permutation` that - pre-composes or post-composes an Operator with a Permutation. This method - works for general qudits. - - Here is an example to calculate :math:`P^\dagger.O.P` which reorders Operator's bits:: - - import numpy as np - from qiskit.quantum_info.operators import Operator - - op = Operator(np.array(range(576)).reshape((24, 24)), input_dims=(2, 3, 4), output_dims=(2, 3, 4)) - perm = [1, 2, 0] - inv_perm = [2, 0, 1] - conjugate_op = op.apply_permutation(inv_perm, front=True).apply_permutation(perm, front=False) - - The conjugate operator has dimensions `(4, 2, 3) x (4, 2, 3)`, which is consistent with permutation - moving qutrit to position 0, qubit to position 1, and the 4-qudit to position 2. - -.. releasenotes/notes/0.24/sparsepauliop-improved-parameter-support-413f7598bac72166.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Natively support the construction of :class:`.SparsePauliOp` objects with - :class:`.ParameterExpression` coefficients, without requiring the explicit construction - of an object-array. Now the following is supported:: - - from qiskit.circuit import Parameter - from qiskit.quantum_info import SparsePauliOp - - x = Parameter("x") - op = SparsePauliOp(["Z", "X"], coeffs=[1, x]) - -.. releasenotes/notes/0.24/sparsepauliop-improved-parameter-support-413f7598bac72166.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added the :meth:`.SparsePauliOp.assign_parameters` method and - :attr:`.SparsePauliOp.parameters` attribute to assign and query unbound parameters - inside a :class:`.SparsePauliOp`. This function can for example be used as:: - - from qiskit.circuit import Parameter - from qiskit.quantum_info import SparsePauliOp - - x = Parameter("x") - op = SparsePauliOp(["Z", "X"], coeffs=[1, x]) - - # free_params will be: ParameterView([x]) - free_params = op.parameters - - # assign the value 2 to the parameter x - bound = op.assign_parameters([2]) - - -.. _Release Notes_0.24.0_Pulse Features: - -Pulse Features -^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/add-new-symbolic-pulses-4dc46ecaaa1ba928.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added new :class:`~.SymbolicPulse` classes to the pulse library (:mod:`qiskit.pulse.library`) - The new pulses in the library are: - - * :class:`~qiskit.pulse.library.Sin` - * :class:`~qiskit.pulse.library.Cos` - * :class:`~qiskit.pulse.library.Sawtooth` - * :class:`~qiskit.pulse.library.Triangle` - - These new classes are instances of :class:`~.ScalableSymbolicPulse`. With the exception of - the :class:`~.Sawtooth` phase, behavior is identical to that of the corresponding waveform generator - function (e.g. :func:`~qiskit.pulse.library.sin`). The phase for the :class:`~.Sawtooth` class - is defined such that a phase of :math:`2\pi` shifts by a full cycle. - -.. releasenotes/notes/0.24/add-support-for-qpy-reference-70478baa529fff8c.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added support to QPY (:mod:`qiskit.qpy`) for working with pulse - :class:`~.ScheduleBlock` instances with unassigned references, - and preserving the data structure for the reference to subroutines. - This feature allows users to serialize and deserialize a template pulse - program for tasks such as pulse calibration. For example: - - .. code-block:: python - - from qiskit import pulse - from qiskit import qpy - - with pulse.build() as schedule: - pulse.reference("cr45p", "q0", "q1") - pulse.reference("x", "q0") - pulse.reference("cr45p", "q0", "q1") - - with open('template_ecr.qpy', 'wb') as fd: - qpy.dump(schedule, fd) - -.. releasenotes/notes/0.24/update-pulse-gate-pass-for-target-ebfb0ec9571f058e.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- A new method :meth:`.CalibrationEntry.user_provided` has been added to - calibration entries. This method can be called to check whether the entry - is defined by an end user or backend. - -.. releasenotes/notes/0.24/update-pulse-gate-pass-for-target-ebfb0ec9571f058e.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new method :meth:`.Target.get_calibration` which provides - convenient access to the calibration of an instruction in a :class:`~.Target` object - This method can be called with parameter args and kwargs, and - it returns a pulse schedule built with parameters when the calibration - is templated with parameters. - - -.. _Release Notes_0.24.0_Providers Features: - -Providers Features -^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/filter-faulty-qubits-and-gates-v2-converter-b56dac9c0ce8057f.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`~.BackendV2Converter` class has a new keyword argument, - ``filter_faulty``, on its constructor. When this argument is set to ``True`` - the converter class will filter out any qubits or operations listed - as non-operational in the :class:`~.BackendProperties` payload for the - input :class:`~.BackendV1`. While not extensively used a - :class:`~.BackendProperties` object supports annotating both qubits - and gates as being non-operational. Previously, if a backend had set that - flag on any qubits or gates the output :class:`BackendV2` instance and - its :class:`~.Target` would include all operations whether they were - listed as operational or not. By leveraging the new flag you can filter - out these non-operational qubits and gates from the :class:`~.Target`. - When the flag is set the output backend will still be listed as the full - width (e.g. a 24 qubit backend with 4 qubits listed as not operational will - still show it has 24 qubits) but the faulty qubits will not have any - operations listed as being supported in the :class:`~.Target`. - -.. releasenotes/notes/0.24/options-implement-mutable-mapping-b11f4e2c6df4cf31.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`~qiskit.providers.options.Options` class now implements the - the ``Mapping`` protocol and ``__setitem__`` method. This means that - :class:`~.Options` instances now offer the same interface as standard - dictionaries, except for the deletion methods (`__delitem__`, `pop`, - `clear`). Key assignments are validated by the validators, - if any are registered. - - -.. _Release Notes_0.24.0_Visualization Features: - -Visualization Features -^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/staged-pass-manager-drawer-f1da0308895a30d9.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new function, :func:`~.staged_pass_manager_drawer`, which is used - for visualizing a :class:`~.StagedPassManager` instance. It draws the full - pass manager with each stage represented as an outer box. - - For example:: - - from qiskit.visualization import staged_pass_manager_drawer - from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager - from qiskit.providers.fake_provider import FakeSherbrooke - - backend = FakeSherbrooke() - pm = generate_preset_pass_manager(3, backend) - staged_pass_manager_drawer(pm) - -.. releasenotes/notes/0.24/staged-pass-manager-drawer-f1da0308895a30d9.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :meth:`.StagedPassManager.draw` method has been updated to include - visualization of the stages in addition to the overall pass manager. The - stages are represented by outer boxes in the visualization. In previous - releases the stages were not included in the visualization. For example:: - - from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager - from qiskit.providers.fake_provider import FakeSherbrooke - - backend = FakeSherbrooke() - pm = generate_preset_pass_manager(3, backend) - pm.draw(pm) - -.. releasenotes/notes/0.24/update-state-visualization-6836bd53e3a24891.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new keyword argument, ``figsize``, to the - :func:`~qiskit.visualization.plot_bloch_multivector` function. This - argument can be used to set a size for individual Bloch sphere sub-plots. - For example, if there are :math:`n` qubits and ``figsize`` is set to - ``(w, h)``, then the overall figure width is set to :math:`n \cdot w`, while the - overall height is set to :math:`h`. - -.. releasenotes/notes/0.24/update-state-visualization-6836bd53e3a24891.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added a new keyword argument, ``font_size``, to the - :func:`~qiskit.visualization.plot_bloch_multivector` function. This argument - can be used to control the font size in the output visualization. - -.. releasenotes/notes/0.24/update-state-visualization-6836bd53e3a24891.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Added two new keyword arguments, ``title_font_size`` and ``title_pad``, to - the :func:`~qiskit.visualization.plot_bloch_multivector` function. These - arguments can be used to control the font size of the overall title and its - padding respectively. - - -.. _Release Notes_0.24.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.24/bump-msrv-f6f2bd42b9636b5e.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The minimum supported Rust version (MSRV) has been increased from 1.56.1 - to 1.61.0. If you're are building Qiskit from source you will now need to - ensure that you have at least Rust 1.61.0 installed to be able to build - Qiskit. This change was made because several upstream dependencies have increased - their MSRVs. - -.. releasenotes/notes/0.24/delete-args-and-methods-in-primitives-d89d444ec0217ae6.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Removed the usage of primitives with the context manager and the initialization with circuits, - (observables only for Estimator), and parameters - which was deprecated in the Qiskit Terra 0.22.0 release in October 2022. - -.. releasenotes/notes/0.24/primitive-job-submit-a7633872e2ae3c7b.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- :meth:`.PrimitiveJob.submit` no longer blocks on execution finishing. - As a result, :meth:`.Sampler.run`, :meth:`.BackendSampler.run`, :meth:`.Estimator.run` - and :meth:`.BaseEstimator.run` do not block until :meth:`.PrimitiveJob.result` method is called. - - -.. _Release Notes_0.24.0_Transpiler Upgrade Notes: - -Transpiler Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^^^ -.. releasenotes/notes/0.24/preset-pm-vf2-max-trials-958bb8a36fff472f.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The maximum number of trials evaluated when searching for the best - layout using :class:`.VF2Layout` and :class:`.VF2PostLayout` is now - limited in - :func:`~qiskit.transpiler.preset_passmanagers.level_1_pass_manager`, - :func:`~qiskit.transpiler.preset_passmanagers.level_2_pass_manager`, - and - :func:`~qiskit.transpiler.preset_passmanagers.level_3_pass_manager` - to ``2 500``, ``25 000``, and ``250 000``, respectively. Previously, - all found possible layouts were evaluated. This change was made to prevent - transpilation from hanging during layout scoring for circuits with many - connected components on larger devices, which scales combinatorially - since each connected component would be evaluated in all possible - positions on the device. To perform a full search as - before, manually run :class:`.VF2PostLayout` over the transpiled circuit - in strict mode, specifying ``0`` for ``max_trials``. - -.. releasenotes/notes/0.24/6110-709f6fa891bdb26b.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The previously deprecated ``condition`` attribute of the - :class:`~.DAGDepNode` class has been removed. It was marked as deprecated - in the 0.18 release (07-2021). Instead you should use the - :attr:`~.Instruction.condition` attribute of the ``op`` attribute to - access the condition of an operation node. For other node types there - is no condition to access. - -.. releasenotes/notes/0.24/circuit_metadata_always_dict-49015896dfa49d33.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The default value of ``metadata`` in both :class:`.DAGCircuit` and - :class:`.DAGDependency` has been changed from ``None`` to ``{}`` for compatibility - with the matching ``metadata`` attribute of :class:`.QuantumCircuit`. - -.. releasenotes/notes/0.24/coupling-map-eq-b0507b703d62a5f3.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :meth:`.CouplingMap.__eq__`` method has been updated to check that the edge lists of the - underlying graphs contain the same elements. Under the assumption that the underlying graphs are - connected, this check additionally ensures that the graphs have the same number of nodes with - the same labels. Any code using ``CouplingMap() == CouplingMap()`` to check object equality - should be updated to ``CouplingMap() is CouplingMap()``. - -.. releasenotes/notes/0.24/remove-faulty-qubits-support-00850f69185c365e.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- When running the :func:`~.transpile` function with a :class:`~.BackendV1` - based backend or a :class:`~.BackendProperties` via the ``backend_properties`` - keyword argument that has any qubits or gates flagged as faulty the function - will no longer try to automatically remap the qubits based on this information. - The method by which :func:`~.transpile` attempted to do this remapping was - fundamentally flawed and in most cases of such a backend it would result - an internal error being raised. In practice very few backends ever set the - fields in :class:`~.BackendProperties` to flag a qubit or gate as faulty. - If you were relying on :func:`~.transpile` to do this - re-mapping for you, you will now need to manually do that and pass a - mapped input to the ``coupling_map`` and ``backend_properties`` arguments - which has filtered out the faulty qubits and gates and then manually re-map - the output. - -.. releasenotes/notes/0.24/sabre-sort-rng-056f26f205e38bab.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The result of transpilations for fixed seeds may have changed compared to - previous versions of Qiskit Terra. This is because of internal tweaks to - the routing algorithm used by :class:`.SabreSwap` and :class:`.SabreLayout`, - which are the default routing and layout passes respectively, to make them - significantly faster for large circuits. - - -.. _Release Notes_0.24.0_Circuits Upgrade Notes: - -Circuits Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/circuit_metadata_always_dict-49015896dfa49d33.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`.QuantumCircuit` :attr:`~.QuantumCircuit.metadata` attribute now - always returns a dictionary, and can only be set to a dictionary. Previously, - its default value was ``None``, and could be manually set to ``None`` or a - dictionary. - - -.. _Release Notes_0.24.0_Algorithms Upgrade Notes: - -Algorithms Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/remove-deprecated-factorizers-linear-solvers-4631870129749624.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The deprecated modules ``factorizers`` and ``linear_solvers``, containing - ``HHL`` and ``Shor`` have been removed from - :mod:`qiskit.algorithms`. These functionalities - were originally deprecated as part of the 0.22.0 release (released on - October 13, 2022). You can access the code through the Qiskit Textbook instead: - `Linear Solvers (HHL) `_ , - `Factorizers (Shor) `_ - - -.. _Release Notes_0.24.0_Pulse Upgrade Notes: - -Pulse Upgrade Notes -^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/update-pulse-gate-pass-for-target-ebfb0ec9571f058e.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- :meth:`.Target.update_from_instruction_schedule_map` no longer raises - ``KeyError`` nor ``ValueError`` when qubits are missing in the target instruction - or ``inst_name_map`` is not provided for the undefined instruction. - In the former case, it just ignores the input :class:`~.InstructionScheduleMap`\'s - definition for undefined qubits. In the latter case, a gate mapping is pulled from - the standard Qiskit gates and finally, a custom opaque :class:`.Gate` object is defined - from the schedule name if no mapping is found. - - -.. _Release Notes_0.24.0_Providers Upgrade Notes: - -Providers Upgrade Notes -^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/remove-execute_function-max_credits-8c822b8b4b3d84ba.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The deprecated ``max_credits`` argument to :func:`~.execute_function.execute`, :func:`~.compiler.assemble` and all - of the ``Qobj`` configurations (e.g. :class:`.QasmQobjConfig` and - :class:`.PulseQobjConfig`) has been removed. This argument dates back - to early versions of Qiskit which was tied more closely to the IBM - Quantum service offering. At that time the ``max_credits`` field was part - of the "credit system" used by IBM Quantum's service offering. However, - that credit system has not been in use on IBM Quantum backends for - nearly three years and also Qiskit is not tied to IBM Quantum's service - offerings anymore (and hasn't been for a long time). If you were relying on - this option in some way for a backend you will need to ensure that your - :class:`~.BackendV2` implementation exposes a ``max_credits`` field in - its :class:`~.Options` object. - -.. releasenotes/notes/0.24/rename-fake-backends-b08f8a66bc19088b.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :attr:`~.BackendV2.name` attribute on the :class:`~.BackendV2` based - fake backend classes in :mod:`qiskit.providers.fake_provider` have changed - from earlier releases. Previously, the names had a suffix ``"_v2"`` to - differentiate the class from the :class:`~.BackendV1` version. This suffix - has been removed as having the suffix could lead to inconsistencies with - other snapshotted data used to construct the backend object. - - -.. _Release Notes_0.24.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.24/deprecate-opflow-qi-32f7e27884deea3f.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- The modules :mod:`qiskit.opflow`, :mod:`qiskit.utils.backend_utils`, - :mod:`qiskit.utils.mitigation`, - :mod:`qiskit.utils.measurement_error_mitigation`, - class :class:`qiskit.utils.QuantumInstance` and methods - :meth:`~qiskit.utils.run_circuits.find_regs_by_name`, - :meth:`~qiskit.utils.run_circuits.run_circuits` - have been deprecated and will be removed in a future release. - Using :class:`~qiskit.utils.QuantumInstance` is superseded by - :class:`~qiskit.primitives.BaseSampler`. - See `Opflow Migration `__. - See `QuantumInstance Migration `__. - - -.. _Release Notes_0.24.0_Transpiler Deprecations: - -Transpiler Deprecations -^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/deprecate-bip-mapping-f0025c4c724e1ec8.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The transpiler routing pass, :class:`~.BIPMapping` has been deprecated - and will be removed in a future release. It has been replaced by an external - plugin package: ``qiskit-bip-mapper``. Details for this new package can - be found at the package's github repository: - - https://github.com/qiskit-community/qiskit-bip-mapper - - The pass was made into a separate plugin package for two reasons, first - the dependency on CPLEX makes it harder to use and secondly the plugin - packge more cleanly integrates with :func:`~.transpile`. - -.. releasenotes/notes/0.24/fix-target-aquire_alignment-typo-d32ff31742b448a1.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Misspelled ``aquire_alignment`` in the class :class:`.Target` has been replaced by - correct spelling ``acquire_alignment``. - The old constructor argument `aquire_alignment` and :attr:`.Target.aquire_alignment` - are deprecated and will be removed in a future release. - Use :attr:`.Target.acquire_alignment` instead to get and set the alignment constraint value. - - -.. _Release Notes_0.24.0_Circuits Deprecations: - -Circuits Deprecations -^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/circuit_metadata_always_dict-49015896dfa49d33.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Setting the :class:`.QuantumCircuit` :attr:`~.QuantumCircuit.metadata` attribute - to ``None`` has been deprecated and will no longer be supported in a future - release. Instead, users should set it to an empty dictionary if they want - it to contain no data. - - -.. _Release Notes_0.24.0_Algorithms Deprecations: - -Algorithms Deprecations -^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/deprecate-algorithms-c6e1e28b6091c507.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- All of the following features are now deprecated, after having been made pending deprecation - since 0.22.0. More information is available at https://qisk.it/algo_migration. - - * Module :mod:`qiskit.algorithms.minimum_eigen_solvers` is deprecated and - superseded by :mod:`qiskit.algorithms.minimum_eigensolvers`. - - * Module :mod:`qiskit.algorithms.eigen_solvers` is deprecated and - superseded by :mod:`qiskit.algorithms.eigensolvers`. - - * Module :mod:`qiskit.algorithms.evolvers` is deprecated and - superseded by :mod:`qiskit.algorithms.time_evolvers`. - - * Class :class:`qiskit.algorithms.TrotterQRTE` is deprecated and superseded by - :class:`qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE`. - - * Using :class:`~qiskit.utils.QuantumInstance` or :class:`~qiskit.providers.Backend` - is deprecated and superseded by :class:`~qiskit.primitives.BaseSampler` in the following - classes: - - * :class:`~qiskit.algorithms.Grover` - * :class:`~qiskit.algorithms.AmplitudeEstimation` - * :class:`~qiskit.algorithms.FasterAmplitudeEstimation` - * :class:`~qiskit.algorithms.IterativePhaseEstimation` - * :class:`~qiskit.algorithms.MaximumLikelihoodAmplitudeEstimation` - * :class:`~qiskit.algorithms.HamiltonianPhaseEstimation` - * :class:`~qiskit.algorithms.IterativePhaseEstimation` - * :class:`~qiskit.algorithms.PhaseEstimation` - - * Using :class:`~qiskit.utils.QuantumInstance` or :class:`~qiskit.providers.Backend` - or :class:`~qiskit.opflow.ExpectationBase` is deprecated and superseded by - :class:`~qiskit.primitives.BaseSampler` in the following static method: - :meth:`~qiskit.algorithms.optimizers.QNSPSA.get_fidelity` - - * Function :func:`~qiskit.algorithms.aux_ops_evaluator.eval_observables` is deprecated - and superseded by :func:`~qiskit.algorithms.observables_evaluator.estimate_observables` - function. - - -.. _Release Notes_0.24.0_Quantum Information Deprecations: - -Quantum Information Deprecations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/deprecate-pauli-table-fc6dcdb5eeb6e0c4.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`~qiskit.quantum_info.PauliTable` and :class:`~qiskit.quantum_info.StabilizerTable` - are deprecated and will be removed in a future release. - Instead, the :class:`~qiskit.quantum_info.PauliList` should be used. - With this change, :meth:`~qiskit.quantum_info.Clifford.table` has been deprecated - so that you should operate directly from :meth:`~qiskit.quantum_info.Clifford.tableau` - without it. - - -.. _Release Notes_0.24.0_Pulse Deprecations: - -Pulse Deprecations -^^^^^^^^^^^^^^^^^^ - -.. releasenotes/notes/0.24/symbolic-pulse-complex-deprecation-89ecdf968b1a2d89.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Assignment of complex values to ``ParameterExpression`` in any Qiskit Pulse object - now raises a ``PendingDeprecationWarning``. This will align the Pulse module with - other modules where such assignment wasn't possible to begin with. The typical use - case for complex parameters in the module was the SymbolicPulse library. As of - Qiskit-Terra 0.23.0 all library pulses were converted from complex amplitude - representation to real representation using two floats (amp,angle), as used in the - ``ScalableSymbolicPulse`` class. This eliminated the need for complex parameters. - Any use of complex parameters (and particularly custom-built pulses) should be - converted in a similar fashion to avoid the use of complex parameters. - - -.. _Release Notes_0.24.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.24/ae-warns-on-goodstate-7dbb689ba6a5e5e4.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The :class:`.AmplitudeEstimation` class now correctly warns if an :class:`.EstimationProblem` - with a set ``is_good_state`` property is passed as input, as it is not supported and ignored. - Previously, the algorithm would silently ignore this option leading to unexpected results. - -.. releasenotes/notes/0.24/append-bad-argument-error-cc9eafe94cc39033.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- :meth:`.QuantumCircuit.append` will now correctly raise an error if given an incorrect number of - classical bits to apply to an operation. Fix `#9385 `__. - -.. releasenotes/notes/0.24/deterministic-barrier-before-final-measurements-04e817d995794067.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- The :class:`.BarrierBeforeFinalMeasurements` and :class:`.MergeAdjacentBarriers` transpiler - passes previously had a non-deterministic order of their emitted :class:`.Barrier` instructions. - This did not change the semantics of circuits but could, in limited cases where there were - non-full-width barriers, cause later stochastic transpiler passes to see a different topological - ordering of the circuit and consequently have different outputs for fixed seeds. The passes - have been made deterministic to avoid this. - -.. releasenotes/notes/0.24/fix-9798-30c0eb0e5181b691.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- The return type of :meth:`~.PassManager.run` will now - always be the same as that of its first argument. Passing a single circuit - returns a single circuit, passing a list of circuits, even of length 1, - returns a list of circuits. - See `#9798 `__. - -.. releasenotes/notes/0.24/fix-PauliOp-adjoint-a275876185df989f.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed a bug where :meth:`.PauliOp.adjoint` did not return a correct value for Paulis - with complex coefficients, like ``PauliOp(Pauli("iX"))``. - Fixed `#9433 `__. - -.. releasenotes/notes/0.24/fix-circuit-drawer-for-qc-params-e78c67310ae43ccf.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed an issue with the circuit drawer function :func:`~.circuit_drawer` and - :meth:`.QuantumCircuit.draw` method when displaying instruction parameters - that type :class:`.QuantumCircuit` which would result in an illegible - drawing. - Fixed `#9908 `__ - -.. releasenotes/notes/0.24/fix-circuit-drawing-low-compression-965c21de51b26ad2.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed an issue with the circuit drawer function :func:`~.circuit_drawer` - and :meth:`.QuantumCircuit.draw` method when using the ``text`` method and - the argument ``vertical_compression="low"`` where it would use an incorrect - character for the top-right corner of boxes used to represent gates - in the circuit. - -.. releasenotes/notes/0.24/fix-control-with-string-parameter-4eb8a308170e08db.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed an issue with the :meth:`.Gate.control` method where it previously - would incorrectly handle ``str`` or ``None`` input types for the - ``ctrl_state`` argument. - -.. releasenotes/notes/0.24/fix-empty-pauli-label-ce2580584db67a4d.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Fixed an edge case in the construction of :class:`.Pauli` instances; a string with an optional - phase and no qubits is now a valid label, making an operator with no qubits (such as - ``Pauli("-i")``). This was already possible when using the array forms, or empty slices. - Fixed `#9720 `__. - -.. releasenotes/notes/0.24/fix-macros-measure-with-backendV2-4354f00ab4f1cd3e.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed an issue when using the :mod:`~qiskit.pulse` macro - :func:`~qiskit.pulse.macros.measure` when working with a - :class:`.BackendV2` based backend. Previously, trying to use - :func:`qiskit.pulse.macros.measure` with a :class:`.BackendV2` - based backend would have resulted in an error. - Fixed `#9488 `__ - -.. releasenotes/notes/0.24/fix-marginal-distribution-np-ints-ee78859bfda79b60.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Fixed an issue with the :func:`~.marginal_distribution` function where it - would incorrectly raise an error when an input counts dictionary was using - a numpy integer type instead of the Python int type. The underlying function - always would handle the different types correctly, but the input type - checking was previously incorrectly raising a ``TypeError`` in this case. - -.. releasenotes/notes/0.24/fix-parameter-is_real-8b8f99811e58075e.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Fixed a bug where :meth:`.Parameter.is_real` did not return ``None`` when - the parameter is not bound. - Fixed `#8619 `__. - -.. releasenotes/notes/0.24/fix-qasm2-c3sxgate-47171c9d17876219.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Circuits containing :class:`.C3SXGate` can now be output and read in again safely from the - OpenQASM 2.0 exporter (:meth:`.QuantumCircuit.qasm`) and parser (:meth:`.QuantumCircuit.from_qasm_str`). - -.. releasenotes/notes/0.24/fix-qpy-mcxgray-421cf8f673f24238.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Fixed a bug in QPY (:mod:`qiskit.qpy`) where circuits containing gates of class - :class:`.MCXGate`, :class:`.MCXGrayCode`, and :class:`MCXRecursive`, and - :class:`.MCXVChain` would fail to serialize. - See `#9390 `__. - -.. releasenotes/notes/0.24/fix-routing-passes-for-none-coupling_map-c4dd53594a9ef645.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed the transpiler routing passes :class:`~.StochasticSwap`, - :class:`~.SabreSwap`, :class:`~.LookaheadSwap`, and :class:`~.BasicSwap` - so that they consistently raise a :class:`~.TranspilerError` when their - respective ``.run()`` method is called if the passes were initialized - with ``coupling_map=None``. Previously, these passes would raise errors - in this case but they were all caused by side effects and the specific - exception was not predictable. - Fixed `#7127 `__ - -.. releasenotes/notes/0.24/fix-setting-circuit-data-operation-1b8326b1b089f10c.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Manually setting an item in :attr:`.QuantumCircuit.data` will now correctly allow the operation - to be any object that implements :class:`.Operation`, not just a :class:`.circuit.Instruction`. - Note that any manual mutation of :attr:`.QuantumCircuit.data` is discouraged; it is not - *usually* any more efficient than building a new circuit object, as checking the invariants - surrounding parametrised objects can be surprisingly expensive. - -.. releasenotes/notes/0.24/fix-template-opt-bd3c40382e9a993b.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Fixed a bug when constructing :class:`~qiskit.dagcircuit.DAGDependency` from - within the :class:`~qiskit.transpiler.passes.TemplateOptimization` transpiler pass, - which could lead to incorrect optimizations. - -.. releasenotes/notes/0.24/fix-tensoredop-to-matrix-6f22644f1bdb8b41.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Fixed a bug in :meth:`.TensoredOp.to_matrix` where the global coefficient of the operator - was multiplied to the final matrix more than once. Now, the global coefficient is correclty - applied, independent of the number of tensored operators or states. - Fixed `#9398 `__. - -.. releasenotes/notes/0.24/fix-unroll-custom-definitions-empty-definition-4fd175c035445540.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Fixed global-phase handling in the :class:`.UnrollCustomDefinitions` transpiler pass if the - instruction in question had a global phase, but no instructions in its definition field. - -.. releasenotes/notes/0.24/improve-transpile-typing-de1197f4dd13ac0c.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed the the type annotations for the :func:`~.transpile` function. - The return type is now narrowed correctly depending on whether a - single circuit or a list of circuits was passed. - -.. releasenotes/notes/0.24/iterative-phase-estimation-bugfix-b676ffc23cea8251.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Fixed a bug where :class:`.IterativePhaseEstimation` was generating the wrong circuit, causing the - algorithm to fail for simple cases. Fixed `#9280 `__. - -.. releasenotes/notes/0.24/paulilist-do-not-broadcast-from-paulis-96de3832fba21b94.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- A bug has been fixed which had allowed broadcasting when a - :class:`.PauliList` is initialized from :class:`.Pauli`\ s or labels. For - instance, the code ``PauliList(["XXX", "Z"])`` now raises a - ``ValueError`` rather than constructing the equivalent of - ``PauliList(["XXX", "ZZZ"])``. - -.. releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will no longer emit duplicate definitions - for gates that appear in other gates' definitions. See - `#7771 `__, - `#8086 `__, - `#8402 `__, - `#8558 `__, and - `#9805 `__. - -.. releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now handle multiple and nested - definitions of :class:`~.library.UnitaryGate`. See - `#4623 `__, - `#6712 `__, - `#7772 `__, and - `#8222 `__. - -.. releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now output definitions for gates used - only in other gates' definitions in a correct order. See - `#7769 `__ and - `#7773 `__. - -.. releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Standard gates defined by Qiskit, such as :class:`.RZXGate`, will now have properly parametrised - definitions when exported using the OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`). See - `#7172 `__. - -.. releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- Quantum volume circuits (:class:`.QuantumVolume`) are now supported by the OpenQASM 2 exporter - (:meth:`.QuantumCircuit.qasm`). See - `#6466 `__ and - `#7051 `__. - -.. releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- The OpenQASM 2 exporter will now output gates with no known definition with ``opaque`` statements, - rather than failing. See `#5036 `__. - -.. releasenotes/notes/0.24/transpile-coupling-maps-3a137f4ca8e97745.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - -- An issue that prevented :func:`~qiskit.transpile` from working when passed - a list of :class:`~qiskit.transpiler.CouplingMap` objects was fixed. Note - that passing such a list of coupling maps is deprecated and will not be - possible starting with Qiskit Terra 0.25. Fixes - `#9885 `_. - -.. releasenotes/notes/0.24/update-state-visualization-6836bd53e3a24891.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Previous to this release, the ``figsize`` argument of - :func:`~qiskit.visualization.plot_bloch_multivector` was not used by the - visualization, making it impossible to change its size (e.g. to shrink - it for single-qubit states). This release fixes it by introducing a use - for the ``figsize`` argument. - -.. releasenotes/notes/0.24/vf2postlayout-fix-16bb54d9bdf3aaf6.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed an issue in :func:`~.transpile` with ``optimization_level=1`` (as - well as in the preset pass managers returned by - :func:`~.generate_preset_pass_manager` and :func:`~.level_1_pass_manager`) - where previously if the ``routing_method`` and ``layout_method`` arguments - were not set and no control flow operations were present in the circuit - then in cases where routing was required the - :class:`~.VF2PostLayout` transpiler pass would not be run. This was the - opposite of the expected behavior because :class:`~.VF2PostLayout` is - intended to find a potentially better performing layout after a heuristic - layout pass and routing are run. - Fixed `#9936 `__ - -.. releasenotes/notes/0.24/fix-0q-operator-statevector-79199c65c24637c4.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Construction of a :class:`~.quantum_info.Statevector` from a :class:`.QuantumCircuit` containing - zero-qubit operations will no longer raise an error. These operations impart a global phase on - the resulting statevector. - -.. releasenotes/notes/0.24/fix-delay-padding-75937bda37ebc3fd.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed an issue in tranpiler passes for padding delays, which did not respect target's constraints - and inserted delays even for qubits not supporting :class:`~.circuit.Delay` instruction. - :class:`~.PadDelay` and :class:`~.PadDynamicalDecoupling` are fixed - so that they do not pad any idle time of qubits such that the target does not support - ``Delay`` instructions for the qubits. - Also legacy scheduling passes ``ASAPSchedule`` and ``ALAPSchedule``, - which pad delays internally, are fixed in the same way. - In addition, :func:`transpile` is fixed to call ``PadDelay`` with a ``target`` object - so that it works correctly when called with ``scheduling_method`` option. - Fixed `#9993 `__ - -.. releasenotes/notes/0.24/improve-quantum-circuit-assign-parameters-typing-70c9623405cbd420.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed the type annotations on the - :meth:`.QuantumCircuit.assign_parameters` - method to correctly reflect the change in return type depending on the - value of the ``inplace`` argument. - -.. releasenotes/notes/0.24/preset-pm-vf2-max-trials-958bb8a36fff472f.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed a performance scaling issue with the :class:`~.VF2Layout` - and :class:`~.VF2PostLayout` passes in the preset pass managers and - :func:`~.transpile`, which would occur when transpiling circuits with many - connected components on large devices. Now the transpiler passes set - upper bounds on the number of potential layouts that will be evaluated. - -.. releasenotes/notes/0.24/unintended-rounding-with-max-size-1498af5f9a467990.yaml @ b'a259fd8003680e84860c7bcea5ded6b7276047b2' - -- Fixed an issue in the :func:`~.state_to_latex` function where it would - potentially produce invalid LaTeX due to unintended coefficient rounding. - This could also result in errors when the :func:`~.state_drawer` was called. - Fixed `#9297 `__. - -Aer 0.12.0 -========== - -No change - -IBM Q Provider 0.20.2 -===================== - -No change - -############# -Qiskit 0.42.1 -############# - -.. _Release Notes_Terra_0.23.3: - -Terra 0.23.3 -============ - -.. _Release Notes_Terra_0.23.3_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.23.3-bf51a905756c4876.yaml @ b'7dc7a1cc7111b80f6cb7eea6de867e36db3ab1a8' - -Qiskit Terra 0.23.3 is a minor bugfix release. - - -.. _Release Notes_Terra_0.23.3_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.23/fix-transpiler-optimize-1q-decomposition-score-e79ea05c3cf1b6fa.yaml @ b'7dc7a1cc7111b80f6cb7eea6de867e36db3ab1a8' - -- Fixes a bug in the :class:`.Optimize1qGatesDecomposition` transformation pass - where the score for substitutions was wrongly calculated when the gate - errors are zero. - -.. releasenotes/notes/add-inverse-ecr-e03720252a0c9c1e.yaml @ b'3d91d02a23fcdce22f3f47c105a5327087911ff2' - -- The method :meth:`.ECRGate.inverse` now returns another :class:`.ECRGate` instance - rather than a custom gate, since it is self inverse. - -.. releasenotes/notes/clip-quantumstate-probabilities-5c9ce05ffa699a63.yaml @ b'7dc7a1cc7111b80f6cb7eea6de867e36db3ab1a8' - -- Clip probabilities in the :meth:`.QuantumState.probabilities` and - :meth:`.QuantumState.probabilities_dict` methods to the interval ``[0, 1]``. - This fixes roundoff errors where probabilities could e.g. be larger than 1, leading - to errors in the shot emulation of the sampler. - Fixed `#9761 `__. - -.. releasenotes/notes/fix-backendsampler-padding-ed959e6dc3deb3f3.yaml @ b'50f5f5f43cb21a60404960533f7cb84af994956e' - -- Fixed a bug in the :class:`.BackendSampler` where the binary probability bitstrings - were truncated to the minimal number of bits required to represent the largest outcome - as integer. That means that if e.g. ``{"0001": 1.0}`` was measured, the result was truncated - to ``{"1": 1.0}``. - -.. releasenotes/notes/fix-backendv1-pm-config-from-backend-914869dd6e1c06be.yaml @ b'7c43efc318b0832f2626b20b4a4b5eb9990de092' - -- Fixed an issue with the :meth:`.PassManagerConfig.from_backend` - constructor method when it was used with a :class:`~.BackendV1` based - simulator backend. For some simulator backends which did not populate - some optional fields the constructor would error. - Fixed `#9265 `__ and - `#8546 `__ - -.. releasenotes/notes/fix-bound-pm-backend-primitives-98fd11c5e852501c.yaml @ b'7dc7a1cc7111b80f6cb7eea6de867e36db3ab1a8' - -- Fixed the :class:`.BackendSampler` and :class:`.BackendEstimator` to run successfully - with a custom ``bound_pass_manager``. Previously, the execution for single circuits with - a ``bound_pass_manager`` would raise a ``ValueError`` because a list was not returned - in one of the steps. - -.. releasenotes/notes/fix-gate-direction-calibration-c51202358d86e18f.yaml @ b'44cda51974e29fc72fa7e428a14b00af48b32562' - -- The :class:`.GateDirection` transpiler pass will no longer reject gates that have been given - explicit calibrations, but do not exist in the generic coupling map or target. - -.. releasenotes/notes/fix-memory-commutation-checker-dbb441de68706b6f.yaml @ b'7dc7a1cc7111b80f6cb7eea6de867e36db3ab1a8' - -- Fixed an issue with the :class:`.CommutationChecker` class where it would - attempt to internally allocate an array for :math:`2^{n}` qubits when it - only needed an array to represent :math:`n` qubits. This could cause - an excessive amount of memory for wide gates, for example a 4 qubit - gate would require 32 gigabytes instead of 2 kilobytes. - Fixed `#9197 `__ - -.. releasenotes/notes/fix-missing-instproperty-calibration-e578052819592a0b.yaml @ b'938994d02a301a7751d1785f687421a6f269c368' - -- Getting empty calibration from :class:`.InstructionProperties` raises - AttributeError has been fixed. Now it returns ``None``. - -.. releasenotes/notes/fix-qasm-reset-ef7b07bf55875be7.yaml @ b'c14f52856c76686cd2f9cc32a21165a9a6705985' - -- Fixed :meth:`~.QuantumCircuit.qasm` so that it appends ``;`` after ``reset`` instruction. - -.. releasenotes/notes/fix-qasm3-name-escape-43a8b0e5ec59a471.yaml @ b'd63dc4ed00668bb28c231b1158f4295acfffafaf' - -- Register and parameter names will now be escaped during the OpenQASM 3 export - (:func:`.qasm3.dumps`) if they are not already valid identifiers. Fixed `#9658 - `__. - -.. releasenotes/notes/fix-qpy-import-StatePreparation-e20f8ab07bfe39a3.yaml @ b'ac9f9b96d4df9fc88a71aa51833764fa4b8820df' - -- QPY (using :func:`.qpy.load`) will now correctly deserialize :class:`~.StatePreparation` - instructions. Previously, QPY would error when attempting to load a file containing one. - Fixed `#8297 `__. - -.. releasenotes/notes/fix-random-circuit-conditional-6067272319986c63.yaml @ b'215aa22d22fa6c9f95b8f3af9c2062e70bc646ca' - -- Fixed a bug in :func:`.random_circuit` with 64 or more qubits and ``conditional=True``, where - the resulting circuit could have an incorrectly typed value in its condition, causing a variety - of failures during transpilation or other circuit operations. Fixed `#9649 - `__. - -.. releasenotes/notes/fix-type-angles-euler-decompose-233e5cee7205ed03.yaml @ b'36807d1d6e957585053d6a6d29c63e72f122c7bb' - -- Fixed an issue with the :class:`~.OneQubitEulerDecomposer` class's methods - :meth:`~.OneQubitEulerDecomposer.angles` and :meth:`~.OneQubitEulerDecomposer.angles_and_phase` - would error if the input matrix was of a dtype other than ``complex``/``np.cdouble``. In earlier - releases this worked fine but this stopped working in Qiskit Terra 0.23.0 - when the internals of :class:`~.OneQubitEulerDecomposer` were re-written - in Rust. - Fixed `#9827 `__ - -.. releasenotes/notes/fix_9559-ec05304e52ff841f.yaml @ b'881e0d9eed2d7d621243358d78b67f62c122305e' - -- The Qiskit gates :class:`~.CCZGate`, :class:`~.CSGate`, :class:`~.CSdgGate` are not defined in - ``qelib1.inc`` and, therefore, when dump as OpenQASM 2.0, their definition should be inserted in the file. - Fixes `#9559 `__, - `#9721 `__, and - `#9722 `__. - -Aer 0.12.0 -========== - -No change - -IBM Q Provider 0.20.2 -===================== - -No change - -############# -Qiskit 0.42.0 -############# - -Terra 0.23.2 -============ - -No change - -Aer 0.12.0 -========== - -.. _Release Notes_0.12.0_Aer_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.12/prepare-0.12-0da477fc0492ca5d.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -The Qiskit Aer 0.12.0 release highlights are: - - * Added a new GPU tensor network simulator based on - `cuTensorNet `__ - * Added a new :class:`~.AerDensityMatrix` class to the :mod:`qiskit_aer.quantum_info` module - * Greatly improving the runtime performance of the :class:`~.AerSimulator` and the legacy - :class:`~.QasmSimulator`, :class:`~.StatevectorSimulator`, and :class:`~.UnitarySimulator` - classes by directly converting the input :class:`~.QuantumCircuit` objects to an internal - C++ representation instead of first serializing the circuit to a :class:`~.QasmQobj`. This - improvement will be most noticeable for circuits with a small number of qubits or parameterized - circuits using the ``parameter_binds`` keyword argument. - - -.. _Release Notes_0.12.0_Aer_New Features: - -New Features ------------- - -.. releasenotes/notes/0.12/add-NoiseModel-from_backendproperties-1a3d6d976133a661.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Added a new class method :meth:`~.NoiseModel.from_backend_properties` to - the :class:`NoiseModel`. This enables constructing a new :class:`~.NoiseModel` - from a :class:`~qiskit.providers.BackendProperties` object. Similar functionality used - to be present in the :meth:`.NoiseModel.from_backend` constructor, - however it was removed since a :class:`~qiskit.providers.BackendProperties` object alone - doesn't contain sufficient information to create a :class:`~.NoiseModel` - object. - -.. releasenotes/notes/0.12/add-aer-density-matrix-e2439120b24c91c9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Added a new class, :class:`~.AerDensityMatrix`, to the :mod:`qiskit_aer.quantum_info` - module. This class is used to provide the same interface to the - upstream :class:`~qiskit.quantum_info.DensityMatrix` class in Qiskit but backed by - Qiskit Aer's simulation. - -.. releasenotes/notes/0.12/add-grouping-fcc4fad69ccdac26.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Added a new keyword argument, ``abelian_grouping``, to - the :class:`~.Estimator`. This argument is used to control whether the - :class:`~.Estimator` will group the input observables into qubit-wise - commutable observables which reduces the number of circuit executions - required to compute the expectation value and improves the runtime - performance of the :class:`~.Estimator`. By default this is set to - ``True``. - -.. releasenotes/notes/0.12/add_initialize_density_matrix-a72b1a614b09726e.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- ``AerState`` has a new method ``initialize_density_matrix()`` that sets a density matrix - to ``AER::QV::DensityMatrix``. This method will be called in ``q.i.states.DensityMatrix`` - to initialize its data with ``ndarray``. ``initialize_density_matrix()`` has a boolean - argument that specifies copy or share of ``ndarray`` data. If the data is shared with - C++ and python, the data must not be collected in python while C++ accesses it. - -.. releasenotes/notes/0.12/own_assembler-0c76e67a054bd12c.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The overhead for running simulations with :meth:`~.AerSimulator.run` - (for all simulator backend classess) has been greatly reduced. This was - accomplished by no longer internally serializing - :class:`~qiskit.circuit.QuantumCircuit` objects into - :class:`~qiskit.qobj.QasmQobj` and instead the - :class:`~qiskit.circuit.QuantumCircuit` object directly to - an internal C++ circuit structure used for simulation. This improvement - is most noticeable for simulations of circuts with a small number of qubits - or parameterized circuits using the ``parameter_binds`` keyword argument - of :meth:`~.AerSimulator.run`. - Note that pulse simualation (via the now deprecated :class:`~.PulseSimulator`) - and DASK-based simulation still use the internal serialization and will - not see this performance improvement. - -.. releasenotes/notes/0.12/own_assembler-0c76e67a054bd12c.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Added a new method to the :class:`~.AerJob`, :meth:`~.AerJob.circuits`, which - returns a list of :class:`~qiskit.circuit.QuantumCircuit` objects. This method returns - ``None`` if Qobj is used for simulation. - -.. releasenotes/notes/0.12/support_kraus-ec31e636c6793b8c.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- :class:`.AerState` and :class:`.AerStatevector` now support applying :class:`~qiskit.quantum_info.Kraus` operators. - In :class:`.AerStatevector`, one of the Kraus operators is applied randomly to the quantum state based on the error probabilities. - -.. releasenotes/notes/0.12/tensor_network_gpu-e8eb3e40be3c35f7.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Added a new simulation method based on NVIDIA's `cuTensorNet `__ - APIs of cuQuantum SDK. This provides a GPU accelerated general tensor - network simulator that can simulate any quantum circuit, by internally - translating the circuit into a tensor network to perform the simulation. - To use this simulation method, set ``method="tensor_network"`` and - ``device="GPU"`` when initializing an :class:`~.AerSimulator` object. - For example:: - - from qiskit_aer import AerSimulator - - tensor_net_sim = AerSimulator(method="tensor_network", device="GPU") - - This method supports both statevector and density matrix simulations. - Noise simulation can also be done with a density matrix single shot - simulation if there are not any :class:`~.SaveStatevector` operations - in the circuit. - - This new simulation method also supports parallelization with multiple GPUs and - MPI processes by using tensor network slicing technique. However, this type of - simulation will likely take a very long time if the input circuits are - complicated. - -.. releasenotes/notes/0.12/use_specified_bla_vendor-ca0322e993378048.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The ``BLA_VENDOR`` environment variable can now be specified to use a - different BLAS library when building Qiskit Aer from source. By default - if this is not specified OpenBLAS will be used by default. If - the BLAS library specified in `BLA_VENDOR`` can not be found then the - Cmake build process will stop. - - -.. _Release Notes_0.12.0_Aer_Known Issues: - -Known Issues ------------- - -.. releasenotes/notes/0.12/use_conan_1.x-f12570e2cfc8bb26.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- This release of Qiskit Aer is not compatible with the Conan 2.X release - series. If you are building Qiskit Aer from source manually ensure that - you are using a Conan 1.x release. Compatibility with newer versions - of Conan will be fixed in a future release. You can refer to - issue `#1730 `__ for - more details. - - -.. _Release Notes_0.12.0_Aer_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.12/add-grouping-fcc4fad69ccdac26.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The default behavior of the :class:`~.Estimator` primitive will now - group the input observable into qubit-wise commutable observables. - The grouping reduces the number of circuits to be executed and improves - the performance. If you desire the previous behavior you can initialize - your :class:`~.Estimator` instance with the keyword argument - ``abelian_grouping=False``. - -.. releasenotes/notes/0.12/delete-args-and-methods-in-primitives-8013546db867e849.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Removed the usage of primitives with the context manager and the initialization with circuits, - (observables only for Estimator), and parameters - which has been deprecated in the Qiskit Terra 0.22.0 release in October 2022. - -.. releasenotes/notes/0.12/own_assembler-0c76e67a054bd12c.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The behavior of :meth:`~.AerSimulator.run` method has changed when invalid - or otherwise unsimulatable :class:`~.QuantumCircuit` objects are passed as - an input. Previously, in these cases the :meth:`~.AerSimulator.run` method - would return an :class:`~.AerJob` whose :meth:`~.AerJob.result` method would - return a :class:`~.Result` with the ``ERROR`` or ``PARTIAL COMPLETED`` - (depending on whether all the circuit inputs or only some were invalid or not). - Starting in this release instead of returning a result object with these statuses - an exception will be raised instead. This change was necessary because - of the performance improvements by no longer internally serializing the - :class:`~.QuantumCircuit` objects to a Qobj before passing it to C++, instead - the direct conversion from :class:`~.QuantumCircuit` now errors directly when - trying to simulate a circuit Qiskit Aer is unable to execute. If you desire the - previous behavior you can build Qiskit Aer in standalone mode and manually - serialize your :class:`~.QuantumCircuit` objects to a JSON representation of - the :class:`~.QasmQobj` which you then pass to the standalone Aer binary - which will retain the previous behavior. - -.. releasenotes/notes/0.12/remove-deprecated-noise-functions-52128d161d3327e9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- A deprecated method :meth:`add_nonlocal_quantum_error` in :class:`~.NoiseModel` has been - removed. No alternative method is available. If you want to add non-local quantum errors, - you should write a transpiler pass that inserts your own quantum error into a circuit, - and run the pass just before running the circuit on Aer simulator. - -.. releasenotes/notes/0.12/remove-deprecated-noise-functions-52128d161d3327e9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The :meth:`.NoiseModel.from_backend` now has changed not to accept ``BackendProperties`` - object as a ``backend`` argument. Use newly added :meth:`.NoiseModel.from_backend_properties` - method instead. - -.. releasenotes/notes/0.12/remove-deprecated-noise-functions-52128d161d3327e9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- A deprecated ``standard_gates`` argument broadly used in several methods and functions - (listed below) across :mod:`~.noise` module has been removed. - - * :meth:`NoiseModel.from_backend` and :func:`noise.device.basic_device_gate_errors` - * :func:`kraus_error`, :func:`mixed_unitary_error`, :func:`pauli_error` and - :func:`depolarizing_error` in :mod:`noise.errors.standard_errors` - * :meth:`QuantumError.__init__` - - No alternative means are available because the user should be agnostic about - how the simulator represents noises (quantum errors) internally. - -.. releasenotes/notes/0.12/remove-deprecated-noise-functions-52128d161d3327e9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The constructor of :class:`~.QuantumError` has now dropped the support of deprecated - json-like input for ``noise_ops`` argument. - Use the new styple input for ``noise_ops`` argument instead, for example, - - .. code-block:: python - - from qiskit.circuit.library import IGate, XGate - from qiskit_aer.noise import QuantumError - - error = QuantumError([ - ((IGate(), [1]), 0.9), - ((XGate(), [1]), 0.1), - ]) - - # json-like input is no longer accepted (the following code fails) - # error = QuantumError([ - # ([{"name": "I", "qubits": [1]}], 0.9), - # ([{"name": "X", "qubits": [1]}], 0.1), - # ]) - - Also it has dropped deprecated arguments: - - * ``number_of_qubits``: Use ``QuantumCircuit`` to define ``noise_ops`` instead. - * ``atol``: Use :attr:`QuantumError.atol` attribute instead. - * ``standard_gates``: No alternative is available (users should not too much care about - internal representation of quantum errors). - -.. releasenotes/notes/0.12/remove-deprecated-noise-functions-52128d161d3327e9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The deprecated :mod:`noise.errors.errorutils` module has been entirely removed - and no alternatives are available. - All functions in the module were helper functions meant to be used - only for implementing functions in :mod:`~.noise.errors.standard_errors` - (i.e. they should have been provided as private functions) - and no longer used in it. - -.. releasenotes/notes/0.12/remove-deprecated-noise-functions-52128d161d3327e9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The deprecated :mod:`utils.noise_remapper` have been entirely removed and no alternatives - are available since the C++ code now automatically truncates and remaps noise models - if it truncates circuits. - -.. releasenotes/notes/0.12/remove-deprecated-noise-functions-52128d161d3327e9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- All deprecated functions (:func:`pauli_operators` and :func:`reset_operators`) - and class (:class:`NoiseTransformer`) in :mod:`utils.noise_transformation` module - have been removed, and no alternatives are available. - They were in fact private functions/class used only for implementing - :func:`approximate_quantum_error` and should not have been public. - -.. releasenotes/notes/0.12/remove-qobj-684e68e99b212973.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The previously deprecated ``qobj`` argument name of the - :class:`~.AerSimulator` and :class:`~.PulseSimulator` classes' - :meth:`~.AerSimulator.run` method has now been removed. This argument - name was deprecated as part of the Qiskit Aer 0.8.0 release and has - been by the ``circuits`` and ``schedules`` argument name respectively. - -.. releasenotes/notes/0.12/remove-setup_requires-751a406e2782885e.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Aer's ``setup.py`` has been updated to no longer attempt to make calls to ``pip`` to - install build requirements, both manually and via the ``setup_requires`` option in - ``setuptools.setup``. The preferred way to build Aer is to use a `PEP 517 `__-compatible - builder such as: - - .. code-block:: text - - pip install . - - This change means that a direct call to ``setup.py`` will no longer work if the - build requirements are not installed. This is inline with modern Python packaging - guidelines. - - -.. _Release Notes_0.12.0_Aer_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.12/deprecate-37-b3ec705b9f469b0b.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Support for running Qiskit Aer with Python 3.7 support has been deprecated - and will be removed in a future release. This means - starting in a future release you will need to upgrade the Python - version you're using to Python 3.8 or above. - -.. releasenotes/notes/0.12/deprecate-pulse-simulator-27cde3ece112c346.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The :class:`~.PulseSimulator` backend has been deprecated and will be - removed in a future release. If you're using the :class:`~.PulseSimulator` - backend to perform pulse level simulation, instead you should use the - `Qiskit Dynamics `__ library - instead to perform the simulation. Qiskit Dynamics provides a more - flexible and robust pulse level simulation framework than the - :class:`~.PulseSimulator` backend. - -.. releasenotes/notes/0.12/own_assembler-0c76e67a054bd12c.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The :meth:`~.AerJob.qobj` method of the :class:`AerJob` class is - now deprecated and will be removed in a future release. The use of - the qobj format as input to :meth:`~.AerSimulator.run` has been - deprecated since qiskit-aer 0.9.0 and in most cases this method - would return ``None`` now anyway. If you'd like to get the input - to the ``run()`` method now you can use the :meth:`~.AerJob.circuits` - method instead, which will return the :class:`~.QuantumCircuit` - objects that were simulated in the job. - -.. releasenotes/notes/0.12/remove-deprecated-noise-functions-52128d161d3327e9.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- A ``warnings`` argument broadly used in several methods and functions - across :mod:`~.noise` module has been deprecated in favor of - the use of filtering functions in Python's standard ``warnings`` library. - - -.. _Release Notes_0.12.0_Aer_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.12/fix-ndarray-contiguity-e903d0fda4744100.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Fixed an issue when creating a new :class:`~.AerStatevector` instance - from a ``numpy.ndarray`` that had non-contiguous memory. Previously, - this would result in unexpected behavior (and a potential error) as - the :class:`~.AerStatevector` assumed the input array was contiguous. This - has been fixed so that memory layout is checked and the ``numpy.ndarray`` - will be copied internally as a contiguous array before using it. - -.. releasenotes/notes/0.12/fix-split-cregs-5b5494a92c4903e7.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Fixed an issue with the :class:`.Sampler` class where it would previously - fail if the input :class:`~.QuantumCircuit` contained multiple - multiple classical registers. - Fixed `#1679 `__ - -.. releasenotes/notes/0.12/fix_batch_execution-da4d88dbee26731b.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The bits count of classical register used on the GPU was not set before - calculating free available memory for chunks that causes infinite loop. - So this fix set bits count before allocating chunks if batch shots - execution is enabled. - -.. releasenotes/notes/0.12/fix_tensor_network_not_installed-a23b8ef65e6e643e.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Fix build errors and test errors when enabling GPU but disabling cuQuantum. - -.. releasenotes/notes/0.12/mps_fix_apply_measure-84c29a728ae0e717.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- Fixed an issue in the matrix product state simulation method (i.e. - setting the keyword argument ``method="matrix_product_state"`` when - initializing an :class:`~.AerSimulator` object) where the simulator - would incorrectly sort the qubits prior to performing measurment - potentially resulting in an infinite loop. This has been fixed so - the measurement of the qubits occurs in the order of the current MPS - structure and then sorting afterwards as a post-processing step. This also - will likely improve the performance of the simulation method and enable - more accurate representation of entangled states. - Fixed `#1694 `__ - -.. releasenotes/notes/0.12/support_break_and_continue_gates-bf30316fcacd4b6b.yaml @ b'2377d2efb48d18aab73df121924a1446310297de' - -- The :class:`.AerSimulator` backend with methods: - - * ``statevector`` - * ``density_matrix`` - * ``matrix_product_state`` - * ``stabilizer`` - - now report that they support ``break_loop`` and ``continue_loop`` instructions when used - as backends for the Terra :func:`~qiskit.compiler.transpile` function. The simulators - already did support these, but had just not been reporting it. - -.. _Release Notes_IBMQ_0.20.2: - -IBM Q Provider 0.20.2 -===================== - -This release removes the overly restrictive version constraints set in the -requirements for the package added in 0.20.1. For the 0.20.1 the only dependency -that was intended to have a version cap was the ``requests-ntlm`` package as its -new release was the only dependency which currently has an incompatibility with -``qiskit-ibmq-provider``. The other version caps which were added as part of -0.20.1 were causing installation issues in several environments because it made -the ``qiskit-ibmq-provider`` package incompatible with the dependency versions -used in other packages. - - -############# -Qiskit 0.41.1 -############# - -.. _Release Notes_Terra_0.23.2: - -Terra 0.23.2 -============ - -.. _Release Notes_Terra_0.23.2_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.23.2-80519f083ae7086c.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -The Qiskit Terra 0.23.2 patch release fixes further bugs identified in the 0.23 series. - - -.. _Release Notes_Terra_0.23.2_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/add-gates-to-Clifford-class-7de8d3213c60836a.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -- Add the following Clifford gates, that already exist in the circuit library, - to the :class:`.Clifford` class: - :class:`.SXGate`, :class:`.SXdgGate`, :class:`.CYGate`, :class:`.DCXGate`, - :class:`.iSwapGate` and :class:`.ECRGate`. - -.. releasenotes/notes/add-gates-to-Clifford-class-7de8d3213c60836a.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -- Add a decomposition of an :class:`.ECRGate` into Clifford gates (up to a global phase) - to the standard equivalence library. - -.. releasenotes/notes/fix-backendv2-converter-simulator-e8f150d1fd6861fe.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -- Fixed an issue with the :class:`~.BackendV2Converter` class when wrapping - a :class:`~.BackendV1`-based simulator. It would error if either - the ``online_date`` field in the :class:`~.BackendConfiguration` for the - simulator was not present or if the simulator backend supported ideal - implementations of gates that involve more than 1 qubit. - Fixed `#9562 `__. - -.. releasenotes/notes/fix-backendv2converter-de342352cf882494.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -- Fixed an incorrect return value of the method :meth:`.BackendV2Converter.meas_map` - that had returned the backend ``dt`` instead. - -.. releasenotes/notes/fix-backendv2converter-de342352cf882494.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -- Fixed missing return values from the methods :meth:`.BackendV2Converter.drive_channel`, - :meth:`~.BackendV2Converter.measure_channel`, :meth:`~.BackendV2Converter.acquire_channel` and - :meth:`~.BackendV2Converter.control_channel`. - -.. releasenotes/notes/fix-deprecated-bit-qpy-roundtrip-9a23a795aa677c71.yaml @ b'3dbbb32e762850db265c7bb40787a36351aad917' - -- The deprecated :class:`~.circuit.Qubit` and :class:`.Clbit` properties :attr:`~.circuit.Qubit.register` and - :attr:`~.circuit.Qubit.index` will now be correctly round-tripped by QPY (:mod:`qiskit.qpy`) in all - valid usages of :class:`.QuantumRegister` and :class:`.ClassicalRegister`. In earlier releases - in the Terra 0.23 series, this information would be lost. In versions before 0.23.0, this - information was partially reconstructed but could be incorrect or produce invalid circuits for - certain register configurations. - - The correct way to retrieve the index of a bit within a circuit, and any registers in that - circuit the bit is contained within is to call :meth:`.QuantumCircuit.find_bit`. This method - will return the correct information in all versions of Terra since its addition in version 0.19. - -.. releasenotes/notes/fix-instmap-from-target-f38962c3fd03e5d3.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -- Fixed an issue with the :meth:`.InstructionScheduleMap.has_custom_gate` method, - where it would always return ``True`` when the :class:`~.InstructionScheduleMap` - object was created by :class:`.Target`. - Fixed `#9595 `__. - -.. releasenotes/notes/fix-numpy-eigensolver-sparse-0e255d7b13b5e43b.yaml @ b'29ccca1295520b5db60346b9a373eafe53f7c5f1' - -- Fixed a bug in the NumPy-based eigensolvers - (:class:`~.minimum_eigensolvers.NumPyMinimumEigensolver` / - :class:`~.eigensolvers.NumPyEigensolver`) - and in the SciPy-based time evolvers (:class:`.SciPyRealEvolver` / - :class:`.SciPyImaginaryEvolver`), where operators that support conversion - to sparse matrices, such as :class:`.SparsePauliOp`, were converted to dense matrices anyways. - -.. releasenotes/notes/fix-sk-sdg-81ec87abe7af4a89.yaml @ b'5c461eb8079ffb5997a86e984efd7356c0cc32ca' - -- Fixed a bug in :func:`.generate_basic_approximations` where the inverse of the - :class:`.SdgGate` was not correctly recognized as :class:`.SGate`. - Fixed `#9585 `__. - -.. releasenotes/notes/fix-vqd-with-spsa-optimizers-9ed02b80f26e8abf.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -- Fixed a bug in the :class:`~.eigensolvers.VQD` algorithm where - the energy evaluation function could not process batches of parameters, making it - incompatible with optimizers with ``max_evals_grouped>1``. - Fixed `#9500 `__. - -.. releasenotes/notes/qnspsa-float-bug-fix-4035f7e1eb61dec2.yaml @ b'09f904a03c056abb5ed80030e4d1f75108943502' - -- Fixed bug in :class:`.QNSPSA` which raised a type error when the computed fidelities - happened to be of type ``int`` but the perturbation was of type ``float``. - -Aer 0.11.2 -========== - -No change - -.. _Release Notes_IBMQ_0.20.1: - -IBM Q Provider 0.20.1 -===================== - -Since ``qiskit-ibmq-provider`` is now deprecated, the dependencies have been bumped and fixed to the -latest working versions. There was an issue with the latest version of the ``requests-ntlm`` package -which caused some end to end tests to fail. - - -############# -Qiskit 0.41.0 -############# - -Terra 0.23.1 -============ - -.. _Release Notes_0.23.1_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.23.1-9fa7d954a6c0590e.yaml @ b'd4e7144efa9c661817161f84553313bf39406fac' - -Qiskit Terra 0.23.1 is a small patch release to fix bugs identified in Qiskit Terra 0.23.0 - - -.. _Release Notes_0.23.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix-instmap-add-with-arguments-250de2a7960565dc.yaml @ b'd4e7144efa9c661817161f84553313bf39406fac' - -- An edge case of pickle :class:`.InstructionScheduleMap` with - non-picklable iterable ``arguments`` is now fixed. - Previously, using an unpickleable iterable as the ``arguments`` - parameter to :meth:`.InstructionScheduleMap.add` (such as ``dict_keys``) - could cause parallel calls to :func:`.transpile` to fail. These - arguments will now correctly be normalized internally to ``list``. - -.. releasenotes/notes/fix-partial-reverse-gradient-f35fb1f30ee15692.yaml @ b'd4e7144efa9c661817161f84553313bf39406fac' - -- Fixed a performance bug in :class:`.ReverseEstimatorGradient` where the calculation - did a large amount of unnecessary copies if the gradient was only calculated for - a subset of parameters, or in a circuit with many unparameterized gates. - -.. releasenotes/notes/fix-register-name-format-deprecation-61ad5b06d618bb29.yaml @ b'6ec3efff0f38f5857dbd80137bf1cba9cb379f22' - -- Fixed a bad deprecation of :attr:`.Register.name_format` which had made the class attribute - available only from instances and not the class. When trying to send dynamic-circuits jobs to - hardware backends, this would frequently cause the error:: - - AttributeError: 'property' object has no attribute 'match' - - Fixed `#9493 `__. - -Aer 0.11.2 -========== - -No change - -.. _Release Notes_IBMQ_0.20.0: - -IBM Q Provider 0.20.0 -===================== - -Prelude -------- - -This release of the ``qiskit-ibmq-provider`` package marks the package as deprecated and will be retired and archived -in the future. The functionality in ``qiskit-ibmq-provider`` has been supersceded by 3 packages ``qiskit-ibm-provider``, -``qiskit-ibm-runtime``, and ``qiskit-ibm-experiment`` which offer different subsets of functionality that -``qiskit-ibmq-provider`` contained. You can refer to the table here: - -https://github.com/Qiskit/qiskit-ibmq-provider#migration-guides - -for links to the migration guides for moving from ``qiskit-ibmq-provider`` to its replacmeent packages. - - -.. _Release Notes_IBMQ_0.20.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.20.0/deprecation-message-37792b01e4118b5b.yaml @ b'bff830447c097e7286d38ebb885e19bd06b0a684' - -- As of version 0.20.0, ``qiskit-ibmq-provider`` has been deprecated with its support - ending and eventual archival being no sooner than 3 months from that date. - The function provided by qiskit-ibmq-provider is not going away rather it has being split out - to separate repositories. Please see https://github.com/Qiskit/qiskit-ibmq-provider#migration-guides. - - -.. _Release Notes_IBMQ_0.20.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.19/fix-terra-version-string-parsing-12afae5b2b947211.yaml @ b'7720d6051b16ead74b8b9f4021247fc76558f3e1' - -- In the upcoming terra release there will be a release candidate tagged - prior to the final release. However changing the version string for the - package is blocked on the qiskit-ibmq-provider right now because it is trying - to parse the version and is assuming there will be no prelease suffix on - the version string (see `#8200 `__ - for the details). PR `#1135 `__ - fixes this version parsing to use the regex from the - pypa/packaging project which handles all the PEP440 package versioning - include pre-release suffixes. This will enable terra to release an - 0.21.0rc1 tag without breaking the qiskit-ibmq-provider. - -.. releasenotes/notes/0.19/remove-basebackend-typehint-63bbcad7e5dd0dc5.yaml @ b'4f1f8c64543aa9b787a8e9e41be106fb8cdfe435' - -- PR `#1129 `__ updates - :meth:`~qiskit.providers.ibmq.least_busy` method to no longer support `BaseBackend` as a valid - input or output type since it has been long deprecated in qiskit-terra and has recently - been removed. - -.. releasenotes/notes/0.19/replace-threading-aliases-64a9552b28abd3cd.yaml @ b'7720d6051b16ead74b8b9f4021247fc76558f3e1' - -- ``threading.currentThread`` and ``notifyAll`` were deprecated in Python 3.10 (October 2021) - and will be removed in Python 3.12 (October 2023). - PR `#1133 `__ replaces them - with ``threading.current_thread``, ``notify_all`` added in Python 2.6 (October 2008). - -.. releasenotes/notes/0.20.0/add-dynamic-circuits-warning-7e17eac231aed88d.yaml @ b'bff830447c097e7286d38ebb885e19bd06b0a684' - -- Calls to run a quantum circuit with ``dynamic=True`` now raise an error - that asks the user to install the new ``qiskit-ibm-provider``. - -############# -Qiskit 0.40.0 -############# -This release officially deprecates the Qiskit IBMQ provider project as part of the Qiskit metapackage. -This means that in a future release, ``pip install qiskit`` will no longer automatically include ``qiskit-ibmq-provider``. -If you're currently installing or listing ``qiskit`` as a dependency to get ``qiskit-ibmq-provider``, you -should update to explicitly include ``qiskit-ibmq-provider`` as well. This is being done as the Qiskit -project moves towards a model where the ``qiskit`` package only contains the common core functionality for -building and compiling quantum circuits, programs, and applications. -Packages that build on that core or link Qiskit to hardware or simulators will be installable as separate packages. - -Terra 0.23.0 -============ - -.. _Release Notes_0.23.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.23/prepare-0.23.0-release-0d954c91143cf9a4.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -Qiskit Terra 0.23.0 is a major feature release that includes -a multitude of new features and bugfixes. The highlights for this release -are: - - * Support for importing OpenQASM 3 programs and creating :class:`.QuantumCircuit` objects - from the input program via two new functions :func:`qiskit.qasm3.load` and - :func:`qiskit.qasm3.loads`. - - * Improvements to the library of synthesis algorithms included in - Qiskit. This includes the following new synthesis functions: - - * Clifford Synthesis - - * :func:`~.synth_clifford_layers` - * :func:`~.synth_clifford_greedy` - - * Linear Function Synthesis: - - * :func:`~.synth_cnot_depth_line_kms` - * :func:`~.synth_cnot_count_full_pmh` - - * Permutation Synthesis: - - * :func:`~.synth_permutation_basic` - * :func:`~.synth_permutation_acg` - * :func:`~.synth_permutation_depth_lnn_kms` - - * :class:`~.SolovayKitaevDecomposition` detailed in: - https://arxiv.org/abs/quant-ph/0505030 - - * New plugins for :class:`~.HighLevelSynthesis`: - - * :class:`~.ACGSynthesisPermutation` - * :class:`~.KMSSynthesisPermutation` - * :class:`~.BasicSynthesisPermutation` - - * New plugin for :class:`~.UnitarySynthesis` - - * :class:`~.SolovayKitaevSynthesis` - - * Performance improvements to :class:`~.SabreLayout`. The pass - is now primarily written in Rust which can lead to a runtime - improvement, however the bigger improvement is in the quality of - the output (on average, fewer :class:`~.SwapGate` gates - introduced by :class:`~.SabreSwap`). For example, running - :class:`~.SabreLayout` and :class:`~.SabreSwap` on Bernstein - Vazirani circuits targeting the :class:`~.FakeSherbrooke` backend - yields the following results: - - .. plot:: - - import time - - import numpy as np - - from qiskit.circuit import QuantumCircuit - from qiskit.providers.fake_provider import FakeSherbrooke - from qiskit.transpiler.passes import SabreLayout, SabreSwap - from qiskit.transpiler.preset_passmanagers.common import generate_embed_passmanager - from qiskit.transpiler import PassManager - - import matplotlib.pyplot as plt - - backend = FakeSherbrooke() - cmap = backend.target.build_coupling_map() - - - def build_bv_circuit(num_qubits): - qc = QuantumCircuit(num_qubits, num_qubits - 1) - for i in range(num_qubits - 1): - qc.h(i) - qc.x(num_qubits - 1) - for i in range(0, num_qubits - 1, 2): - qc.cx(i, num_qubits - 1) - for i in range(0, num_qubits - 1): - qc.measure(i, i) - return qc - - - new_sabre_pass = SabreLayout(cmap, seed=23042, swap_trials=10, layout_trials=10) - old_sabre_pass = PassManager( - SabreLayout( - cmap, - routing_pass=SabreSwap(cmap, "decay", seed=23042, fake_run=True, trials=10), - seed=23042, - ) - ) - old_sabre_pass += generate_embed_passmanager(cmap) - old_sabre_pass.append(SabreSwap(cmap, "decay", 23042, trials=5)) - - new_run_times = [] - old_run_times = [] - new_non_local_counts = [] - old_non_local_counts = [] - bv_sizes = [] - - for i in np.linspace(10, 120, dtype=int): - bv_sizes.append(i) - qc = build_bv_circuit(i) - start = time.perf_counter() - new_res = new_sabre_pass(qc) - stop = time.perf_counter() - new_run_times.append(stop - start) - new_non_local_counts.append(new_res.num_nonlocal_gates()) - start = time.perf_counter() - old_run = old_sabre_pass.run(qc) - stop = time.perf_counter() - old_run_times.append(stop - start) - old_non_local_counts.append(old_run.num_nonlocal_gates()) - - plt.plot(bv_sizes, new_non_local_counts, label="New SabreLayout") - plt.plot(bv_sizes, old_non_local_counts, label="Old SabreLayout") - plt.xlabel("Number of BV Circuit Qubits") - plt.ylabel("Number of non-local gates in output") - plt.title("Number of non-local gates after SabreLayout and SabreSwap") - plt.legend() - plt.show() - -This release also deprecates support for running with Python 3.7. A ``DeprecationWarning`` -will now be emitted if you run Qiskit with Python 3.7. Support for Python 3.7 will be removed -as part of the 0.25.0 release (currently planned for release in July 2023), at which point -you will need Python 3.8 or newer to use Qiskit. - -New Features ------------- - -.. releasenotes/notes/0.23/Symbolic-Pulses-conversion-to-amp-angle-0c6bcf742eac8945.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The pulses in :mod:`qiskit.pulse.library` - - * :class:`~qiskit.pulse.library.Gaussian` - * :class:`~qiskit.pulse.library.GaussianSquare` - * :class:`~qiskit.pulse.library.Drag` - * :class:`~qiskit.pulse.library.Constant` - - can be initialized with new parameter ``angle``, such that two float parameters could be provided: - ``amp`` and ``angle``. Initialization with complex ``amp`` is still supported. - -.. releasenotes/notes/0.23/adapt-vqe-improvements-8617aaa94a6e6621.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The :class:`~.AdaptVQE` class has a new attribute, - :attr:`~.AdaptVQEResult.eigenvalue_history`, which is used to track - the lowest achieved energy per iteration of the AdaptVQE. For example:: - - from qiskit.algorithms.minimum_eigensolvers import VQE - from qiskit.algorithms.minimum_eigensolvers.adapt_vqe import AdaptVQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import EvolvedOperatorAnsatz - from qiskit.opflow import PauliSumOp - from qiskit.primitives import Estimator - from qiskit.quantum_info import SparsePauliOp - from qiskit.utils import algorithm_globals - - excitation_pool = [ - PauliSumOp( - SparsePauliOp(["IIIY", "IIZY"], coeffs=[0.5 + 0.0j, -0.5 + 0.0j]), coeff=1.0 - ), - PauliSumOp( - SparsePauliOp(["ZYII", "IYZI"], coeffs=[-0.5 + 0.0j, 0.5 + 0.0j]), coeff=1.0 - ), - PauliSumOp( - SparsePauliOp( - ["ZXZY", "IXIY", "IYIX", "ZYZX", "IYZX", "ZYIX", "ZXIY", "IXZY"], - coeffs=[ - -0.125 + 0.0j, - 0.125 + 0.0j, - -0.125 + 0.0j, - 0.125 + 0.0j, - 0.125 + 0.0j, - -0.125 + 0.0j, - 0.125 + 0.0j, - -0.125 + 0.0j, - ], - ), - coeff=1.0, - ), - ] - ansatz = EvolvedOperatorAnsatz(excitation_pool, initial_state=self.initial_state) - optimizer = SLSQP() - h2_op = PauliSumOp.from_list( - [ - ("IIII", -0.8105479805373266), - ("ZZII", -0.2257534922240251), - ("IIZI", +0.12091263261776641), - ("ZIZI", +0.12091263261776641), - ("IZZI", +0.17218393261915543), - ("IIIZ", +0.17218393261915546), - ("IZIZ", +0.1661454325638243), - ("ZZIZ", +0.1661454325638243), - ("IIZZ", -0.2257534922240251), - ("IZZZ", +0.16892753870087926), - ("ZZZZ", +0.17464343068300464), - ("IXIX", +0.04523279994605788), - ("ZXIX", +0.04523279994605788), - ("IXZX", -0.04523279994605788), - ("ZXZX", -0.04523279994605788), - ] - ) - - algorithm_globals.random_seed = 42 - calc = AdaptVQE(VQE(Estimator(), ansatz, self.optimizer)) - res = calc.compute_minimum_eigenvalue(operator=h2_op) - - print(calc.eigenvalue_history) - - the returned value of ``calc.history`` should be roughly ``[-1.85727503]`` as - there is a single iteration. - -.. releasenotes/notes/0.23/adapt-vqe-improvements-8617aaa94a6e6621.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The runtime logging when running the :class:`~.AdaptVQE` has been improved. - When running the class now, ``DEBUG`` and ``INFO`` level log messages - will be emitted as the class runs. - -.. releasenotes/notes/0.23/add-collect-and-collapse-pass-d4411b682bd03294.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new transpiler pass, :class:`~.CollectAndCollapse`, to collect and to consolidate - blocks of nodes in a circuit. This pass is designed to be a general base class for - combined block collection and consolidation. To be completely general, the work of - collecting and collapsing the blocks is done via functions provided during - instantiating the pass. For example, the :class:`~.CollectLinearFunctions` has been - updated to inherit from :class:`~.CollectAndCollapse` and collects blocks of - :class:`.CXGate` and :class:`.SwapGate` gates, and replaces each block with a - :class:`.LinearFunction`. The :class:`~.CollectCliffords` which is also now - based on :class:`~.CollectAndCollapse`, collects blocks of "Clifford" gates and - replaces each block with a :class:`.Clifford`. - - The interface also supports the option ``do_commutative_analysis``, which allows - to exploit commutativity between gates in order to collect larger blocks of nodes. - For example, collecting blocks of CX gates in the following circuit:: - - qc = QuantumCircuit(2) - qc.cx(0, 1) - qc.z(0) - qc.cx(1, 0) - - using ``do_commutative_analysis`` enables consolidating the two CX gates, as - the first CX gate and the Z gate commute. - -.. releasenotes/notes/0.23/add-collect-and-collapse-pass-d4411b682bd03294.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new class :class:`~.BlockCollector` that implements various collection strategies, - and a new class :class:`~.BlockCollapser` that implements various collapsing strategies. - Currently :class:`~.BlockCollector` includes the strategy to greedily collect all gates - adhering to a given filter function (for example, collecting all Clifford gates), and - :class:`~.BlockCollapser` includes the strategy to consolidate all gates in a block to a - single object (or example, a block of Clifford gates can be consolidated to a single - :class:`.Clifford`). - -.. releasenotes/notes/0.23/add-collect-and-collapse-pass-d4411b682bd03294.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new :class:`~.CollectCliffords` transpiler pass that collects blocks of Clifford - gates and consolidates these blocks into :class:`qiskit.quantum_info.Clifford` objects. - This pass inherits from :class:`~.CollectAndCollapse` and in particular supports the option - ``do_commutative_analysis``. - It also supports two additional options ``split_blocks`` and ``min_block_size``. - See the release notes for :class:`~.CollectAndCollapse` and :class:`~.CollectLinearFunctions` - for additional details. - -.. releasenotes/notes/0.23/add-collect-and-collapse-pass-d4411b682bd03294.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The :class:`~.CollectLinearFunctions` transpiler pass has several new arguments - on its constructor: - - * ``do_commutative_analysis``: enables exploiting commutativity between gates - in order to collect larger blocks of nodes. - - * ``split_blocks``: enables spliting collected blocks into sub-blocks over - disjoint subsets of qubits. For example, in the following circuit:: - - qc = QuantumCircuit(4) - qc.cx(0, 2) - qc.cx(1, 3) - qc.cx(2, 0) - qc.cx(3, 1) - qc.cx(1, 3) - - the single block of CX gates over qubits ``{0, 1, 2, 3}`` can be split into two disjoint - sub-blocks, one over qubits ``{0, 2}`` and the other over qubits ``{1, 3}``. - - * ``min_block_size``: allows to specify the minimum size of the block to be consolidated, - blocks with fewer gates will not be modified. For example, in the following circuit:: - - qc = QuantumCircuit(4) - qc.cx(1, 2) - qc.cx(2, 1) - - the two CX gates will be consolidated when ``min_block_size`` is 1 or 2, and will remain unchanged - when ``min_block_size`` is 3 or larger. - -.. releasenotes/notes/0.23/add-linear-synthesis-lnn-depth-5n-36c1aeda02b8bc6f.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a depth-efficient synthesis algorithm - :func:`~.synth_cnot_depth_line_kms` - for linear reversible circuits :class:`~qiskit.circuit.library.LinearFunction` - over the linear nearest-neighbor architecture, - following the paper: https://arxiv.org/abs/quant-ph/0701194. - -.. releasenotes/notes/0.23/add-new-node-return-f2574c1593cbb57b.yaml @ None - -- The :meth:`.DAGCircuit.replace_block_with_op` method will now - return the new :class:`~.DAGOpNode` that is created when the block - is replaced. Previously, calling this method would not return anything. - -.. releasenotes/notes/0.23/add-permutation-lnn-synthesis-46dca864cebe0af3.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a depth-efficient synthesis algorithm - :func:`~.synth_permutation_depth_lnn_kms` - for :class:`~qiskit.circuit.library.Permutation` - over the linear nearest-neighbor architecture, - following the paper: https://arxiv.org/abs/quant-ph/0701194 - -.. releasenotes/notes/0.23/add-permutation-synthesis-plugins-9ab9409bc852f5de.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new class :class:`~qiskit.circuit.library.PermutationGate` for - representing permutation logic as a circuit element. Unlike the existing - :class:`~qiskit.circuit.library.Permutation` circuit library element - which had a static definition this new class avoids synthesizing a permutation - circuit when it is declared. This delays the actual synthesis to the transpiler. - It also allows enables using several different algorithms for synthesizing - permutations, which are available as high-level-synthesis - permutation plugins. - - Another key feature of the :class:`~qiskit.circuit.library.PermutationGate` is - that implements the ``__array__`` interface for efficiently returning a unitary - matrix for a permutation. - -.. releasenotes/notes/0.23/add-permutation-synthesis-plugins-9ab9409bc852f5de.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added several high-level-synthesis plugins for synthesizing permutations: - - * :class:`~.BasicSynthesisPermutation`: applies to fully-connected - architectures and is based on sorting. This is the previously used - algorithm for constructing quantum circuits for permutations. - - * :class:`~.ACGSynthesisPermutation`: applies to fully-connected - architectures but is based on the Alon, Chung, Graham method. It synthesizes - any permutation in depth 2 (measured in terms of SWAPs). - - * :class:`~.KMSSynthesisPermutation`: applies to linear nearest-neighbor - architectures and corresponds to the recently added Kutin, Moulton, Smithline - method. - - For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import PermutationGate - from qiskit.transpiler import PassManager - from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig, HighLevelSynthesis - from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPluginManager - - # Create a permutation and add it to a quantum circuit - perm = PermutationGate([4, 6, 3, 7, 1, 2, 0, 5]) - qc = QuantumCircuit(8) - qc.append(perm, range(8)) - - # Print available plugin names for synthesizing permutations - # Returns ['acg', 'basic', 'default', 'kms'] - print(HighLevelSynthesisPluginManager().method_names("permutation")) - - # Default plugin for permutations - # Returns a quantum circuit with size 6 and depth 3 - qct = PassManager(HighLevelSynthesis()).run(qc) - print(f"Default: {qct.size() = }, {qct.depth() = }") - - # KMSSynthesisPermutation plugin for permutations - # Returns a quantum circuit with size 18 and depth 6 - # but adhering to the linear nearest-neighbor architecture. - qct = PassManager(HighLevelSynthesis(HLSConfig(permutation=[("kms", {})]))).run(qc) - print(f"kms: {qct.size() = }, {qct.depth() = }") - - # BasicSynthesisPermutation plugin for permutations - # Returns a quantum circuit with size 6 and depth 3 - qct = PassManager(HighLevelSynthesis(HLSConfig(permutation=[("basic", {})]))).run(qc) - print(f"basic: {qct.size() = }, {qct.depth() = }") - - # ACGSynthesisPermutation plugin for permutations - # Returns a quantum circuit with size 6 and depth 2 - qct = PassManager(HighLevelSynthesis(HLSConfig(permutation=[("acg", {})]))).run(qc) - print(f"acg: {qct.size() = }, {qct.depth() = }") - -.. releasenotes/notes/0.23/add-qfi-with-primitive-86d935d19dfff1a1.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added new classes for Quantum Fisher Information (QFI) and Quantum - Geometric Tensor (QGT) algorithms using :mod:`~qiskit.primitives`, - :class:`qiskit.algorithms.gradients.QFI` and - :class:`qiskit.algorithms.gradients.LinCombQGT`, to the - gradients module: :mod:`qiskit.algorithms.gradients`. For example:: - - from qiskit.circuit import QuantumCircuit, Parameter - from qiskit.algorithms.gradients import LinCombQGT, QFI - - estimator = Estimator() - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - parameter_value = [[np.pi / 4, 0]] - - qgt = LinCombQGT(estimator) - qgt_result = qgt.run([qc], parameter_value).result() - - qfi = QFI(qgt) - qfi_result = qfi.run([qc], parameter_value).result() - -.. releasenotes/notes/0.23/add-qfi-with-primitive-86d935d19dfff1a1.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new keyword argument, ``derivative_type``, to the constructor for the - :class:`~qiskit.algorithms.gradients.LinCombEstimatorGradient`. This argument - takes a :class:`~.DerivativeType` enum that enables specifying to compute - only the real or imaginary parts of the gradient. - -.. releasenotes/notes/0.23/add-reverse-bits-to-user-config-options-0e465e6e92d5b49f.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new option ``circuit_reverse_bits`` to the user config file. - This allows users to set a boolean for their preferred default - behavior of the ``reverse_bits`` argument of the circuit drawers - :meth:`.QuantumCircuit.draw` and :func:`.circuit_drawer`. For example, - adding a section to the user config file in the default location - ``~/.qiskit/settings.conf`` with: - - .. code-block:: ini - - [default] - circuit_reverse_bits = True - - will change the default to display the bits in reverse order. - -.. releasenotes/notes/0.23/add-sparsepauliop-based-z2symetries-1811e956c232f664.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new class :class:`~qiskit.quantum_info.Z2Symmetries` to :mod:`qiskit.quantum_info` - which is used to identify any :math:`Z_2` symmetries from an input - :class:`~.SparsePauliOp`. - -.. releasenotes/notes/0.23/add-timeblockade-instruction-9469a5e9e0218adc.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new pulse directive :class:`~qiskit.pulse.instructions.TimeBlockade`. - This directive behaves almost identically to the delay instruction, but will - be removed before execution. This directive is intended to be used internally - within the pulse builder and helps :class:`.ScheduleBlock` represent - instructions with absolute time intervals. This allows the pulse builder to - convert :class:`Schedule` into :class:`ScheduleBlock`, rather than wrapping - with :class:`~qiskit.pulse.instructions.Call` instructions. - -.. releasenotes/notes/0.23/add-varqte-primitives-3f0ae76bc281e909.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added primitive-enabled algorithms for Variational Quantum Time Evolution that implement the - interface for Quantum Time Evolution. The :class:`qiskit.algorithms.VarQRTE` class is used - for real and the :class:`qiskit.algorithms.VarQITE` class is used for imaginary - quantum time evolution according to a variational principle passed. - - Each algorithm accepts a variational principle which implements the - :class:`~.ImaginaryVariationalPrinciple` abstract interface. The - following implementations are included: - - * :class:`~.ImaginaryMcLachlanPrinciple` - * :class:`~.RealMcLachlanPrinciple` - - For example: - - .. code-block:: python - - from qiskit.algorithms import TimeEvolutionProblem, VarQITE - from qiskit.algorithms.time_evolvers.variational import ImaginaryMcLachlanPrinciple - from qiskit.circuit.library import EfficientSU2 - from qiskit.quantum_info import SparsePauliOp - import numpy as np - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - ansatz = EfficientSU2(observable.num_qubits, reps=1) - init_param_values = np.zeros(len(ansatz.parameters)) - for i in range(len(ansatz.parameters)): - init_param_values[i] = np.pi / 2 - var_principle = ImaginaryMcLachlanPrinciple() - time = 1 - evolution_problem = TimeEvolutionProblem(observable, time) - var_qite = VarQITE(ansatz, var_principle, init_param_values) - evolution_result = var_qite.evolve(evolution_problem) - -.. releasenotes/notes/0.23/add-xxyy-equivalence-a941c9b9bc60747b.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added rules for converting :class:`.XXPlusYYGate` and - :class:`.XXMinusYYGate` to other gates to the ``SessionEquivalenceLibrary``. This enables - running :func:`~.transpile` targeting a backend or :class:`~.Target` that - uses these gates. - -.. releasenotes/notes/0.23/add_fake_prague-79f82b83c2e2329c.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added two new fake backends, :class:`~.FakePrague` and - :class:`~.FakeSherbrooke` to the :mod:`qiskit.providers.fake_provider` module. - :class:`~.FakePrague` provides a backend with a snapshot of the properties - from the IBM Prague Egret R1 backend and :class:`~.FakeSherbrooke` - provides a backend with a snapshot of the properties from the IBM - Sherbrooke Eagle R3 backend. - -.. releasenotes/notes/0.23/allow-unknown-parameters-eca32e2cfebe8c5a.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new keyword argument, ``allow_unknown_parameters``, to the - :meth:`.ParameterExpression.bind` and :meth:`.ParameterExpression.subs` - methods. When set this new argument enables passing a dictionary - containing unknown parameters to these methods without causing an error - to be raised. Previously, this would always raise an error without - any way to disable that behavior. - -.. releasenotes/notes/0.23/base-estimator-observable-validation-3addb17a2a8c9d97.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The :meth:`.BaseEstimator.run` method's ``observables`` argument now - accepts a ``str`` or sequence of ``str`` input type in addition to the - other types already accepted. When used the input string format - should match the Pauli string representation accepted by the constructor - for :class:`~.quantum_info.Pauli` objects. - -.. releasenotes/notes/0.23/circuit-from-instructions-832b43bfd2bfd921.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new constructor method :meth:`.QuantumCircuit.from_instructions` - that enables creating a :class:`~.QuantumCircuit` object from an iterable - of instructions. For example: - - .. plot:: - :include-source: - - from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister - from qiskit.circuit.quantumcircuitdata import CircuitInstruction - from qiskit.circuit import Measure - from qiskit.circuit.library import HGate, CXGate - - - qr = QuantumRegister(2) - cr = ClassicalRegister(2) - instructions = [ - CircuitInstruction(HGate(), [qr[0]], []), - CircuitInstruction(CXGate(), [qr[0], qr[1]], []), - CircuitInstruction(Measure(), [qr[0]], [cr[0]]), - CircuitInstruction(Measure(), [qr[1]], [cr[1]]), - ] - circuit = QuantumCircuit.from_instructions(instructions) - circuit.draw("mpl") - -.. releasenotes/notes/0.23/clifford-compose-performance-96808ba11327e7dd.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The :class:`.Clifford` class now takes an optional ``copy`` keyword argument in its - constructor. If set to ``False``, then a :class:`.StabilizerTable` provided - as input will not be copied, but will be used directly. This can have - performance benefits, if the data in the table will never be mutated by any - other means. - -.. releasenotes/notes/0.23/clifford-compose-performance-96808ba11327e7dd.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The performance of :meth:`.Clifford.compose` has been greatly improved for - all numbers of qubits. For operators of 20 qubits, the speedup is on the - order of 100 times. - -.. releasenotes/notes/0.23/clifford_layered_synthesis-1a6b1038458ae8c3.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new synthesis function :func:`~.synth_clifford_layers`, for - synthesizing a :class:`~.Clifford` into layers. The algorithm is based - on S. Bravyi, D. Maslov, `Hadamard-free circuits expose the structure - of the Clifford group`, `arxiv:2003.09412 `__. - This decomposes the Clifford into 8 layers of gates including two layers - of CZ gates, and one layer of CX gates. For example, a 5-qubit Clifford - circuit is decomposed into the following layers: - - .. parsed-literal:: - ┌─────┐┌─────┐┌────────┐┌─────┐┌─────┐┌─────┐┌─────┐┌────────┐ - q_0: ┤0 ├┤0 ├┤0 ├┤0 ├┤0 ├┤0 ├┤0 ├┤0 ├ - │ ││ ││ ││ ││ ││ ││ ││ │ - q_1: ┤1 ├┤1 ├┤1 ├┤1 ├┤1 ├┤1 ├┤1 ├┤1 ├ - │ ││ ││ ││ ││ ││ ││ ││ │ - q_2: ┤2 S2 ├┤2 CZ ├┤2 CX_dg ├┤2 H2 ├┤2 S1 ├┤2 CZ ├┤2 H1 ├┤2 Pauli ├ - │ ││ ││ ││ ││ ││ ││ ││ │ - q_3: ┤3 ├┤3 ├┤3 ├┤3 ├┤3 ├┤3 ├┤3 ├┤3 ├ - │ ││ ││ ││ ││ ││ ││ ││ │ - q_4: ┤4 ├┤4 ├┤4 ├┤4 ├┤4 ├┤4 ├┤4 ├┤4 ├ - └─────┘└─────┘└────────┘└─────┘└─────┘└─────┘└─────┘└────────┘ - - This method will allow to decompose a :class:`~.Clifford` in 2-qubit depth - :math:`7n+2` for linear nearest neighbor (LNN) connectivity. - -.. releasenotes/notes/0.23/efficient-gate-power-effa21e3ee4581ee.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The return types for the :meth:`~.Gate.power` methods on several standard - library gate classes have been updated to return more specific - gate objects that result in a less lossy and more efficient output. - For example, running :meth:`~.IGate.power` now returns an :class:`~.IGate` - instance instead of :class:`~.library.UnitaryGate` as was done previously. - - The full list of output types that have been improved are: - - .. list-table:: Output for :meth:`~.Gate.power` - :header-rows: 1 - - * - Gate Class - - Output Class from :meth:`~.Gate.power` - * - :class:`~.CPhaseGate` - - :class:`~.CPhaseGate` - * - :class:`~.CSGate` - - :class:`~.CPhaseGate` - * - :class:`~.CSdgGate` - - :class:`~.CPhaseGate` - * - :class:`~.IGate` - - :class:`~.IGate`. - * - :class:`~.PhaseGate` - - :class:`~.PhaseGate` - * - :class:`~.RGate` - - :class:`~.RGate` - * - :class:`~.RXGate` - - :class:`~.RXGate` - * - :class:`~.RXXGate` - - :class:`~.RXXGate` - * - :class:`~.RYGate` - - :class:`~.RYGate` - * - :class:`~.RYYGate` - - :class:`~.RYYGate` - * - :class:`~.RZGate` - - :class:`~.RZGate` - * - :class:`~.RZXGate` - - :class:`~.RZXGate` - * - :class:`~.RZZGate` - - :class:`~.RZZGate` - * - :class:`~.SdgGate` - - :class:`~.PhaseGate` - * - :class:`~.SGate` - - :class:`~.PhaseGate` - * - :class:`~.TdgGate` - - :class:`~.PhaseGate` - * - :class:`~.TGate` - - :class:`~.PhaseGate` - * - :class:`~.XXMinusYYGate` - - :class:`~.XXMinusYYGate` - * - :class:`~.XXPlusYYGate` - - :class:`~.XXPlusYYGate` - * - :class:`~.ZGate` - - :class:`~.PhaseGate` - * - :class:`~.iSwapGate` - - :class:`~.XXPlusYYGate` - -.. releasenotes/notes/0.23/equivalence-to-graph-3b52912ecb542db8.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The :class:`~.EquivalenceLibrary` is now - represented internally as a ``PyDiGraph``, this underlying graph object - can be accesed from the new :attr:`~.EquivalenceLibrary.graph` attribute. - This attribute is intended for use internally in Qiskit and therefore - should always be copied before being modified by the user to prevent - possible corruption of the internal equivalence graph. - -.. releasenotes/notes/0.23/final_layout-8178327a57b8b96a.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The :meth:`.Operator.from_circuit` constructor method now will reverse - the output permutation caused by the routing/swap mapping stage of the - transpiler. By default if a transpiled circuit had Swap gates inserted - the output matrix will have that permutation reversed so the returned - matrix will be equivalent to the original un-transpiled circuit. If you'd - like to disable this default behavior the ``ignore_set_layout`` keyword - argument can be set to ``True`` to do this (in addition to previous behavior - of ignoring the initial layout from transpilation). If you'd like to - manually set a final layout you can use the new ``final_layout`` keyword - argument to pass in a :class:`~.Layout` object to use for the output - permutation. - -.. releasenotes/notes/0.23/fix-trivial-gate-inversions-1e39293d59bc6027.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added support to the :class:`~.GateDirection` transpiler pass to - handle the the symmetric :class:`~.RXXGate`, :class:`~.RYYGate`, and - :class:`~.RZZGate` gates. The pass will now correctly handle these gates - and simply reverse the qargs order in place without any other - modifications. - -.. releasenotes/notes/0.23/gate-power-6f97f9db5c36def3.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added support for using the Python exponentiation operator, ``**``, with - :class:`~.Gate` objects is now supported. It is equivalent to running the - :meth:`.Gate.power` method on the object. - - For example:: - - from qiskit.circuit.library import XGate - - sx = XGate() ** 0.5 - -.. releasenotes/notes/0.23/gaussian-square-drag-pulse-1e54fe77e59d5247.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added new :class:`~.GaussianSquareDrag` pulse shape to the :mod:`qiskit.pulse.library` - module. This pulse shape is similar to :class:`~.GaussianSquare` but uses - the :class:`~.Drag` shape during its rise and fall. The correction - from the DRAG pulse shape can suppress part of the frequency spectrum of - the rise and fall of the pulse which can help avoid exciting spectator - qubits when they are close in frequency to the drive frequency of the - pulse. - -.. releasenotes/notes/0.23/gradient-methods-b2ec34916b83c17b.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new keyword argument, ``method``, to the constructors for the - :class:`.FiniteDiffEstimatorGradient` and :class:`.FiniteDiffSamplerGradient` - classes. The ``method`` argument accepts a string to indicate the - computation method to use for the gradient. There are three methods, - available ``"central"``, ``"forward"``, and ``"backward"``. The - definition of the methods are: - - .. list-table:: - :header-rows: 1 - - * - Method - - Computation - * - ``"central"`` - - :math:`\frac{f(x+e)-f(x-e)}{2e}` - * - ``"forward"`` - - :math:`\frac{f(x+e) - f(x)}{e}` - * - ``"backward"`` - - :math:`\frac{f(x)-f(x-e)}{e}` - - where :math:`e` is the offset epsilon. - -.. releasenotes/notes/0.23/gradients-preserve-unparameterized-8ebff145b6c96fa3.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- All gradient classes in :mod:`qiskit.algorithms.gradients` now preserve unparameterized - operations instead of attempting to unroll them. This allows to evaluate gradients on - custom, opaque gates that individual primitives can handle and keeps a higher - level of abstraction for optimized synthesis and compilation after the gradient circuits - have been constructed. - -.. releasenotes/notes/0.23/gradients-preserve-unparameterized-8ebff145b6c96fa3.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Added a :class:`.TranslateParameterizedGates` pass to map only parameterized gates in a - circuit to a specified basis, but leave unparameterized gates untouched. The pass first - attempts unrolling and finally translates if a parameterized gate cannot be further unrolled. - -.. releasenotes/notes/0.23/improve-collect-cliffords-f57aeafe95460b18.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The :class:`~.CollectCliffords` transpiler pass has been expanded to collect - and combine blocks of "clifford gates" into :class:`.Clifford` objects, where - "clifford gates" may now also include objects of type :class:`.LinearFunction`, - :class:`~.Clifford`, and :class:`~.PauliGate`. - For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import LinearFunction, PauliGate - from qiskit.quantum_info.operators import Clifford - from qiskit.transpiler.passes import CollectCliffords - from qiskit.transpiler import PassManager - - # Create a Clifford - cliff_circuit = QuantumCircuit(2) - cliff_circuit.cx(0, 1) - cliff_circuit.h(0) - cliff = Clifford(cliff_circuit) - - # Create a linear function - lf = LinearFunction([[0, 1], [1, 0]]) - - # Create a pauli gate - pauli_gate = PauliGate("XYZ") - - # Create a quantum circuit with the above and also simple clifford gates. - qc = QuantumCircuit(4) - qc.cz(0, 1) - qc.append(cliff, [0, 1]) - qc.h(0) - qc.append(lf, [0, 2]) - qc.append(pauli_gate, [0, 2, 1]) - qc.x(2) - - # Run CollectCliffords transpiler pass - qct = PassManager(CollectCliffords()).run(qc) - - All the gates will be collected and combined into a single :class:`~.Clifford`. Thus the final - circuit consists of a single :class:`~.Clifford` object. - -.. releasenotes/notes/0.23/iterable-couplingmap-b8f0cbb1b34a2005.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- :class:`.CouplingMap` is now implicitly iterable, with the iteration being - identical to iterating through the output of :meth:`.CouplingMap.get_edges()`. - In other words, - - .. code-block:: python - - from qiskit.transpiler import CouplingMap - coupling = CouplingMap.from_line(3) - list(coupling) == list(coupling.get_edges()) - - will now function as expected, as will other iterations. This is purely a - syntactic convenience. - -.. releasenotes/notes/0.23/linear_function_synthesis_utils-f2f96924ca45e1fb.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new function :func:`~.synth_cnot_count_full_pmh` which is used to - synthesize linear reversible circuits for all-to-all architectures - using the Patel, Markov and Hayes method. This function is identical to - the available ``qiskit.transpiler.synthesis.cnot_synth()`` - function but has a more descriptive name and is more logically placed - in the package tree. This new function supersedes the legacy function - which will likely be deprecated in a future release. - -.. releasenotes/notes/0.23/load-backend-fast-9030885adcd9248f.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- :class:`.InstructionScheduleMap` has been updated to store backend calibration data - in the format of PulseQobj JSON and invokes conversion when the data is accessed - for the first time, i.e. lazy conversion. - This internal logic update drastically improves the performance of loading backend - especially with many calibration entries. - -.. releasenotes/notes/0.23/load-backend-fast-9030885adcd9248f.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- New module :mod:`qiskit.pulse.calibration_entries` has been added. This - contains several wrapper classes for different pulse schedule representations. - - * :class:`~.ScheduleDef` - * :class:`~.CallableDef` - * :class:`~.PulseQobjDef` - - These classes implement the :meth:`~.ScheduleDef.get_schedule` and - :meth:`~.ScheduleDef.get_signature` methods that returns pulse schedule - and parameter names to assign, respectively. These classes are internally - managed by the :class:`.InstructionScheduleMap` or backend :class:`~.Target`, - and thus they will not appear in a typical user programs. - -.. releasenotes/notes/0.23/new_pulse_subclass-44da774612699312.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Introduced a new subclass :class:`~qiskit.pulse.library.ScalableSymbolicPulse`, as a - subclass of :class:`~qiskit.pulse.library.SymbolicPulse`. The new subclass behaves - the same as :class:`~qiskit.pulse.library.SymbolicPulse`, - except that it assumes that the envelope of the pulse includes a complex amplitude - pre-factor of the form :math:`\text{amp} * e^{i \times \text{angle}}`. - This envelope shape matches many common pulses, including all of the pulses in - the Qiskit Pulse library (which were also converted to ``amp``, ``angle`` representation in - this release). - - The new subclass removes the non-unique nature of the ``amp``, ``angle`` representation, - and correctly compares pulses according to their complex amplitude. - -.. releasenotes/notes/0.23/pauli-sum-op-dtype-cd09b4c6521aeb42.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added a new keyword argument, ``dtype``, to the :meth:`.PauliSumOp.from_list` - method. When specified this argument can be used to specify the ``dtype`` - of the numpy array allocated for the :class:`~.SparsePauliOp` used - internally by the constructed :class:`~.PauliSumOp`. - -.. releasenotes/notes/0.23/qasm3-import-0e7e01cb75aa6251.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Support for importing OpenQASM 3 programs into Qiskit has been added. This can most easily be - accessed using the functions :func:`.qasm3.loads` and :func:`.qasm3.load`, to load a program - directly from a string and indirectly from a filename, respectively. For example, one can now - do:: - - from qiskit import qasm3 - - circuit = qasm3.loads(""" - OPENQASM 3.0; - include "stdgates.inc"; - - qubit q; - qubit[5] qr; - bit c; - bit[5] cr; - - h q; - c = measure q; - - if (c) { - h qr[0]; - cx qr[0], qr[1]; - cx qr[0], qr[2]; - cx qr[0], qr[3]; - cx qr[0], qr[4]; - } else { - h qr[4]; - cx qr[4], qr[3]; - cx qr[4], qr[2]; - cx qr[4], qr[1]; - cx qr[4], qr[0]; - } - cr = measure qr; - """) - - This will load the program into a :class:`.QuantumCircuit` instance in the variable ``circuit``. - - Not all OpenQASM 3 features are supported at first, because Qiskit does not yet have a way to - represent advanced classical data processing. The capabilities of the importer will increase - along with the capabilities of the rest of Qiskit. The initial feature set of the importer is - approximately the same set of features that would be output by the exporter (:func:`.qasm3.dump` - and :func:`.qasm3.dumps`). - - Note that Qiskit's support of OpenQASM 3 is not meant to provide a totally lossless - representation of :class:`.QuantumCircuit`\ s. For that, consider using :mod:`qiskit.qpy`. - -.. releasenotes/notes/0.23/refactor-gradient-d6d315cb256a17db.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The :mod:`~.qiskit.primitives`\ -based gradient classes defined by the - :class:`~.BaseEstimatorGradient` and :class:`~.BaseSamplerGradient` - abstract classes have been updated to simplify extending the base - interface. There are three new internal overridable methods, ``_preprocess()``, - ``_postprocess()``, and ``_run_unique()``. ``_preprocess()`` enables - a subclass to customize the input gradient circuits and parameters, - ``_postprocess`` enables to customize the output result, and - ``_run_unique`` enables calculating the gradient of a circuit with - unique parameters. - -.. releasenotes/notes/0.23/rusty-sabre-layout-2e1ca05d1902dcb5.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The :class:`~.SabreLayout` transpiler pass has greatly improved performance - as it has been re-written in Rust. As part of this rewrite the pass has been - transformed from an analysis pass to a transformation pass that will run both - layout and routing. This was done to not only improve the runtime performance - but also improve the quality of the results. The previous functionality of the - pass as an analysis pass can be retained by manually setting the ``routing_pass`` - argument or using the new ``skip_routing`` argument. - -.. releasenotes/notes/0.23/rusty-sabre-layout-2e1ca05d1902dcb5.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The :class:`~.SabreLayout` transpiler pass has a new constructor argument - ``layout_trials``. This argument is used to control how many random number - generator seeds will be attempted to run :class:`~.SabreLayout` with. When - set the SABRE layout algorithm is run ``layout_trials`` number of times and - the best quality output (measured in the lowest number of swap gates added) - is selected. These seed trials are executed in parallel using multithreading - to minimize the potential performance overhead of running layout multiple - times. By default if this is not specified the :class:`~.SabreLayout` - pass will default to using the number of physical CPUs are available on the - local system. - -.. releasenotes/notes/0.23/scipy-evolvers-ca92bcb90e90b035.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added two new classes :class:`~.SciPyRealEvolver` and - :class:`~.SciPyImaginaryEvolver` that implement integration methods - for time evolution of a quantum state. - The value and standard deviation of observables as well as the times they are - evaluated at can be queried as :attr:`.TimeEvolutionResult.observables` and - :attr:`.TimeEvolutionResult.times`. - For example: - - .. code-block:: python - - from qiskit.algorithms.time_evolvers.time_evolution_problem import TimeEvolutionProblem - from qiskit.quantum_info import SparsePauliOp - from qiskit.quantum_info.states.statevector import Statevector - from qiskit.algorithms import SciPyImaginaryEvolver - - initial_state = Statevector.from_label("+++++") - hamiltonian = SparsePauliOp("ZZZZZ") - evolution_problem = TimeEvolutionProblem(hamiltonian, 100, initial_state, {"Energy":hamiltonian}) - classic_evolver = SciPyImaginaryEvolver(num_timesteps=300) - result = classic_evolver.evolve(evolution_problem) - print(result.observables) - -.. releasenotes/notes/0.23/solovay-kitaev-transpiler-pass-bc256c2f3aac28c6.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Added the :class:`.SolovayKitaev` transpiler pass to run the Solovay-Kitaev algorithm for - approximating single-qubit unitaries using a discrete gate set. In combination with the basis - translator, this allows to convert any unitary circuit to a universal discrete gate set, - which could be implemented fault-tolerantly. - - This pass can e.g. be used after compiling to U and CX gates: - - .. code-block:: - - from qiskit import transpile - from qiskit.circuit.library import QFT - from qiskit.transpiler.passes.synthesis import SolovayKitaev - - qft = QFT(3) - - # optimize to general 1-qubit unitaries and CX - transpiled = transpile(qft, basis_gates=["u", "cx"], optimization_level=1) - - skd = SolovayKitaev() # uses T Tdg and H as default basis - discretized = skd(transpiled) - - print(discretized.count_ops()) - - The decomposition can also be used with the unitary synthesis plugin, as - the "sk" method on the :class:`~.UnitarySynthesis` transpiler pass: - - .. plot:: - :include-source: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import Operator - from qiskit.transpiler.passes import UnitarySynthesis - - circuit = QuantumCircuit(1) - circuit.rx(0.8, 0) - unitary = Operator(circuit).data - - unitary_circ = QuantumCircuit(1) - unitary_circ.unitary(unitary, [0]) - - synth = UnitarySynthesis(basis_gates=["h", "s"], method="sk") - out = synth(unitary_circ) - - out.draw('mpl') - -.. releasenotes/notes/0.23/speedup-random-circuits-8d3b724cce1faaad.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Random-circuit generation with :func:`qiskit.circuit.random.random_circuit` is - now significantly faster for large circuits. - -.. releasenotes/notes/0.23/speedup-random-circuits-8d3b724cce1faaad.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Random-circuit generation with :func:`qiskit.circuit.random.random_circuit` will now output all - "standard" gates in Qiskit's circuit library (:mod:`qiskit.circuit.library`). This includes two - 4-qubit gates :class:`.C3SXGate` and :class:`.RC3XGate`, and the allowed values of - ``max_operands`` have been expanded accordingly. - -.. releasenotes/notes/0.23/target-aware-optimize-1q-decomposition-cb9bb4651607b639.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The :class:`~.Optimize1qGatesDecomposition` transpiler pass has a new keyword - argument, ``target``, on its constructor. This argument can be used to - specify a :class:`~.Target` object that represnts the compilation target. - If used it superscedes the ``basis`` argument to determine if an - instruction in the circuit is present on the target backend. - -.. releasenotes/notes/0.23/target-aware-unroll-custom-definitions-a1b839de199ca048.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The :class:`~.UnrollCustomDefinitions` transpiler pass has a new keyword - argument, ``target``, on its constructor. This argument can be used to - specify a :class:`~.Target` object that represnts the compilation target. - If used it superscedes the ``basis_gates`` argument to determine if an - instruction in the circuit is present on the target backend. - -.. releasenotes/notes/0.23/turbo-gradients-5bebc6e665b900b2.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Added the :class:`.ReverseEstimatorGradient` class for a classical, fast evaluation of - expectation value gradients based on backpropagation or reverse-mode gradients. - This class uses statevectors and thus provides exact gradients but scales - exponentially in system size. It is designed for fast reference calculation of smaller system - sizes. It can for example be used as:: - - from qiskit.circuit.library import EfficientSU2 - from qiskit.quantum_info import SparsePauliOp - from qiskit.algorithms.gradients import ReverseEstimatorGradient - - observable = SparsePauliOp.from_sparse_list([("ZZ", [0, 1], 1)], num_qubits=10) - circuit = EfficientSU2(num_qubits=10) - values = [i / 100 for i in range(circuit.num_parameters)] - gradient = ReverseEstimatorGradient() - - result = gradient.run([circuit], [observable], [values]).result() - -.. releasenotes/notes/0.23/use_dag-one-qubit-euler-decomposer-6df00931384b14bd.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Added a new keyword argument, ``use_dag`` to the constructor for the - :class:`~.OneQubitEulerDecomposer` class. When ``use_dag`` is set to - ``True`` the output from the decomposer will be a :class:`~.DAGCircuit` - object instead of :class:`~.QuantumCircuit` object. This is useful - for transpiler passes that use :class:`~.OneQubitEulerDecomposer` (such - as :class:`~.Optimize1qGatesDecomposition`) as working directly with - a :class:`~.DAGCircuit` avoids the overhead of converting between - :class:`~.QuantumCircuit` and :class:`~.DAGCircuit`. - -.. releasenotes/notes/0.23/vf2_custom_score_analysis-abb191d56c0c1578.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Added the ability for analysis passes to set custom heuristic weights - for the :class:`~.VF2Layout` and :class:`~.VF2PostLayout` transpiler - passes. If an analysis pass sets the ``vf2_avg_error_map`` key in the - property set, its value is used for the error weights instead of - the error rates from the backend's :class:`~.Target` (or - :class:`~.BackendProperties` for :class:`~.BackendV1`). The value should be - an :class:`~.ErrorMap` instance, where each value represents the avg error rate - for all 1 or 2 qubit operation on those qubits. If a value is ``NaN``, the - corresponding edge is treated as an ideal edge (or qubit for 1q operations). - For example, an error map created as:: - - from qiskit.transpiler.passes.layout.vf2_utils import ErrorMap - - error_map = ErrorMap(3) - error_map.add_error((0, 0), 0.0024) - error_map.add_error((0, 1), 0.01) - error_map.add_error((1, 1), 0.0032) - - describes a 2 qubit target, where the avg 1q error - rate is ``0.0024`` on qubit 0 and ``0.0032`` on qubit 1, the avg 2q - error rate for gates that operate on (0, 1) is 0.01, and (1, 0) is not - supported by the target. This will be used for scoring if it's set for the - ``vf2_avg_error_map`` key in the property set when :class:`~.VF2Layout` and - :class:`~.VF2PostLayout` are run. For example:: - - from qiskit.transpiler import AnalysisPass, PassManager, Target - from qiskit.transpiler.passes import VF2Layout - from qiskit.transpiler.passes.layout.vf2_utils import ErrorMap - from qiskit.circuit.library import CZGate, UGate - from qiskit.circuit import Parameter - - class CustomVF2Scoring(AnalysisPass): - """Set custom score for vf2.""" - - def run(self, dag): - error_map = ErrorMap(3) - error_map.add_error((0, 0), 0.0024) - error_map.add_error((0, 1), 0.01) - error_map.add_error((1, 1), 0.0032) - self.property_set["vf2_avg_error_map"] = error_map - - - target = Target(num_qubits=2) - target.add_instruction( - UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')), - {(0,): None, (1,): None} - ) - target.add_instruction( - CZGate(), {(0, 1): None} - ) - - vf2_pass = VF2Layout(target=target, seed=1234568942) - pm = PassManager([CustomVF2Scoring(), vf2_pass]) - - That will run :class:`~.VF2Layout` with the custom scoring from ``error_map`` for - a 2 qubit :class:`~.Target` that doesn't contain any error rates. - - -.. _Release Notes_0.23.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.23/Symbolic-Pulses-conversion-to-amp-angle-0c6bcf742eac8945.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- When initializing any of the pulse classes in :mod:`qiskit.pulse.library`: - - * :class:`~qiskit.pulse.library.Gaussian` - * :class:`~qiskit.pulse.library.GaussianSquare` - * :class:`~qiskit.pulse.library.Drag` - * :class:`~qiskit.pulse.library.Constant` - - providing a complex ``amp`` argument with a finite ``angle`` will result in - :class:`~.PulseError` now. For example, instead of calling ``Gaussian(duration=100,sigma=20,amp=0.5j)`` one - should use ``Gaussian(duration=100,sigma=20,amp=0.5,angle=np.pi/2)`` instead now. The pulse envelope - which used to be defined as ``amp * ...`` is in turn defined as ``amp * exp(1j * angle) * ...``. - This change was made to better support `Qiskit Experiments `__ - where the amplitude and angle of pulses are calibrated in separate experiments. - -.. releasenotes/notes/0.23/add-singledispatchmethod-78ff14b1ef25ef99.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- For Python 3.7 `singledispatchmethod `__ - is now a dependency. This was added to enable leveraging the method dispatch - mechanism in the standard library of newer versions of Python. If you're on - Python >= 3.8 there is no extra dependency required. - -.. releasenotes/notes/0.23/drop-ms-basis-pass-19721ea1fdfee713.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The previously deprecated ``MSBasisDecomposer`` transpiler pass available - via the :mod:`qiskit.transpiler.passes` module has been removed. It was - originally deprecated as part of the Qiskit Terra 0.16.0 release - (10-16-2020). Instead the :class:`~.BasisTranslator` transpiler pass - should be used instead to translate a circuit into an appropriate basis - with a :class:`~.RXXGate` - -.. releasenotes/notes/0.23/equivalence-to-graph-3b52912ecb542db8.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- :class:`~.EquivalenceLibrary` objects that are initialized with the ``base`` - attribute will no long have a shared reference with the - :class:`~.EquivalenceLibrary` passed in. In earlier releases if you mutated - ``base`` after it was used to create a new :class:`~.EquivalenceLibrary` - instance both instances would reflect that change. This no longer is the case - and updates to ``base`` will no longer be reflected in the new - :class:`~.EquivalenceLibrary`. For example, if you created an equivalence library - with:: - - import math - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import XGate - from qiskit.circuit.equivalence import EquivalenceLibrary - - original_lib = EquivalenceLibrary() - qc = QuantumCircuit(1) - qc.rx(math.pi, 0) - original_lib.add_equivalence(XGate(), qc) - new_lib = EquivalenceLibrary(base=original_lib) - - if you modified ``original_lib`` with:: - - import from qiskit.circuit.library import SXGate - - qc = QuantumCircuit(1) - qc.rx(math.pi / 2, 0) - original_lib.add_equivalence(SXGate(), qc) - - in previous releases ``new_lib`` would also include the definition of ``SXGate`` - after it was added to ``original_lib``, but in this release this no longer will - be the case. This change was made because of the change in internal data - structure to be a graph, which improved performance of the - :class:`~.EquivalenceLibrary` class, especially when there are multiple runs of - the :class:`~.BasisTranslator` transpiler pass. - -.. releasenotes/notes/0.23/initial_state-8e20b04fc2ec2f4b.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The ``initial_state`` argument for the constructor of the - :class:`~.NLocal` class along with assigning directly to - the :class:`.NLocal.initial_state` atrribute must be a - :class:`~.QuantumCircuit` now. Support for using other types - for this argument and attribute is no longer supported. Support - for other types was deprecated as part of the Qiskit Terra 0.18.0 - release (July 2021). - -.. releasenotes/notes/0.23/latex-refactor-0745471ddecac605.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The LaTeX array drawers (e.g. ``array_to_latex``, - ``Statevector.draw('latex')``) now use the same sympy function as the - ket-convention drawer. This means it may render some numbers differently - to previous releases, but will provide a more consistent experience. - For example, it may identify new factors, or rationalize denominators where - it did not previously. The default ``precision`` has been changed from 5 to - 10. - -.. releasenotes/notes/0.23/new_pulse_subclass-44da774612699312.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The QPY version format version emitted by :func:`~.qpy.dump` has been - increased to version 6. This new format version is incompatible with the - previous versions and will result in an error when trying to load it with - a deserializer that isn't able to handle QPY version 6. This change was - necessary to support the introduction of :class:`~qiskit.pulse.library.ScalableSymbolicPulse` - which was handled by adding a ``class_name_size`` attribute to the header - of the dumped :class:`~qiskit.pulse.library.SymbolicPulse` objects. - -.. releasenotes/notes/0.23/new_pulse_subclass-44da774612699312.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The ``__hash__`` method for the :class:`~qiskit.pulse.library.SymbolicPulse` was removed. - This was done to reflect the mutable nature (via parameter assignment) of this class - which could result in errors when using :class:`~qiskit.pulse.library.SymbolicPulse` - in situtations where a hashable object was required. This means the builtin ``hash()`` - method and using :class:`~qiskit.pulse.library.SymbolicPulse` as keys in dictionaries - or set members will no longer work. - -.. releasenotes/notes/0.23/relax-register-naming-0e7d2dba9bf7fb38.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The names of :class:`.Register` instances (which includes instances of - :class:`~.QuantumRegister` and :class:`~.ClassicalRegigster`) are no longer constrained to be - valid OpenQASM 2 identifiers. This is being done as the restriction is - overly strict as Qiskit becomes more decoupled from OpenQASM 2, and even the - OpenQASM 3 specification is not so restrictive. If you were relying on - registers having valid OpenQASM 2 identifier names, you will need to begin - escaping the names. A simplistic version of this could be done, for example, - by:: - - import re - import string - - def escape(name: str) -> str: - out = re.sub(r"\W", "_", name, flags=re.ASCII) - if not out or out[0] not in string.ascii_lowercase: - return "reg_" + out - return out - -.. releasenotes/notes/0.23/remove-deprecated-circuit-methods-3e4eb27c4709ba12.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The :class:`.QuantumCircuit` methods ``u1``, ``u2``, ``u3``, and their - controlled variants ``cu1``, ``cu3`` and ``mcu1`` have been removed following - their deprecation in Qiskit Terra 0.16.0. This was to remove gate names - that were usually IBM-specific, in favour of the more general methods :meth:`~.QuantumCircuit.p`, - :meth:`~.QuantumCircuit.u`, :meth:`~.QuantumCircuit.cp` and :meth:`~.QuantumCircuit.cu`. - The gate classes :class:`.U1Gate`, :class:`.U2Gate` and :class:`.U3Gate` - are still available for use with :meth:`.QuantumCircuit.append`, so backends - can still support bases with these gates explicitly given. - -.. releasenotes/notes/0.23/remove-deprecated-circuit-methods-3e4eb27c4709ba12.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The :class:`.QuantumCircuit` methods ``combine`` and ``extend`` have been - removed following their deprecation in Qiskit Terra 0.17.0. This was done - because these functions were simply less powerful versions of - :meth:`.QuantumCircuit.compose`, which should be used instead. - - The removal of ``extend`` also means that the ``+`` and ``+=`` operators are - no longer defined for :class:`.QuantumCircuit`. Instead, you can use the - ``&`` and ``&=`` operators respectively, which use - :meth:`.QuantumCircuit.compose`. - -.. releasenotes/notes/0.23/remove-deprecated-ops-d01b83362c3557ca.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The previously deprecated functions: ``qiskit.circuit.measure.measure()`` - and ``qiskit.circuit.reset.reset()`` have been removed. These functions - were deprecated in the Qiskit Terra 0.19.0 release (December, 2021). - Instead you should use the :meth:`.QuantumCircuit.measure` and - :meth:`.QuantumCircuit.reset` methods of the :class:`~.QuantumCircuit` - object you wish to append a :class:`~.Measure` or :class:`~.Reset` - operation to. - -.. releasenotes/notes/0.23/remove-deprecated-parameterview-cc08100049605b73.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The previously deprecated :class:`.ParameterView` methods which were - inherited from ``set`` have been removed from :class:`.ParameterView`, - the type returned by :attr:`.QuantumCircuit.parameters`. The specific - methods which have been removed are: - - * ``add()`` - * ``difference()`` - * ``difference_update()`` - * ``discard()`` - * ``intersection()`` - * ``intersection_update()`` - * ``issubset()`` - * ``issuperset()`` - * ``symmetric_difference()`` - * ``symmetric_difference_update()`` - * ``union()`` - * ``update()`` - - along with support for the Python operators: - - * ``ixor``: ``^=`` - * ``isub``: ``-=`` - * ``ior``: ``|=`` - - These were deprecated in the Qiskit Terra 0.17.0 release (April, 2021). - The :class:`.ParameterView` type is now a general sequence view type and doesn't - support these ``set`` operations any longer. - -.. releasenotes/notes/0.23/remove-networkx-converters-0a7eccf6fa847975.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The previously deprecated `NetworkX `_ converter - methods for the :class:`~.DAGCircuit` and :class:`~.DAGDependency` - classes: :meth:`.DAGCircuit.to_networkx`, - :meth:`.DAGCircuit.from_networkx`, and :meth:`.DAGDependency.to_networkx` - have been removed. These methods were originally deprecated as part of - the Qiskit Terra 0.21.0 release (June, 2022). Qiskit has been using - `rustworkx `__ as its graph - library since the qiskit-terra 0.12.0 release and since then the NetworkX - converter function have been a lossy process. They were originally added so - that users could leverage NetworkX's algorithms library to leverage - functionality not present in :class:`~.DAGCircuit` and/or rustworkx. However, - since that time both :class:`~.DAGCircuit` and rustworkx has matured and - offers more functionality and the :class:`~.DAGCircuit` is tightly - coupled to rustworkx for its operation and having these converter methods - provided limited functionality and therefore have been removed. - -.. releasenotes/notes/0.23/remove-tweedledum-0f21ca327a782bc3.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- ``tweedledum`` has been removed as a core requirement of Qiskit Terra. The - functionality provided (:mod:`qiskit.circuit.classicalfunction`) is still - available, if ``tweedledum`` is installed manually, such as by:: - - pip install tweedledum - - This change was made because ``tweedledum`` development has slowed to the - point of not keeping up with new Python and OS releases, and was blocking - some Qiskit users from installing Qiskit. - -.. releasenotes/notes/0.23/remove-visualization-optionals-e4c3ed415bc1bbbe.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The lazy optional checkers :data:`.HAS_MATPLOTLIB`, :data:`.HAS_PIL`, :data:`.HAS_PYLATEX` and - :data:`.HAS_PDFTOCAIRO` are no longer exposed from :mod:`qiskit.visualization`, having been - deprecated in Qiskit Terra 0.21. The canonical location for these (and many other lazy checkers) - is :mod:`qiskit.utils.optionals`, and all four objects can be found there. - -.. releasenotes/notes/0.23/remove_gates_to_decompose-7099068d2886dce2.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The previously deprecated ``gate`` argument to the constructor of the - :class:`~.Decompose` transpiler pass, along with its matching attribute - ``Decompose.gate`` have been removed. The argument and attribute were - deprecated as part of the Qiskit Terra 0.19.0 release (December, 2021). - Instead the ``gates_to_decompose`` argument for the constructor along - with the :attr:`.Decompose.gates_to_decompose` attribute should be used - instead. The ``gates_to_decompose`` argument and attribute should function - the same, but has a more explicit name and also enables specifying lists - of gates instead of only supporting a single gate. - -.. releasenotes/notes/0.23/remove_mcmt_label-cee8a11e0164f8e1.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The previously deprecated ``label`` argument for the constructor of the - :class:`~.MCMT` and :class:`~.MCMTVChain` classes has been removed. - It was deprecated as of the Qiskit Terra 0.19.0 release (Decemeber, 2021). - Using the ``label`` argument on these classes was undefined behavior - as they are subclasses of :class:`~.QuantumCircuit` instead of - :class:`~.circuit.Instruction`. This would result in the assigned label generally - being ignored. If you need to assign a ``label`` to an - instance of :class:`~.MCMT` or :class:`~.MCMTVChain` you should convert - them to an :class:`~.Gate` instance with :meth:`~.QuantumCircuit.to_gate` - and then assign the desired label to :attr:`~.Gate.label` attribute. For - example:: - - from qiskit.circuit.library import MCMT, XGate - - mcmt_circuit = MCMT(XGate(), 3, 2) - mcmt_gate = mcmt_circuit.to_gate() - mcmt_gate.label = "Custom MCMT X" - -.. releasenotes/notes/0.23/rustworkx-not-retworkx-b7c4da600df58701.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The ``retworkx`` dependency for Qiskit has been removed and replaced by - ``rustworkx`` library. These are the same packages, but ``rustworkx`` is - the new name for ``retworkx`` which was renamed as part of their combined - 0.12.0 release. If you were previously using retworkx 0.12.0 with Qiskit - then you already installed rustworkx (retworkx 0.12.0 was just a redirect - shim for backwards compatibility). This change was made to migrate to the - new package name which will be the only supported package in the future. - -.. releasenotes/notes/0.23/rusty-sabre-layout-2e1ca05d1902dcb5.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The default behavior of the :class:`~.SabreLayout` compiler pass has - changed. The pass is no longer an :class:`~.AnalysisPass` and by default - will compute the initital layout, apply it to the circuit, and will - also run :class:`~.SabreSwap` internally and apply the swap mapping - and set the ``final_layout`` property set with the permutation caused - by swap insertions. This means for users running :class:`~.SabreLayout` - as part of a custom :class:`~.PassManager` will need to adjust the pass - manager to account for this (unless they were setting the ``routing_pass`` - argument for :class:`~.SabreLayout`). This change was made in the interest - of improving the quality output, the layout and routing quality are highly - coupled and :class:`~.SabreLayout` will now run multiple parallel seed - trials and to calculate which seed provides the best results it needs to - perform both the layout and routing together. There are three ways you can - adjust the usage in your custom pass manager. The first is to avoid using - embedding in your preset pass manager. If you were previously running something - like:: - - from qiskit.transpiler import PassManager - from qiskit.transpiler.preset_passmanagers import common - from qiskit.transpiler.passes.SabreLayout - - pm = PassManager() - pm.append(SabreLayout(coupling_map) - pm += common.generate_embed_passmanager(coupling_map) - - to compute the layout and then apply it (which was typically followed by routing) - you can adjust the usage to just simply be:: - - - from qiskit.transpiler import PassManager - from qiskit.transpiler.preset_passmanagers import common - from qiskit.transpiler.passes.SabreLayout - - pm = PassManager() - pm.append(SabreLayout(coupling_map) - - as :class:`~.SabreLayout` will apply the layout and you no longer need the embedding - stage. Alternatively, you can specify the ``routing_pass`` argument which will revert - :class:`~.SabreLayout` to its previous behavior. For example, if you want to run - :class:`~.SabreLayout` as it was run in previous releases you can do something like:: - - from qiskit.transpiler.passes import SabreSwap, SabreLayout - routing_pass = SabreSwap( - coupling_map, "decay", seed=seed, fake_run=True - ) - layout_pass = SabreLayout(coupling_map, routing_pass=routing_pass, seed=seed) - - which will have :class:`~.SabreLayout` run as an analysis pass and just set - the ``layout`` property set. The final approach is to leverage the ``skip_routing`` - argument on :class:`~SabreLayout`, when this argument is set to ``True`` it will - skip applying the found layout and inserting the swap gates from routing. However, - doing this has a runtime penalty as :class:`~SabreLayout` will still be computing - the routing and just does not use this data. The first two approaches outlined do - not have additional overhead associated with them. - -.. releasenotes/notes/0.23/rusty-sabre-layout-2e1ca05d1902dcb5.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The layouts computed by the :class:`~.SabreLayout` pass (when run without - the ``routing_pass`` argument) with a fixed seed value may change from - previous releases. This is caused by a new random number generator being - used as part of the rewrite of the :class:`~.SabreLayout` pass in Rust which - significantly improved the performance. If you rely on having consistent - output you can run the pass in an earlier version of Qiskit and leverage - :mod:`qiskit.qpy` to save the circuit and then load it using the current - version. Alternatively you can explicitly set the ``routing_pass`` argument - to an instance of :class:`~.SabreSwap` to mirror the previous behavior - of :class:`~.SabreLayout`:: - - from qiskit.transpiler.passes import SabreSwap, SabreLayout - - - routing_pass = SabreSwap( - coupling_map, "decay", seed=seed, fake_run=True - ) - layout_pass = SabreLayout(coupling_map, routing_pass=routing_pass, seed=seed) - - which will mirror the behavior of the pass in the previous release. Note, that if you - were using the ``swap_trials`` argument on :class:`~.SabreLayout` in previous releases - when adjusting the usage to this form that you will need to set ``trials`` argument - on the :class:`~.SabreSwap` constructor if you want to retain the previous output with - a fixed seed. - -.. releasenotes/notes/0.23/speedup-random-circuits-8d3b724cce1faaad.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The exact circuit returned by ``qiskit.circuit.random.random_circuit`` for a - given seed has changed. This is due to efficiency improvements in the - internal random-number generation for the function. - -.. releasenotes/notes/0.23/toqm-extra-0.1.0-4fedfa1ff0fedfa0.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The version requirement for the optional feature package ``qiskit-toqm``, - installable via ``pip install qiskit-terra[toqm]``, has been upgraded from - version ``0.0.4`` to ``0.1.0``. To use the ``toqm`` routing method - with :func:`~.transpile` you must now use qiskit-toqm version - ``0.1.0`` or newer. Older versions are no longer discoverable by - the transpiler. - -.. releasenotes/notes/0.23/update-sampler-zero-filter-8bf0d721af4fbd17.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The output :class:`~.QuasiDistribution` from the :class:`.Sampler.run` - method has been updated to filter out any states with a probability of - zero. Now if a valid state is missing from the dictionary output it can - be assumed to have a 0 probability. Previously, all possible outcomes for - a given number of bits (e.g. for a 3 bit result ``000``, ``001``, - ``010``, ``011``, ``100``, ``101``, ``110``, and ``111``) even if the - probability of a given state was 0. This change was made to reduce the - size of the output as for larger number of bits the output size could be - quite large. Also, filtering the zero probability results makes the output - consistent with other implementations of :class:`~.BaseSampler`. - -.. releasenotes/notes/0.23/upgrade-pulse-builder-and-rzx-builder-033ac8ad8ad2a192.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The behavior of the pulse builder when a :class:`.Schedule` is called - has been upgraded. Called schedules are internally converted into - :class:`.ScheduleBlock` representation and now reference mechanism is - always applied rather than appending the schedules wrapped by - the :class:`~qiskit.pulse.instructions.Call` instruction. - Note that the converted block doesn't necessary recover the original alignment context. - This is simply an ASAP aligned sequence of pulse instructions with absolute time intervals. - This is an upgrade of internal representation of called pulse programs and thus no API changes. - However the :class:`~qiskit.pulse.instructions.Call` instruction and :class:`.Schedule` - no longer appear in the builder's pulse program. - This change guarantees the generated schedule blocks are always QPY compatible. - If you are filtering the output schedule instructions by :class:`~qiskit.pulse.instructions.Call`, - you can access to the :attr:`.ScheduleBlock.references` instead to retrieve the called program. - -.. releasenotes/notes/0.23/upgrade-pulse-builder-and-rzx-builder-033ac8ad8ad2a192.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- :class:`~qiskit.transpiler.passes.RZXCalibrationBuilder` - and :class:`~qiskit.transpiler.passes.RZXCalibrationBuilderNoEcho` transpiler pass - have been upgraded to generate :class:`.ScheduleBlock`. - This change guarantees the transpiled circuits are always QPY compatible. - If you are directly using :meth:`~qiskit.transpiler.passes.RZXCalibrationBuilder.rescale_cr_inst`, - method from another program or a pass subclass to rescale cross resonance pulse of the device, - now this method is turned into a pulse builder macro, and you need to use this method - within the pulse builder context to adopts to new release. - The method call injects a play instruction to the context pulse program, - instead of returning a :class:`.Play` instruction with the stretched pulse. - - -.. _Release Notes_0.23.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.23/deprecate-3.7-1040fe5988ba914a.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Support for running Qiskit with Python 3.7 support has been deprecated - and will be removed in the qiskit-terra 0.25.0 release. This means - starting in the 0.25.0 release you will need to upgrade the Python - version you're using to Python 3.8 or above. - -.. releasenotes/notes/0.23/deprecate-linear-functions-synthesis-a62c41171cf396dc.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The class :class:`~.LinearFunctionsSynthesis` class is now deprecated - and will be removed in a future release. It has been superseded - by the more general :class:`~.HighLevelSynthesis` class which should - be used instead. For example, you can instantiate an instance of - :class:`~.HighLevelSynthesis` that will behave the same way as - :class:`~.LinearFunctionSynthesis` with:: - - from qiskit.transpiler.passes import HighLevelSynthesis - from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig - - HighLevelSynthesis( - HLSConfig( - linear_function=[("default", {})], - use_default_on_unspecified=False, - ) - ) - -.. releasenotes/notes/0.23/deprecate-list-args-transpile-f92e5b3d411f361f.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Support for passing in lists of argument values to the :func:`~.transpile` - function is deprecated and will be removed in the 0.25.0 release. This - is being done to facilitate greatly reducing the overhead for parallel - execution for transpiling multiple circuits at once. If you're using - this functionality currently you can call :func:`~.transpile` multiple - times instead. For example if you were previously doing something like:: - - from qiskit.transpiler import CouplingMap - from qiskit import QuantumCircuit - from qiskit import transpile - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - cmaps = [CouplingMap.from_heavy_hex(d) for d in range(3, 15, 2)] - results = transpile([qc] * 6, coupling_map=cmaps) - - instead you should run something like:: - - from itertools import cycle - from qiskit.transpiler import CouplingMap - from qiskit import QuantumCircuit - from qiskit import transpile - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - cmaps = [CouplingMap.from_heavy_hex(d) for d in range(3, 15, 2)] - - results = [] - for qc, cmap in zip(cycle([qc]), cmaps): - results.append(transpile(qc, coupling_map=cmap)) - - You can also leverage :func:`~.parallel_map` or ``multiprocessing`` from - the Python standard library if you want to run this in parallel. - -.. releasenotes/notes/0.23/deprecate-old-pulse-visualization-b62d28f7c53b9c4c.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The legacy version of the pulse drawer present in the - :mod:`qiskit.visualization.pulse` has been deprecated and will be - removed in a future release. This includes the :class:`~.ScheduleDrawer` - and :class`WaveformDrawer` classes. This module has been superseded - by the :mod:`qiskit.visualization.pulse_v2` drawer and the typical user - API :func:`.pulse_drawer` and :meth:`.PulseBlock.draw` are already updated - internally to use :mod:`qiskit.visualization.pulse_v2`. - -.. releasenotes/notes/0.23/deprecate-old-pulse-visualization-b62d28f7c53b9c4c.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The :meth:`.pulse.Instruction.draw` method has been deprecated and will - removed in a future release. The need for this method has been superseded - by the :mod:`qiskit.visualization.pulse_v2` drawer which doesn't require - :class:`~.pulse.Instrucion` objects to have their own draw method. If - you need to draw a pulse instruction you should leverage the - :func:`.pulse_drawer` instead. - -.. releasenotes/notes/0.23/deprecate-old-qpy-d39c754d82655400.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The import ``qiskit.circuit.qpy_serialization`` is deprecated, as QPY has been promoted to the - top level. You should import the same objects from :mod:`qiskit.qpy` instead. The old path - will be removed in a future of Qiskit Terra. - -.. releasenotes/notes/0.23/deprecate-qiskit-ibmq-f0dc372526fe0c57.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The ``qiskit.IBMQ`` object is deprecated. This alias object lazily redirects - attribute access to ``qiskit.providers.ibmq.IBMQ``. As the - ``qiskit-ibmq-provider`` package has been supersceded by - ``qiskit-ibm-provider`` package which maintains its own namespace - maintaining this alias is no longer relevant with the new package. If you - were relying on the ``qiskit.IBMQ`` alias you should update your usage - to use ``qiskit.providers.ibmq.IBMQ`` directly instead (and also consider - migrating to ``qiskit-ibm-provider``, see the - `migration guide `__ - for more details). - -.. releasenotes/notes/0.23/fix-pulse-qobj-converter-name-collision-0b225af630f4a6c6.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Several public methods of pulse Qobj converters have been deprecated and in a future - release they will no longer be directly callable. The list of methods is: - - In :class:`.InstructionToQobjConverter`, - - * :meth:`~InstructionToQobjConverter.convert_acquire` - * :meth:`~InstructionToQobjConverter.convert_bundled_acquires` - * :meth:`~InstructionToQobjConverter.convert_set_frequency` - * :meth:`~InstructionToQobjConverter.convert_shift_frequency` - * :meth:`~InstructionToQobjConverter.convert_set_phase` - * :meth:`~InstructionToQobjConverter.convert_shift_phase` - * :meth:`~InstructionToQobjConverter.convert_delay` - * :meth:`~InstructionToQobjConverter.convert_play` - * :meth:`~InstructionToQobjConverter.convert_snapshot` - - In :class:`.QobjToInstructionConverter`, - - * :meth:`~QobjToInstructionConverter.convert_acquire` - * :meth:`~QobjToInstructionConverter.convert_set_phase` - * :meth:`~QobjToInstructionConverter.convert_shift_phase` - * :meth:`~QobjToInstructionConverter.convert_set_frequency` - * :meth:`~QobjToInstructionConverter.convert_shift_frequency` - * :meth:`~QobjToInstructionConverter.convert_delay` - * :meth:`~QobjToInstructionConverter.bind_pulse` - * :meth:`~QobjToInstructionConverter.convert_parametric` - * :meth:`~QobjToInstructionConverter.convert_snapshot` - - Instead of calling any of these methods directly they will be implicitly selected when a - converter instance is directly called. For example:: - - converter = QobjToInstructionConverter() - converter(pulse_qobj) - -.. releasenotes/notes/0.23/latex-refactor-0745471ddecac605.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The ``qiskit.visualization.state_visualization.num_to_latex_ket()`` - and ``qiskit.visualization.state_visualization.num_to_latex_terms()`` - functions have been deprecated and will be removed in a future release. - These function were primarily used internally by the LaTeX output from - :meth:`.Statevector.draw` and :meth:`.DensityMatrix.draw` which no longer - are using these function and are leverging - `sympy `__ for this instead. If you were - using these functions you should cosinder using Sympy's - `nsimplify() `__ - `latex() `__ functions. - -.. releasenotes/notes/0.23/relax-register-naming-0e7d2dba9bf7fb38.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The method :meth:`.Register.qasm` is deprecated and will be removed in a - future release. This method is found on the subclasses :class:`.QuantumRegister` - and :class:`.ClassicalRegister`. The deprecation is because the - :meth:`~.Register.qasm` method promotes a false view of the responsible - party for safe conversion to OpenQASM 2; a single object alone does not - have the context to provide a safe conversion, such as whether its name - clashes after escaping it to produce a valid identifier. - -.. releasenotes/notes/0.23/relax-register-naming-0e7d2dba9bf7fb38.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The class-variable regular expression :attr:`.Register.name_format` is - deprecated and wil be removed in a future release. The names of registers - are now permitted to be any valid Python string, so the regular expression - has no use any longer. - -.. releasenotes/notes/0.23/transfer_clifford_cnotdihedral_synth-8d73833d78ff09c4.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The functions :func:`qiskit.quantum_info.synthesis.decompose_clifford` - and :func:`qiskit.quantum_info.synthesis.decompose_cnot_dihedral` - are deprecated and will be removed in a future release. - They are replaced by the two functions - :func:`qiskit.synthesis.synth_clifford_full` and - :func:`qiskit.synthesis.synth_cnotdihedral_full` respectively. - - -.. _Release Notes_0.23.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.23/fix-PauliOp-adjoint-a275876185df989f.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue in the :meth:`.PauliOp.adjoint` method where it would - return the correct value for Paulis with complex coefficients, - for example: ``PauliOp(Pauli("iX"))``. - Fixed `#9433 `__. - -.. releasenotes/notes/0.23/fix-ae-algorithms-1c0a43c596766cb3.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the amplitude estimation algorithms in the - ``qiskit.algorithms.amplitude_estimators`` module (see - :mod:`~qiskit.algorithms.amplitude_estimators`) for - the usage with primitives built from the abstract :class:`.BaseSampler` primitive (such - as :class:`~.Sampler` and :class:`~.BackendSampler`). Previously, the measurement - results were expanded to more bits than actually measured which for oracles with more - than one qubit led to potential errors in the detection of the "good" quantum states - for oracles. - -.. releasenotes/notes/0.23/fix-dag-parameterized-calibration-f5c0a740fcdeb2ec.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue where the :meth:`.QuantumCircuit.add_calibrations` and - :meth:`.DAGCircuit.add_calibrations` methods had a mismatch in - their behavior of parameter-formatting logic. Previously - :meth:`.DAGCircuit.add_calibrations` tried to cast every parameter - into ``float``, :meth:`.QuantumCircuit.add_calibrations` used given - parameters as-is. This would potentially cause an error when running - :func:`~.transpile` on a :class:`~.QuantumCircuit` with pulse - gates as the parameters of the calibrations could be kept as - :class:`~.ParameterExpresion` objects. - -.. releasenotes/notes/0.23/fix-qpy-mcxgray-421cf8f673f24238.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed a deserialization issue in QPY's (:mod:`qiskit.qpy`) :func:`~.qpy.load` - function where circuits containing gates of class :class:`.MCXGate`, - :class:`.MCXGrayCode`, :class:`.MCXRecursive`, and - :class:`.MCXVChain` would fail to deserialize. - Fixed `#9390 `__. - -.. releasenotes/notes/0.23/fix-tensoredop-to-matrix-6f22644f1bdb8b41.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue in :meth:`.TensoredOp.to_matrix` where the global coefficient of the operator - was multiplied to the final matrix more than once. Now, the global coefficient is correctly - applied, independent of the number of tensored operators or states. - Fixed `#9398 `__. - - - -.. releasenotes/notes/0.23/backend-sampler-shots-c233d5a3965e0c11.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The output from the :meth:`~.BackendSampler.run` method of the the - :class:`~.BackendSampler` class now sets the - ``shots`` and ``stddev_upper_bound`` attributes of the returned - :class:`~.QuasiDistribution`. Previously these attributes were missing - which prevent some post-processing using the output. - Fixed `#9311 `__ - -.. releasenotes/notes/0.23/change-qasm-float-output-d69d0c2896f8ecbb.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- The OpenQASM 2 exporter method :meth:`.QuantumCircuit.qasm` will now emit - higher precision floating point numbers for gate parameters by default. - In addition, a tighter bound (:math:`1e-12` instead of :math:`1e-6`) is used for - checking whether a given parameter is close to a fraction/power of :math:`\pi`. - Fixed `#7166 `__. - -.. releasenotes/notes/0.23/circuit-key-supports-controlflow-a956ebd2fcebaece.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed support in the :mod:`~.qiskit.primitives` module for running - :class:`~.QuantumCircuit` objects with control flow instructions (e.g. - :class:`~.IfElseOp`). Previously, the :class:`~BaseSampler` and - :class:`~BaseEstimator` base classes could not correctly - normalize such circuits. However, executing these circuits is dependent - on the particular implementation of the primitive supporting control - flow instructions. This just fixed support to enable a particular - implementation of :class:`~BaseSampler` or :class:`~BaseEstimator` - to use control flow instructions. - -.. releasenotes/notes/0.23/fix-Identity-PauliOp-matmul-5e28c9207ed61e90.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the :meth:`.PauliOp.matmul` method where it would - return incorrect results with ``iI``. - Fixed `#8680 `__. - -.. releasenotes/notes/0.23/fix-aqc-check-if-su-matrix.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the Approximate Quantum Compiler (:class:`~.AQC`) - class which caused it to return an incorrect circuit when the input - unitary had a determinant of -1. - Fixed `#9327 `__ - -.. releasenotes/notes/0.23/fix-compose-35d2fdbe5b052bca.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the :meth:`.QuantumCircuit.compose` method where it would - incorrectly reject valid qubit or clbit specifiers. This has been fixed so - that the method now accepts the same set of qubit and clbit - specifiers as other :class:`.QuantumCircuit` methods, such as - :meth:`~.QuantumCircuit.append`. - Fixed `#8691 `__. - -.. releasenotes/notes/0.23/fix-compose-35d2fdbe5b052bca.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the :meth:`.QuantumCircuit.compose` method where it would - incorrectly map registers in conditions on the given circuit to complete - registers on the base. Previously, the mapping was very imprecise; the bits - used within each condition were not subject to the mapping, and instead an inaccurate attempt was - made to find a corresponding register. This could also result in a condition on a smaller register - being expanded to be on a larger register, which is not a valid transformation. Now, a - condition on a single bit or a register will be composed to be on precisely the bits as defined - by the ``clbits`` argument. A new aliasing register will be added to the base circuit to - facilitate this, if necessary. Fixed `#6583 `__. - -.. releasenotes/notes/0.23/fix-identity-simplification-no-target-62cd8614044a0fe9.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Fixed an issue with the :func:`~.transpile` function when run with - ``optimization_level`` set to ``1``, ``2``, or ``3`` and no - ``backend``, ``basis_gates``, or ``target`` argument specified. If - the input circuit had runs of single qubit gates which could be simplified - the output circuit would not be as optimized as possible as those runs - of single qubit gates would not have been removed. This could have been - corrected previously by specifying either the ``backend``, ``basis_gates``, - or ``target`` arguments on the :func:`~.transpile` call, but now the output - will be as simplified as it can be without knowing the target gates allowed. - Fixed `#9217 `__ - -.. releasenotes/notes/0.23/fix-identity-simplification-no-target-62cd8614044a0fe9.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Fixed an issue with the :func:`~.transpile` function when run with - ``optimization_level=3`` and no ``backend``, ``basis_gates``, or ``target`` - argument specified. If the input circuit contained any 2 qubit blocks which - were equivalent to an identity matrix the output circuit would not be as - optimized as possible and and would still contain that identity block. - This could have been corrected previously by specifying either the - ``backend``, ``basis_gates``, or ``target`` arguments on the - :func:`~.transpile` call, but now the output will be as simplified as it - can be without knowing the target gates allowed. - Fixed `#9217 `__ - -.. releasenotes/notes/0.23/fix-libcomb-sampler-gradient-d759d6b0e2659abe.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with :class:`~.LinCombSamplerGradient` where it would - potentially raise an error when run with the - :class:`~qiskit_aer.primitives.Sampler` class from ``qiskit-aer``. - -.. releasenotes/notes/0.23/fix-numpy-eigensolver-sparse-pauli-op-b09a9ac8fb93fe4a.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Fixed an issue with - :class:`~qiskit.algorithms.eigensolvers.NumPyEigensolver` and by extension - :class:`~qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` - where solving for - :class:`~qiskit.quantum_info.operators.base_operator.BaseOperator` - subclasses other than :class:`~qiskit.quantum_info.operators.Operator` - would cause an error. - -.. releasenotes/notes/0.23/fix-primitives-metadata-1e79604126e26b53.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue in the metadata output from :mod:`~.qiskit.primitives` - where the list made copies by reference and all elements were updated - with the same value at every iteration. - -.. releasenotes/notes/0.23/fix-pulse-qobj-converter-name-collision-0b225af630f4a6c6.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the :class:`.QobjToInstructionConverter` - when multiple backends are called and they accidentally have the - same pulse name in the pulse library. This was an edge case that could - only be caused when a converter instance was reused across multiple - backends (this was not a typical usage pattern). - -.. releasenotes/notes/0.23/fix-pvqd-loss-cb1ebe0258f225de.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the :class:`.PVQD` class where the loss function - was incorrecly squaring the fidelity. This has been fixed so that - the loss function matches the definition in the original algorithm - definition. - -.. releasenotes/notes/0.23/fix-qpy-register-57ed7cf2f3f67e78.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Fixed a bug in QPY (:mod:`qiskit.qpy`) where circuits containing registers - whose bits occurred in the circuit after loose bits would fail to deserialize. - See `#9094 `__. - -.. releasenotes/notes/0.23/fix-twoqubit-pickle-8628047aa396919a.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- The class :class:`.TwoQubitWeylDecomposition` is now compatible with the - ``pickle`` protocol. Previously, it would fail to deserialize and would - raise a ``TypeError``. - See `#7312 `__. - -.. releasenotes/notes/0.23/fix_shots_passing_local_readout_mitigator-603514a4e0d22dc5.yaml @ b'c0961b9247d68456c62bea2a8d7760c410c2d557' - -- Fixed an issue with the :meth:`.LocalReadoutMitigator.quasi_probabilities` method where - the ``shots`` argument was not used. It is now used to set the number of shots - in the return object. - -.. releasenotes/notes/0.23/improve-collect-cliffords-f57aeafe95460b18.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed a regression in the construction of :class:`~.Clifford` objects - from :class:`~.QuantumCircuits` that contain other :class:`~.Clifford` - objects. - -.. releasenotes/notes/0.23/pickle_weyl-34e16e3aab2f7133.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the :class:`~.TwoQubitWeylDecomposition` class (and - its subclasses) to enable the Python standard library ``pickle`` - to serialize these classes. This partially fixed - `#7312 `__ - -.. releasenotes/notes/0.23/relax-register-naming-0e7d2dba9bf7fb38.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- :meth:`.QuantumCircuit.qasm` will now correctly escape gate and register - names that collide with reserved OpenQASM 2 keywords. Fixes - `#5043 `__. - -.. releasenotes/notes/0.23/upgrade-pulse-builder-and-rzx-builder-033ac8ad8ad2a192.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue in the :class:`~.RZXCalibrationBuilder` where - the ECR pulse sequence was misaligned. - Fixed `#9013 `__. - -.. releasenotes/notes/0.23/visualization-missing-channels-bc66c1c976a79c06.yaml @ b'5d6ba50234a45e461ac65eed5b98a58ffb1f5be7' - -- Fixed an issue with the :func:`~.pulse_drawer` where in some cases the - output visualization would omit some of the channels in a schedule. - Fixed `#8981 `__. - -Aer 0.11.2 -========== - -No change - -IBM Q Provider 0.19.2 -===================== - -No change - -############# -Qiskit 0.39.5 -############# - -.. _Release Notes_Terra_0.22.4: - -Terra 0.22.4 -============ - -.. _Release Notes_Terra_0.22.4_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.22.4-cddd573e87bffb9c.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -Qiskit Terra 0.22.4 is a minor bugfix release, fixing some bugs identified in the 0.22 series. - -.. _Release Notes_Terra_0.22.4_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix-backend-sampler-890cbcf913667b08.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -- Fixed a bug in :class:`~.BackendSampler` that raised an error - if its :meth:`~.BackendSampler.run` method was called two times sequentially. - -.. releasenotes/notes/fix-composedop-08e14db184c637c8.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -- Fixed two bugs in the :class:`.ComposedOp` where the :meth:`.ComposedOp.to_matrix` - method did not provide the correct results for compositions with :class:`.StateFn` - and for compositions with a global coefficient. - Fixed `#9283 `__. - -.. releasenotes/notes/fix-primitives-numpy-parameters-1589d997864dfb37.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -- Fixed the problem in which primitives, :class:`.Sampler` and :class:`.Estimator`, did not - work when passed a circuit with ``numpy.ndarray`` as a parameter. - -.. releasenotes/notes/fix-sampling-vqe-aggregation-107e3983147c57bc.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -- Fixed a bug in :class:`.SamplingVQE` where the ``aggregation`` argument did not have an effect. - Now the aggregation function and, with it, the CVaR expectation value can correctly be specified. - -.. releasenotes/notes/fix-sampling-vqe-performance-b5bfe92c2d3e10ab.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -- Fixed a performance bug where :class:`.SamplingVQE` evaluated the energies of eigenstates - in a slow manner. - -.. releasenotes/notes/fix-vqd-betas-async-df99ab6e26e9da1e.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -- Fixed the autoevaluation of the beta parameters in - :class:`~qiskit.algorithms.eigensolvers.VQD`, added support for - :class:`~qiskit.quantum_info.SparsePauliOp` inputs, and fixed - the energy evaluation function to leverage the asynchronous execution - of primitives, by only retrieving the job results after both - jobs have been submitted. - -.. releasenotes/notes/probabilities_dict_bug_fix-aac3b3d3853828dc.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -- Fixed an issue with the :meth:`.Statevector.probabilities_dict` and :meth:`.DensityMatrix.probabilities_dict` - methods where they would return incorrect results for non-qubit systems when the ``qargs`` argument was - specified. - Fixed `#9210 `__ - -.. releasenotes/notes/wrap-method-311-147d254d4b40e805.yaml @ b'23ec9022185161a48ab7726c3549d452ac9074cf' - -- Fixed handling of some ``classmethod``\ s by - :func:`~qiskit.utils.wrap_method` in Python 3.11. Previously, in Python - 3.11, ``wrap_method`` would wrap the unbound function associated with the - ``classmethod`` and then fail when invoked because the class object usually - bound to the ``classmethod`` was not passed to the function. Starting in - Python 3.11.1, this issue affected :class:`~qiskit.test.QiskitTestCase`, - preventing it from being imported by other test code. Fixed `#9291 - `__. - -Aer 0.11.2 -========== - -No change - -IBM Q Provider 0.19.2 -===================== - -No change - -############# -Qiskit 0.39.4 -############# - -Terra 0.22.3 -============ - -No change - -.. _Release Notes_Aer_0.11.2: - -Aer 0.11.2 -========== - -.. _Release Notes_Aer_0.11.2_New Features: - -New Features ------------- - -.. releasenotes/notes/add-python-311-support-027047fb389116dd.yaml @ b'1d2520d3197bf8a59d62965ade6b4cae8c7ed5b5' - -- Added support for running Qiskit Aer with Python 3.11 support. - - -.. _Release Notes_Aer_0.11.2_Known Issues: - -Known Issues ------------- - -.. releasenotes/notes/fix_aer_statevector_mps-c3dd40b936700ff4.yaml @ b'7a881dd758b327566f4e9726771b5960fa2c50d8' - -- Fix two bugs in AerStatevector. AerStatevector uses mc* instructions, which are - not enabled in matrix_product_state method. This commit changes AerStatevector - not to use MC* and use H, X, Y, Z, U and CX. AerStatevector also failed if an - instruction is decomposed to empty QuantumCircuit. This commit allows such - instruction. - - -.. _Release Notes_Aer_0.11.2_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix-AerSimulator_from_backend_BackendV2-bccf835bc42a193d.yaml @ b'a7d802fa0d16b9899200ae851bd061f8f7843da1' - -- Fixed support in the :meth:`.AerSimulator.from_backend` method for instantiating - an :class:`~.AerSimulator` instance from an a :class:`~.BackendV2` object. - Previously, attempting to use :meth:`.AerSimulator.from_backend` with a - :class:`~.BackendV2` object would have raised an :class:`~.AerError` saying this - wasn't supported. - -.. releasenotes/notes/fix-device-noise-models-2eca2f9c9dc25771.yaml @ b'0ced15bb5a7137563134789357d973743d25977d' - -- Fixes a bug where :meth:`NoiseModel.from_backend` with a ``BackendV2`` object may generate - a noise model with excessive ``QuantumError`` s on non-Gate instructions while, - for example, only ``ReadoutError`` s should be sufficient for measures. - This commit updates :meth:`NoiseModel.from_backend` with a ``BackendV2`` object so that - it returns the same noise model as that called with the corresponding ``BackendV1`` object. - That is, the resulting noise model does not contain any ``QuantumError`` s on measures and - it may contain only thermal relaxation errors on other non-gate instructions such as resets. - Note that it still contains ``ReadoutError`` s on measures. - -.. releasenotes/notes/fix-temperature-a9c51c4599af3a49.yaml @ b'5bcb45434ae5af6e02f6945555670bf47a3d9ca6' - -- Fixed a bug in :meth:`NoiseModel.from_backend` where using the ``temperature`` kwarg with - a non-default value would incorrectly compute the excited state population for - the specified temperature. Previously, there was an additional factor of 2 in - the Boltzman distribution calculation leading to an incorrect smaller value - for the excited state population. - -.. releasenotes/notes/fix-topological-control-flow-e2f1a25098004f00.yaml @ b'f8babdd98b627b23d895316ccf9725c4fde60935' - -- Fixed incorrect logic in the control-flow compiler that could allow unrelated instructions to - appear "inside" control-flow bodies during execution, causing incorrect results. For example, - previously:: - - from qiskit import QuantumCircuit - from qiskit_aer import AerSimulator - - backend = AerSimulator(method="statevector") - - circuit = QuantumCircuit(3, 3) - circuit.measure(0, 0) - circuit.measure(1, 1) - - with circuit.if_test((0, True)): - with circuit.if_test((1, False)): - circuit.x(2) - - with circuit.if_test((0, False)): - with circuit.if_test((1, True)): - circuit.x(2) - - circuit.measure(range(3), range(3)) - print(backend.run(circuit, method=method, shots=100).result()) - - would print ``{'010': 100}`` as the nested control-flow operations would accidentally jump over - the first X gate on qubit 2, which should have been executed. - -.. releasenotes/notes/fix-vervose-warnings-efbbbfcb4b65a2a5.yaml @ b'15a02738cac23033f42e66bbbe19121d2c1eebc0' - -- Fixes a bug where ``NoiseModel.from_backend()`` prints verbose warnings when - supplying a backend that reports un-physical device parameters such as T2 > 2 * T1 - due to statistical errors in their estimation. - This commit removes such warnings because they are not actionable for users in the sense - that there are no means other than truncating them to the theoretical bounds as - done within ``noise.device`` module. - See `Issue 1631 `__ - for details of the fixed bug. - -.. releasenotes/notes/fix_GPU_statevector-715da5ead0a59fb5.yaml @ b'286690076c1472ecda6211b832fcdff2b9d7598d' - -- This is fix for GPU statevector simulator. - Chunk distribution tried to allocate all free memory on GPU, - but this causes memory allocation error. - So this fix allocates 80 percent of free memory. - Also this fixes size of matrix buffer when noise sampling is applied. - -.. releasenotes/notes/fix_cache_blocking_AerState-ccb035bb5be6f895.yaml @ b'6b2b8b5c4c0e0dfa748704d07ff9b1519f17001c' - -- This is a fix of AerState running with cache blocking. AerState wrongly configured - transpiler of Aer for cache blocking, and then its algorithm to swap qubits - worked wrongly. This fix corrects AerState to use this transpiler. More specifically, - After the transpilation, a swapped qubit map is recoverd to the original map - when using AerState. This fix is necessary for AerStatevector to use multiple-GPUs. - -.. releasenotes/notes/improve-statevector-initialization-75274fdcb4106d24.yaml @ b'e6248e21e430375d693fccad4a206740fafda54e' - -- This is fix for AerStatevector. - It was not possible to create an AerStatevector instance directly from - terra's Statevector. - This fix allows a Statevector as AerStatevector's input. - -.. releasenotes/notes/sampler-counts-90e5aaabccdc415b.yaml @ b'8d34a8d3b011426714ceda417f95f5a3c9076143' - -- :attr:`.SamplerResult.quasi_dists` contain the data about the number of qubits. - :meth:`QuasiDistribution.binary_probabilities` returns bitstrings with correct length. - -.. releasenotes/notes/set_seed_for_each_in_aerstatevec-ef5fcac628dec63b.yaml @ b'cb8b33514475c6d07dd82984d870c75324a9424a' - -- Previously seed is not initialized in AerStatevector and then sampled results - are always same. With this commit, a seed is initialized for each sampling - and sampled results can be vary. - - -IBM Q Provider 0.19.2 -===================== - -No change - - -############# -Qiskit 0.39.3 -############# - -.. _Release Notes_Terra_0.22.3: - -Terra 0.22.3 -============ - -.. _Release Notes_Terra_0.22.3_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.22.3-cdc2d5c29ec5555e.yaml @ b'92fc7082b422241f2c5c4543aaea31e9eabef922' - -Qiskit Terra 0.22.3 is a minor bugfix release, fixing some further bugs in the 0.22 series. - - -.. _Release Notes_Terra_0.22.3_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/adapt-vqe-supports-aux-operators-1383103839a338c6.yaml @ b'3caa782638389e66f8f2a70ea111b796a169aa13' - -- :class:`~qiskit.algorithms.minimum_eigensolver.AdaptVQE` now correctly - indicates that it supports auxiliary operators. - -.. releasenotes/notes/fix-cregbundle-warning-d3c991bb6276761d.yaml @ b'2f338866358b80e5bcc7e4520800813a3a8a3a23' - -- The circuit drawers (:meth:`.QuantumCircuit.draw` and :func:`.circuit_drawer`) will no - longer emit a warning about the ``cregbundle`` parameter when using the default arguments, - if the content of the circuit requires all bits to be drawn individually. This was most - likely to appear when trying to draw circuits with new-style control-flow operations. - -.. releasenotes/notes/fix-qnspsa-max-evals-grouped-52eb462fa6c82079.yaml @ b'92fc7082b422241f2c5c4543aaea31e9eabef922' - -- Fixed a bug causing :class:`.QNSPSA` to fail when ``max_evals_grouped`` was set to a - value larger than 1. - -.. releasenotes/notes/fix-sabre-swap-random-seed-dcf3dace63042791.yaml @ b'b9f3b523b15f50871bf75b6c07f73d10a6d3eceb' - -- Fixed an issue with the :class:`~.SabreSwap` pass which would cause the - output of multiple runs of the pass without the ``seed`` argument specified - to reuse the same random number generator seed between runs instead of - using different seeds. This previously caused identical results to be - returned between runs even when no ``seed`` was specified. - -.. releasenotes/notes/fix-serialization-primitives-c1e44a37cfe7a32a.yaml @ b'775ac1e1d5d7e621d100f0c1a428e75be8e64be0' - -- Fixed an issue with the primitive classes, :class:`~.BackendSampler` and :class:`~.BackendEstimator`, - where instances were not able to be serialized with ``pickle``. In general these classes are not guaranteed - to be serializable as :class:`~.BackendV2` and :class:`~.BackendV1` instances are not required to be - serializable (and often are not), but the class definitions of :class:`~.BackendSampler` and - :class:`~.BackendEstimator` no longer prevent the use of ``pickle``. - -.. releasenotes/notes/reinstate-pulse-instruction-draw-7bf4bbabaa1f1862.yaml @ b'92fc7082b422241f2c5c4543aaea31e9eabef922' - -- The :meth:`.pulse.Instruction.draw` method will now succeed, as before. - This method is deprecated with no replacement planned, but it should - still work for the period of deprecation. - - -Aer 0.11.1 -========== - -No change - - -IBM Q Provider 0.19.2 -===================== - -No change - - -############# -Qiskit 0.39.2 -############# - -.. _Release Notes_Terra_0.22.2: - -Terra 0.22.2 -============ - -.. _Release Notes_Terra_0.22.2_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.22.2-cd8a0fa538c623b9.yaml @ b'dac39d0b10638a2d35819d3caee5895e05cab254' - -Qiskit Terra 0.22.2 is a minor bugfix release, and marks the first official support for Python 3.11. - - -.. _Release Notes_Terra_0.22.2_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix-backend-primitive-no-max-experiments-e2ca41ec61de353e.yaml @ b'bece288c0f677cb1f783ea1c355839efbccf3523' - -- Fixed an issue with the backend primitive classes :class:`~.BackendSampler` - and :class:`~.BackendEstimator` which prevented running with a - :class:`~.BackendV1` instance that does not have a ``max_experiments`` - field set in its :class:`~.BackendConfiguration`. - -.. releasenotes/notes/fix-vf2post-regression-d4b057ea02ce00d3.yaml @ b'1e3cad33efecaccbd83c28cdaf5b1f8df4fcba39' - -- Fixed a bug in the :class:`.VF2PostLayout` pass when transpiling for backends - with a defined :class:`.Target`, where the interaction graph would be built - incorrectly. This could result in excessive runtimes due to the graph being - far more complex than necessary. - -.. releasenotes/notes/remove-pulse-deepcopy-9a19aa7f6452248b.yaml @ b'e61b93f5513355a820a953a3b1ac53f9fcc3f2ba' - -- The Pulse expression parser should no longer periodically hang when called - from Jupyter notebooks. This is achieved by avoiding an internal ``deepycopy`` - of a recursive object that seemed to be particularly difficult for the - memoization to evaluate. - -Aer 0.11.1 -========== - -No change - - -IBM Q Provider 0.19.2 -===================== - -No change - - -############# -Qiskit 0.39.1 -############# - -.. _Release Notes_0.22.1: - -Terra 0.22.1 -============ - -.. _Release Notes_0.22.1_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.22.1-dec5623f902c4e7d.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -Qiskit Terra 0.22.1 is a bugfix release, addressing some minor issues identified since the 0.22.0 release. - - -.. _Release Notes_0.22.1_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/fix-pauli-basis-dep-27c0a4506ad38d2c.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- The ``pauli_list`` kwarg of :func:`.pauli_basis` has been deprecated as - :func:`.pauli_basis` now always returns a :class:`.PauliList`. This argument - was removed prematurely from Qiskit Terra 0.22.0 which broke compatibility - for users that were leveraging the ``pauli_list``argument. Now, the argument - has been restored but will emit a ``DeprecationWarning`` when used. If used - it has no effect because since Qiskit Terra 0.22.0 a :class:`~.PauliList` is - always returned. - - -.. _Release Notes_0.22.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix-barrier-before-final-measurements-loose-1849282c11fc5eb0.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed the :class:`.BarrierBeforeFinalMeasurements` transpiler pass when there - are conditions on loose :class:`.Clbit`\ s immediately before the final measurement - layer. Previously, this would fail claiming that the bit was not present - in an internal temporary circuit. - Fixed `#8923 `__ - -.. releasenotes/notes/fix-circuit-condition-compare-d8d85e5ca47c1416.yaml @ b'e3849ad1fa02e6515b4fc37b5b8b462a5cb47c5d' - -- The equality checkers for :class:`.QuantumCircuit` and :class:`.DAGCircuit` - (with objects of the same type) will now correctly handle conditions on single - bits. Previously, these would produce false negatives for equality, as the - bits would use "exact" equality checks instead of the "semantic" checks the rest - of the properties of circuit instructions get. - -.. releasenotes/notes/fix-clbit-handling-stochasticswap-controlflow-fbb9d8fab5040643.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed handling of classical bits in :class:`.StochasticSwap` with control flow. - Previously, control-flow operations would be expanded to contain all the - classical bits in the outer circuit and not contracted again, leading to a - mismatch between the numbers of clbits the instruction reported needing and - the actual number supplied to it. - Fixed `#8903 `__ - -.. releasenotes/notes/fix-global-inst-qarg-method-target-a9188e172ea7f325.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed handling of globally defined instructions for the :class:`~.Target` - class. Previously, two methods, :meth:`~.Target.operations_for_qargs` and - :meth:`~.Target.operation_names_for_qargs` would ignore/incorrectly handle - any globally defined ideal operations present in the target. For example:: - - from qiskit.transpiler import Target - from qiskit.circuit.library import CXGate - - target = Target(num_qubits=5) - target.add_instruction(CXGate()) - names = target.operation_names_for_qargs((1, 2)) - ops = target.operations_for_qargs((1, 2)) - - will now return ``{"cx"}`` for ``names`` and ``[CXGate()]`` for ``ops`` - instead of raising a ``KeyError`` or an empty return. - -.. releasenotes/notes/fix-global-inst-qarg-method-target-a9188e172ea7f325.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed an issue in the :meth:`.Target.add_instruction` method where it - would previously have accepted an argument with an invalid number of - qubits as part of the ``properties`` argument. For example:: - - from qiskit.transpiler import Target - from qiskit.circuit.library import CXGate - - target = Target() - target.add_instruction(CXGate(), {(0, 1, 2): None}) - - This will now correctly raise a ``TranspilerError`` instead of causing - runtime issues when interacting with the target. - Fixed `#8914 `__ - -.. releasenotes/notes/fix-hinton-bug-1141a297050f55bb.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed an issue with the :func:`.plot_state_hinton` visualization function - which would result in a misplaced axis that was offset from the actual - plot. - Fixed `#8446 ` - -.. releasenotes/notes/fix-hinton-bug-1141a297050f55bb.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed the output of the :func:`.plot_state_hinton` function so that the state labels - are ordered ordered correctly, and the image matches up with the natural matrix - ordering. - Fixed `#8324 `__ - -.. releasenotes/notes/fix-max_circuits-backend-primitives-c70590bca557001f.yaml @ b'45de12a4bdc0a09e1557750e97c56a0e60e8a3cb' - -- Fixed an issue with the primitive classes, :class:`~.BackendSampler` and - :class:`~.BackendEstimator` when running on backends that have a limited - number of circuits in each job. Not all backends support an unlimited - batch size (most hardware backends do not) and previously the backend - primitive classes would have potentially incorrectly sent more circuits - than the backend supported. This has been corrected so that - :class:`~.BackendSampler` and :class:`~.BackendEstimator` will chunk the - circuits into multiple jobs if the backend has a limited number of - circuits per job. - -.. releasenotes/notes/fix-max_circuits-backend-primitives-c70590bca557001f.yaml @ b'45de12a4bdc0a09e1557750e97c56a0e60e8a3cb' - -- Fixed an issue with the :class:`~.BackendEstimator` class where previously - setting a run option named ``monitor`` to a value that evaluated as - ``True`` would have incorrectly triggered a job monitor that only - worked on backends from the ``qiskit-ibmq-provider`` package. This - has been removed so that you can use a ``monitor`` run option if needed - without causing any issues. - -.. releasenotes/notes/fix-mixed-ideal-target-coupling-map-7fca04f9c5139a49.yaml @ b'147b7575f7b5925add5ec09557aa7afa8c08ee7f' - -- Fixed an issue with the :meth:`.Target.build_coupling_map` method where - it would incorrectly return ``None`` for a :class:`~.Target` object - with a mix of ideal globally available instructions and instructions - that have qubit constraints. Now in such cases the - :meth:`.Target.build_coupling_map` will return a coupling map for the - constrained instruction (unless it's a 2 qubit operation which will - return ``None`` because globally there is no connectivity constraint). - Fixed `#8971 `__ - -.. releasenotes/notes/fix-mixed-ideal-target-coupling-map-7fca04f9c5139a49.yaml @ b'147b7575f7b5925add5ec09557aa7afa8c08ee7f' - -- Fixed an issue with the :attr:`.Target.qargs` attribute where it would - incorrectly return ``None`` for a :class:`~.Target` object that contained - any globally available ideal instruction. - -.. releasenotes/notes/fix-pauli-basis-dep-27c0a4506ad38d2c.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed the premature removal of the ``pauli_list`` keyword argument of - the :func:`.pauli_basis` function which broke existing code using the - ``pauli_list=True`` future compatibility path on upgrade to Qiskit Terra - 0.22.0. This keyword argument has been added back to the function and is - now deprecated and will be removed in a future release. - -.. releasenotes/notes/fix-qpy-custom-controlled-gate-a9355df1a88a83a5.yaml @ b'6c4a3b62f71b622b25caef63c2a196aa01215480' - -- Fixed an issue in QPY serialization (:func:`~.qpy.dump`) when a custom - :class:`~.ControlledGate` subclass that overloaded the ``_define()`` - method to provide a custom definition for the operation. Previously, - this case of operation was not serialized correctly because it wasn't - accounting for using the potentially ``_define()`` method to provide - a definition. - Fixes `#8794 `__ - -.. releasenotes/notes/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml @ b'e0befd769fc54e9f50cdc4b355983b9d1eda6f31' - -- QPY deserialisation will no longer add extra :class:`.Clbit` instances to the - circuit if there are both loose :class:`.Clbit`\ s in the circuit and more - :class:`~.circuit.Qubit`\ s than :class:`.Clbit`\ s. - -.. releasenotes/notes/fix-qpy-loose-bits-5283dc4ad3823ce3.yaml @ b'e0befd769fc54e9f50cdc4b355983b9d1eda6f31' - -- QPY deserialisation will no longer add registers named `q` and `c` if the - input circuit contained only loose bits. - -.. releasenotes/notes/fix-sparse-pauli-real-63c31d87801671b1.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed the :meth:`.SparsePauliOp.dot` method when run on two operators with - real coefficients. To fix this, the dtype that :class:`SparsePauliOp` can - take is restricted to ``np.complex128`` and ``object``. - Fixed `#8992 `__ - -.. releasenotes/notes/fix-styles-manifest-b8c852a07fb86966.yaml @ b'c336cf583285ad4803c3ef02a15eac3651655434' - -- Fixed an issue in the :func:`~.circuit_drawer` function and - :func:`.QuantumCircuit.draw` method where the only built-in style - for the ``mpl`` output that was usable was ``default``. If another - built-in style, such as ``iqx``, were used then a warning about - the style not being found would be emitted and the drawer would - fall back to use the ``default`` style. - Fixed `#8991 `__ - -.. releasenotes/notes/fix-target-transpile-parallel-772f943a08d0570b.yaml @ b'3af82426f9cbb25d47bf50b9c218ce9d30f79fdd' - -- Fixed an issue with the :func:`~.transpile` where it would previously - fail with a ``TypeError`` if a custom :class:`~.Target` object was - passed in via the ``target`` argument and a list of multiple circuits - were specified for the ``circuits`` argument. - -.. releasenotes/notes/fix-transpile-ideal-measurement-c37e04533e196ded.yaml @ b'bcec3b9e3ec792387a72fe3400b491e666d02eb6' - -- Fixed an issue with :func:`~.transpile` when targeting a :class:`~.Target` - (either directly via the ``target`` argument or via a - :class:`~.BackendV2` instance from the ``backend`` argument) that - contained an ideal :class:`~.Measure` instruction (one that does not have - any properties defined). Previously this would raise an exception - trying to parse the target. - Fixed `#8969 `__ - -.. releasenotes/notes/fix-vf2-layout-no-noise-22261601684710c3.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed an issue with the :class:`~.VF2Layout` pass where it would error - when running with a :class:`~.Target` that had instructions that were - missing error rates. This has been corrected so in such cases the - lack of an error rate will be treated as an ideal implementation and - if no error rates are present it will just select the first matching - layout. - Fixed `#8970 `__ - -.. releasenotes/notes/fix-vf2-layout-no-noise-22261601684710c3.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed an issue with the :class:`~.VF2PostLayout` pass where it would - error when running with a :class:`~.Target` that had instructions that - were missing. In such cases the lack of an error rate will be treated as - an ideal implementation of the operation. - -.. releasenotes/notes/fix-vqd-kgt2-1ed95de3e32102c1.yaml @ b'6ef8691ab7ede702c48f57087b27f88ad08427fc' - -- Fixed an issue with the :class:`~.eigensolvers.VQD` class if more than - ``k=2`` eigenvalues were computed. Previously this would fail due to an - internal type mismatch, but now runs as expected. - Fixed `#8982 `__ - -.. releasenotes/notes/fix-vqe-default-batching-eb08e6ce17907da3.yaml @ b'695bfb9ecfaf3c9127a63055d874e0a72a8ed122' - -- Fixed a performance bug where the new primitive-based variational algorithms - :class:`.minimum_eigensolvers.VQE`, :class:`.eigensolvers.VQD` and :class:`.SamplingVQE` - did not batch energy evaluations per default, which resulted in a significant slowdown - if a hardware backend was used. - -.. releasenotes/notes/fix-zero-operand-gates-323510ec8f392f27.yaml @ b'ad6f295ea2eaa0e6142db87a3abbf3d7fac5dec8' - -- Zero-operand gates and instructions will now work with - :func:`.circuit_to_gate`, :meth:`.QuantumCircuit.to_gate`, - :meth:`.Gate.control`, and the construction of an - :class:`~.quantum_info.Operator` from a :class:`.QuantumCircuit` containing - zero-operand instructions. This edge case is occasionally useful in creating - global-phase gates as part of larger compound instructions, though for many - uses, :attr:`.QuantumCircuit.global_phase` may be more appropriate. - -.. releasenotes/notes/fix_8897-2a90c4b0857c19c2.yaml @ b'8a5dbc96f66a0c3355a30b384e83488c918628e6' - -- Fixes issue where :meth:`.Statevector.evolve` and :meth:`.DensityMatrix.evolve` - would raise an exeception for nested subsystem evolution for non-qubit - subsystems. - Fixes `issue #8897 `_ - -.. releasenotes/notes/fix_8897-2a90c4b0857c19c2.yaml @ b'8a5dbc96f66a0c3355a30b384e83488c918628e6' - -- Fixes bug in :meth:`.Statevector.evolve` where subsystem evolution - will return the incorrect value in certain cases where there are 2 or more - than non-evolved subsystems with different subsystem dimensions. - Fixes `issue #8899 `_ - - -.. _Release Notes_Aer_0.11.1: - -Aer 0.11.1 -========== - -.. _Release Notes_Aer_0.11.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/cmake_cuda_arch-817eb0b7232bd291.yaml @ b'8c4b6c145d55a1ec16d42ea061b02b8c82262db6' - -- Fixed a potential build error when trying to use CMake 3.18 or newer and - building qiskit-aer with GPU support enabled. Since CMake 3.18 or later - when building with CUDA the ``CMAKE_CUDA_ARCHITECTURES`` was required to - be set with the architecture value for the target GPU. This has been - corrected so that setting ``AER_CUDA_ARCH`` will be used if this was - not set. - -.. releasenotes/notes/fix-local-noise-pass-83815d5a80f9a0e9.yaml @ b'040de7a8018a4ae46279d340bde475825c66111f' - -- Fixes a bug in the handling of instructions with clbits in :class:`.LocalNoisePass`. - Previously, it was accidentally erasing clbits of instructions (e.g. measures) - to which the noise is applied in the case of ``method="append"``. - -.. releasenotes/notes/sampler-cache-78f916cedb0c5421.yaml @ b'b8f4db645c38caceafc69d51a9ad74f73a6666eb' - -- Fixed the performance overhead of the Sampler class when running with identical circuits on multiple executions. - This was accomplished by skipping/caching the transpilation of these identical circuits on subsequent executions. - -.. releasenotes/notes/support_terra_primitive_022-8852b784608bcdcb.yaml @ b'de3abb55bfe118905f66dd79a8d4537bd646e849' - -- Fixed compatibility of the :class:`~.qiskit_aer.primitives.Sampler` and :class:`~.qiskit_aer.primtives.Estimator` - primitive classes with qiskit-terra 0.22.0 release. In qiskit-terra 0.22.0 breaking API changes were made to the - abstract interface which broke compatibility with these classes, this has been addressed so that - :class:`~.qiskit_aer.primitives.Sampler` and :class:`~.qiskit_aer.primtives.Estimator` can now be used with - qiskit-terra >= 0.22.0. - -IBM Q Provider 0.19.2 -===================== - -No change - - -############# -Qiskit 0.39.0 -############# - -This release also officially deprecates the Qiskit Aer project as part of the Qiskit metapackage. -This means that in a future release ``pip install qiskit`` will no longer include ``qiskit-aer``. -If you're currently installing or listing ``qiskit`` as a dependency to get Aer you should upgrade -this to explicitly list ``qiskit-aer`` as well. - -The ``qiskit-aer`` project is still active and maintained moving forward but for the Qiskit -metapackage (i.e. what gets installed via ``pip install qiskit``) the project is moving towards -a model where the Qiskit package only contains the common core functionality for building and -compiling quantum circuits, programs, and applications and packages that build on it or link -Qiskit to hardware or simulators are separate packages. - -Terra 0.22.0 -============ - -.. _Release Notes_0.22.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.22/prepare-0.22-118e15de86d36072.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -The Qiskit Terra 0.22.0 release is a major feature release that includes -a myriad of new feature and bugfixes. The highlights for this release are: - - * Adding initial support to the transpiler for transpiling - :class:`~.QuantumCircuit` objects that contain control flow instructions - such as :class:`~.ForLoopOp` and :class:`~.WhileLoopOp`. - - * Greatly improved scaling and performance for the :func:`~.transpile` function - with large numbers of qubits, especially when ``optimization_level=3`` is used. - - * External plugin interface for :func:`~.transpile` that enables external - packages to implement stages for the default pass managers. More details on this - can be found at :mod:`qiskit.transpiler.preset_passmanagers.plugin`. - Additionally, :class:`~.BackendV2` backends can now optionally set - custom default plugins to use for the scheduling and translation stages. - - * Updated algorithm implementations in :mod:`qiskit.algorithms` that leverage - the :mod:`~.primitives` classes that implement the :class:`~.BaseSampler` and - :class:`~.BaseEstimator`. - - -.. _Release Notes_0.22.0_New Features: - -New Features ------------- - -.. releasenotes/notes/0.22/fix-target-control-flow-representation-09520e2838f0657e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Add support for representing an operation that has a variable width - to the :class:`~.Target` class. Previously, a :class:`~.Target` object - needed to have an instance of :class:`~Operation` defined for each - operation supported in the target. This was used for both validation - of arguments and parameters of the operation. However, for operations - that have a variable width this wasn't possible because each instance - of an :class:`~Operation` class can only have a fixed number of qubits. - For cases where a backend supports variable width operations the - instruction can be added with the class of the operation instead of an - instance. In such cases the operation will be treated as globally - supported on all qubits. For example, if building a target like:: - - from qiskit.circuit import Parameter, Measure, IfElseOp, ForLoopOp, WhileLoopOp - from qiskit.circuit.library import IGate, RZGate, SXGate, XGate, CXGate - from qiskit.transpiler import Target, InstructionProperties - - theta = Parameter("theta") - - ibm_target = Target() - i_props = { - (0,): InstructionProperties(duration=35.5e-9, error=0.000413), - (1,): InstructionProperties(duration=35.5e-9, error=0.000502), - (2,): InstructionProperties(duration=35.5e-9, error=0.0004003), - (3,): InstructionProperties(duration=35.5e-9, error=0.000614), - (4,): InstructionProperties(duration=35.5e-9, error=0.006149), - } - ibm_target.add_instruction(IGate(), i_props) - rz_props = { - (0,): InstructionProperties(duration=0, error=0), - (1,): InstructionProperties(duration=0, error=0), - (2,): InstructionProperties(duration=0, error=0), - (3,): InstructionProperties(duration=0, error=0), - (4,): InstructionProperties(duration=0, error=0), - } - ibm_target.add_instruction(RZGate(theta), rz_props) - sx_props = { - (0,): InstructionProperties(duration=35.5e-9, error=0.000413), - (1,): InstructionProperties(duration=35.5e-9, error=0.000502), - (2,): InstructionProperties(duration=35.5e-9, error=0.0004003), - (3,): InstructionProperties(duration=35.5e-9, error=0.000614), - (4,): InstructionProperties(duration=35.5e-9, error=0.006149), - } - ibm_target.add_instruction(SXGate(), sx_props) - x_props = { - (0,): InstructionProperties(duration=35.5e-9, error=0.000413), - (1,): InstructionProperties(duration=35.5e-9, error=0.000502), - (2,): InstructionProperties(duration=35.5e-9, error=0.0004003), - (3,): InstructionProperties(duration=35.5e-9, error=0.000614), - (4,): InstructionProperties(duration=35.5e-9, error=0.006149), - } - ibm_target.add_instruction(XGate(), x_props) - cx_props = { - (3, 4): InstructionProperties(duration=270.22e-9, error=0.00713), - (4, 3): InstructionProperties(duration=305.77e-9, error=0.00713), - (3, 1): InstructionProperties(duration=462.22e-9, error=0.00929), - (1, 3): InstructionProperties(duration=497.77e-9, error=0.00929), - (1, 2): InstructionProperties(duration=227.55e-9, error=0.00659), - (2, 1): InstructionProperties(duration=263.11e-9, error=0.00659), - (0, 1): InstructionProperties(duration=519.11e-9, error=0.01201), - (1, 0): InstructionProperties(duration=554.66e-9, error=0.01201), - } - ibm_target.add_instruction(CXGate(), cx_props) - measure_props = { - (0,): InstructionProperties(duration=5.813e-6, error=0.0751), - (1,): InstructionProperties(duration=5.813e-6, error=0.0225), - (2,): InstructionProperties(duration=5.813e-6, error=0.0146), - (3,): InstructionProperties(duration=5.813e-6, error=0.0215), - (4,): InstructionProperties(duration=5.813e-6, error=0.0333), - } - ibm_target.add_instruction(Measure(), measure_props) - ibm_target.add_instruction(IfElseOp, name="if_else") - ibm_target.add_instruction(ForLoopOp, name="for_loop") - ibm_target.add_instruction(WhileLoopOp, name="while_loop") - - The :class:`~.IfElseOp`, :class:`~.ForLoopOp`, and :class:`~.WhileLoopOp` - operations are globally supported for any number of qubits. This is then - reflected by other calls in the :class:`~.Target` API such as - :meth:`~.Target.instruction_supported`:: - - ibm_target.instruction_supported(operation_class=WhileLoopOp, qargs=(0, 2, 3, 4)) - ibm_target.instruction_supported('if_else', qargs=(0, 1)) - - both return ``True``. - -.. releasenotes/notes/0.22/prepare-0.22-118e15de86d36072.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added new primitive implementations, :class:`~.BackendSampler` and :class:`~.BackendEstimator`, - to :mod:`qiskit.primitives`. Thes new primitive class implementation wrap a :class:`~.BackendV1` - or :class:`~.BackendV2` instance as a :class:`~.BaseSampler` or :class:`~.BaseEstimator` - respectively. The intended use case for these primitive implementations is to bridge the gap - between providers that do not have native primitive implementations and use that provider's - backend with APIs that work with primitives. For example, the :class:`~.SamplingVQE` class - takes a :class:`~.BaseSampler` instance to function. If you'd like to run that class with - a backend from a provider without a native primitive implementation you can construct a - :class:`~.BackendSampler` to do this:: - - from qiskit.algorithms.minimum_eigensolvers import SamplingVQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.primitives import BackendSampler - from qiskit.providers.fake_provider import FakeHanoi - from qiskit.opflow import PauliSumOp - from qiskit.quantum_info import SparsePauliOp - - backend = FakeHanoi() - sampler = BackendSampler(backend=backend) - - operator = PauliSumOp(SparsePauliOp(["ZZ", "IZ", "II"], coeffs=[1, -0.5, 0.12])) - ansatz = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - optimizer = SLSQP() - sampling_vqe = SamplingVQE(sampler, ansatz, optimizer) - result = sampling_vqe.compute_minimum_eigenvalue(operator) - eigenvalue = result.eigenvalue - - If you're using a provider that has native primitive implementations (such as - ``qiskit-ibm-runtime`` or ``qiskit-aer``) it is always a better choice to use that native - primitive implementation instead of :class:`~.BackendEstimator` or :class:`~.BackendSampler` - as the native implementations will be much more efficient and/or do additional pre and post - processing. :class:`~.BackendEstimator` and :class:`~.BackendSampler` are designed to be - generic that can work with any backend that returns :class:`~.Counts` in their - :class:`~.Results` which precludes additional optimization. - -.. releasenotes/notes/0.22/adapt-vqe-0f71234cb6ec92f8.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new algorithm class, :class:`~.AdaptVQE` to :mod:`qiskit.algorithms` - This algorithm uses a :class:`qiskit.algorithms.minimum_eigensolvers.VQE` - in combination with a pool of operators from which to build out an - :class:`qiskit.circuit.library.EvolvedOperatorAnsatz` adaptively. - For example: - - .. code-block:: python - - from qiskit.algorithms.minimum_eigensolvers import AdaptVQE, VQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.primitives import Estimator - from qiskit.circuit.library import EvolvedOperatorAnsatz - - # get your Hamiltonian - hamiltonian = ... - - # construct your ansatz - ansatz = EvolvedOperatorAnsatz(...) - - vqe = VQE(Estimator(), ansatz, SLSQP()) - - adapt_vqe = AdaptVQE(vqe) - - result = adapt_vqe.compute_minimum_eigenvalue(hamiltonian) - -.. releasenotes/notes/0.22/add-backend-custom-passes-cddfd05c8704a4b1.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`~.BackendV2` class now has support for two new optional hook - points enabling backends to inject custom compilation steps as part of - :func:`~.transpile` and :func:`~.generate_preset_pass_manager`. If a - :class:`~.BackendV2` implementation includes the methods - ``get_scheduling_stage_plugin()`` or ``get_translation_stage_plugin()`` the - transpiler will use the returned string as the default value for - the ``scheduling_method`` and ``translation_method`` arguments. This enables - backends to run additional custom transpiler passes when targetting that - backend by leveraging the transpiler stage - :mod:`~qiskit.transpiler.preset_passmanagers.plugin` interface. - For more details on how to use this see: :ref:`custom_transpiler_backend`. - -.. releasenotes/notes/0.22/add-backend-custom-passes-cddfd05c8704a4b1.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new keyword argument, ``ignore_backend_supplied_default_methods``, to the - :func:`~.transpile` function which can be used to disable a backend's - custom selection of a default method if the target backend has - ``get_scheduling_stage_plugin()`` or ``get_translation_stage_plugin()`` - defined. - -.. releasenotes/notes/0.22/add-barrier-label-8e677979cb37461e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a ``label`` parameter to the :class:`.Barrier` class's constructor - and the :meth:`~.QuantumCircuit.barrier` method which allows a user to - assign a label to an instance of the :class:`~.Barrier` directive. For - visualizations generated with :func:`~.circuit_drawer` or - :meth:`.QuantumCircuit.draw` this label will be printed at the top of the - ``barrier``. - - .. code-block:: python - - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.h(1) - circuit.barrier(label="After H") - circuit.draw('mpl') - -.. releasenotes/notes/0.22/add-ccz-cs-and-csdg-gates-4ad05e323f1dec4d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Add new gates :class:`.CCZGate`, :class:`.CSGate`, and :class:`.CSdgGate` - to the standard gates in the Circuit Library - (:mod:`qiskit.circuit.library`). - -.. releasenotes/notes/0.22/add-eigensolvers-with-primitives-8b3a9f55f5fd285f.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added :mod:`qiskit.algorithms.eigensolvers` package to include - interfaces for primitive-enabled algorithms. This new module - will eventually replace the previous ``qiskit.algorithms.eigen_solvers``. - This new module contains an alternative implementation of the - :class:`~qiskit.algorithms.eigensolvers.VQD` which instead of taking - a backend or :class:`~.QuantumInstance` instead takes an instance of - :class:`~.BaseEstimator`, including :class:`~.Estimator`, - :class:`~.BackendEstimator`, or any provider implementations such as - those as those present in ``qiskit-ibm-runtime`` and ``qiskit-aer``. - - For example, to use the new implementation with an instance of - :class:`~.Estimator` class: - - .. code-block:: python - - from qiskit.algorithms.eigensolvers import VQD - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.primitives import Sampler, Estimator - from qiskit.algorithms.state_fidelities import ComputeUncompute - from qiskit.opflow import PauliSumOp - from qiskit.quantum_info import SparsePauliOp - - h2_op = PauliSumOp(SparsePauliOp( - ["II", "IZ", "ZI", "ZZ", "XX"], - coeffs=[ - -1.052373245772859, - 0.39793742484318045, - -0.39793742484318045, - -0.01128010425623538, - 0.18093119978423156, - ], - )) - - estimator = Estimator() - ansatz = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - optimizer = SLSQP() - fidelity = ComputeUncompute(Sampler()) - - vqd = VQD(estimator, fidelity, ansatz, optimizer, k=2) - result = vqd.compute_eigenvalues(h2_op) - eigenvalues = result.eigenvalues - - Note that the evaluated auxillary operators are now obtained via the - ``aux_operators_evaluated`` field on the results. This will consist of a list or dict of - tuples containing the expectation values for these operators, as we well as the metadata from - primitive run. ``aux_operator_eigenvalues`` is no longer a valid field. - -.. releasenotes/notes/0.22/add-fidelity-interface-primitives-dc543d079ecaa8dd.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added new algorithms to calculate state fidelities/overlaps - for pairs of quantum circuits (that can be parametrized). Apart from - the base class (:class:`~qiskit.algorithms.state_fidelities.BaseStateFidelity`) which defines the interface, - there is an implementation of the compute-uncompute method that leverages - instances of the :class:`~.BaseSampler` primitive: :class:`qiskit.algorithms.state_fidelities.ComputeUncompute`. - - For example: - - .. code-block:: python - - import numpy as np - from qiskit.primitives import Sampler - from qiskit.algorithms.state_fidelities import ComputeUncompute - from qiskit.circuit.library import RealAmplitudes - - sampler = Sampler(...) - fidelity = ComputeUncompute(sampler) - circuit = RealAmplitudes(2) - values = np.random.random(circuit.num_parameters) - shift = np.ones_like(values) * 0.01 - - job = fidelity.run([circuit], [circuit], [values], [values+shift]) - fidelities = job.result().fidelities - -.. releasenotes/notes/0.22/add-gradients-with-primitives-561cf9cf75a7ccb8.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new module :mod:`qiskit.algorithms.gradients` that contains - classes which are used to compute gradients using the primitive - interfaces defined in :mod:`qiskit.primitives`. There are 4 types of - gradient classes: Finite Difference, Parameter Shift, Linear - Combination of Unitary, and SPSA with implementations that either use - an instance of the :class:`~.BaseEstimator` interface: - - * :class:`~.ParamShiftEstimatorGradient` - * :class:`~.LinCombEstimatorGradient` - * :class:`~.FiniteDiffEstimatorGradient` - * :class:`~.SPSAEstimatorGradient` - - or an instance of the :class:`~.BaseSampler` interface: - - * :class:`~.ParamShiftSamplerGradient` - * :class:`~.LinCombSamplerGradient` - * :class:`~.FiniteDiffSamplerGradient` - * :class:`~.SPSASamplerGradient` - - The estimator-based gradients compute the gradient of expectation - values, while the sampler-based gradients return gradients of the - measurement outcomes (also referred to as "probability gradients"). - - For example: - - .. code-block:: python - - estimator = Estimator(...) - gradient = ParamShiftEstimatorGradient(estimator) - job = gradient.run(circuits, observables, parameters) - gradients = job.result().gradients - -.. releasenotes/notes/0.22/add-grover-primitives-10f81efdba93703d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`~.Grover` class has a new keyword argument, ``sampler`` which is - used to run the algorithm using an instance of the :class:`~.BaseSampler` - interface to calculate the results. This new argument supersedes the - the ``quantum_instance`` argument and accordingly, ``quantum_instance`` - is pending deprecation and will be deprecated and subsequently removed in - future releases. - - Example: - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.primitives import Sampler - from qiskit.algorithms import Grover, AmplificationProblem - - sampler = Sampler() - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - problem = AmplificationProblem(oracle, is_good_state=["11"]) - grover = Grover(sampler=sampler) - result = grover.amplify(problem) - -.. releasenotes/notes/0.22/add-pulse-drawer-option-936b6d943de9a270.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- A new option, ``"formatter.control.fill_waveform"`` has been added to - the pulse drawer (:func:`.pulse_v2.draw` and :meth:`.Schedule.draw`) - style sheets. This option can be used to remove the face color of pulses - in the output visualization which allows for drawing pulses only with - lines. - - For example: - - .. code-block:: python - - from qiskit.visualization.pulse_v2 import IQXStandard - - my_style = IQXStandard( - **{"formatter.control.fill_waveform": False, "formatter.line_width.fill_waveform": 2} - ) - - my_sched.draw(style=my_style) - -.. releasenotes/notes/0.22/add-reset-simplification-pass-82377d80dd0081fd.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new transpiler pass, :class:`~.ResetAfterMeasureSimplification`, - which is used to replace a :class:`~.Reset` operation after a - :class:`~.Measure` with a conditional :class:`~.XGate`. This pass can - be used on backends where a :class:`~.Reset` operation is performed by - doing a measurement and then a conditional X gate so that this will - remove the duplicate implicit :class:`~.Measure` from the :class:`~.Reset` - operation. For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.transpiler.passes import ResetAfterMeasureSimplification - - qc = QuantumCircuit(1) - qc.measure_all() - qc.reset(0) - qc.draw('mpl') - - .. code-block:: python - - result = ResetAfterMeasureSimplification()(qc) - result.draw('mpl') - -.. releasenotes/notes/0.22/add-reverse-linear-entanglement-nlocal-38581e4ffb7a7c68.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new supported value, ``"reverse_linear"`` for the ``entanglement`` keyword argument - to the constructor for the :class:`~.NLocal` circuit class. For :class:`~.TwoLocal` circuits - (which are subclassess of :class:`~.NLocal`), if ``entanglement_blocks="cx"`` then - using ``entanglement="reverse_linear"`` provides an equivalent n-qubit circuit as - ``entanglement="full"`` but with only :math:`n-1` :class:`~.CXGate` gates, instead of - :math:`\frac{n(n-1)}{2}`. - -.. releasenotes/notes/0.22/add-schedule-block-reference-mechanism-8a7811e17b4fead3.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- :class:`.ScheduleBlock` has been updated so that it can manage unassigned subroutine, - in other words, to allow lazy calling of other programs. - For example, this enables the following workflow: - - .. code-block:: python - - from qiskit import pulse - - with pulse.build() as prog: - pulse.reference("x", "q0") - - with pulse.build() as xq0: - pulse.play(Gaussian(160, 0.1, 40), pulse.DriveChannel(0)) - - prog.assign_references({("x", "q0"): xq0}) - - Now a user can create ``prog`` without knowing actual implementation of - the reference ``("x", "q0")``, and assign it at a later time for execution. - This improves modularity of pulse programs, and thus one can easily write a template - pulse program relying on other calibrations. - - To realize this feature, the new pulse instruction (compiler directive) - :class:`~qiskit.pulse.instructions.Reference` has been added. - This instruction is injected into the current builder scope when - the :func:`~qiskit.pulse.builder.reference` command is used. - All references defined in the current pulse program can be listed with - the :attr:`~qiskit.pulse.schedule.ScheduleBlock.references` property. - - In addition, every reference is managed with a scope to ease parameter management. - :meth:`~.scoped_parameters` and :meth:`~.search_parameters` have been added to - :class:`~.ScheduleBlock`. See API documentation for more details. - -.. releasenotes/notes/0.22/add-sparsepauliop-methods-00a7e6cc7055e1d0.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new method :meth:`.SparsePauliOp.argsort`, which - returns the composition of permutations in the order of sorting - by coefficient and sorting by Pauli. By using the ``weight`` - keyword argument for the method the output can additionally be sorted - by the number of non-identity terms in the Pauli, where the set of - all Paulis of a given weight are still ordered lexicographically. - -.. releasenotes/notes/0.22/add-sparsepauliop-methods-00a7e6cc7055e1d0.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new method :meth:`.SparsePauliOp.sort`, which will first - sort the coefficients using numpy's ``argsort()`` and then sort - by Pauli, where the Pauli sort takes precedence. If the Pauli sort - is the same, it will then be sorted by coefficient. By using the - ``weight`` keyword argument the output can additionally be sorted - by the number of non-identity terms in the Pauli, where the set of - all Paulis of a given weight are still ordered lexicographically. - -.. releasenotes/notes/0.22/add-wire-order-to-drawers-657cb54e365c621a.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new keyword argument, ``wire_order``, to the :func:`~.circuit_drawer` - function and :meth:`.QuantumCircuit.draw` method which allows arbitrarily - reordering both the quantum and classical bits in the output visualization. - For example: - - .. code-block:: python - - from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister - - qr = QuantumRegister(4, "q") - cr = ClassicalRegister(4, "c") - cr2 = ClassicalRegister(2, "ca") - circuit = QuantumCircuit(qr, cr, cr2) - circuit.h(0) - circuit.h(3) - circuit.x(1) - circuit.x(3).c_if(cr, 10) - circuit.draw('mpl', cregbundle=False, wire_order=[2, 1, 3, 0, 6, 8, 9, 5, 4, 7]) - -.. releasenotes/notes/0.22/add_cnot_dihedral_class_cs_ccz_gates-6bd567daf3a467bd.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added support for the :class:`~.CSGate`, :class:`~.CSdgGate` and - :class:`~.CCZGate` classes to the constructor for the operator - class :class:`~qiskit.quantum_info.CNOTDihedral`. The input - circuits when creating a :class:`~.CNOTDihedral` operator will now - support circuits using these gates. For example:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import CNOTDihedral - - qc = QuantumCircuit(2) - qc.t(0) - qc.cs(0, 1) - qc.tdg(0) - operator = CNOTDihedral(qc) - -.. releasenotes/notes/0.22/ae-algorithms-primitives-497bae1b2b04f877.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The amplitude estimation algorithm classes: - - * :class:`~qiskit.algorithms.AmplitudeEstimation`, - * :class:`~qiskit.algorithms.FasterAmplitudeEstimation`, - * :class:`~qiskit.algorithms.IterativeAmplitudeEstimation`, - * :class:`~qiskit.algorithms.MaximumLikelihoodAmplitudeEstimation` - - Now have a new keyword argument, ``sampler`` on their constructor that - takes an instance of an object that implements the :class:`~.BaseSampler` - interface including :class:`~.BackendSampler`, :class:`Sampler`, or any - provider implementations such as those as those present in - qiskit-ibm-runtime and qiskit-aer. This provides an alternative to using - the ``quantum_instance`` argument to set the target :class:`~.Backend` - or :class:`~.QuantumInstance` to run the algorithm on. - Using a :class:`~.QuantumInstance` is pending deprecation and will - be deprecated in a future release. - -.. releasenotes/notes/0.22/backend-converter-05360f12f9042829.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new class, :class:`~.BackendV2Converter`, which is used to wrap - a :class:`~.BackendV1` instance in a :class:`~.BackendV2` interface. It - enables you to have a :class:`~.BackendV2` instance from any - :class:`~.BackendV1`. This enables standardizing access patterns on the - newer :class:`~.BackendV2` interface even if you still support - :class:`~.BackendV1`. - -.. releasenotes/notes/0.22/backend-converter-05360f12f9042829.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new function :func:`~.convert_to_target` which is used to take - a :class:`~.BackendConfiguration`, and optionally a - :class:`~.BackendProperties` and :class:`~.PulseDefaults` and create - a :class:`~.Target` object equivalent to the contents of those objects. - -.. releasenotes/notes/0.22/base-operators-sums-d331e78a9fa4b5d8.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- ``qiskit.quantum_info.BaseOperator`` subclasses (such as :class:`.ScalarOp`, - :class:`.SparsePauliOp` and :class:`.PauliList`) can now be used with - the built-in Python ``sum()`` function. - -.. releasenotes/notes/0.22/c_if-to-if_else-converter-2d48046de31814a8.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- A new transpiler pass, :class:`.ConvertConditionsToIfOps` was added, which - can be used to convert old-style :meth:`.Instruction.c_if`-conditioned - instructions into :class:`.IfElseOp` objects. This is to help ease the transition - from the old type to the new type for backends. For most users, there is no - need to add this to your pass managers, and it is not included in any preset - pass managers. - -.. releasenotes/notes/0.22/commutative-inverse-cancellation-a10e72d8e42ac74b.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Refactored gate commutativity analysis into a class :class:`~qiskit.circuit.CommutationChecker`. - This class allows you to check (based on matrix multiplication) whether two gates commute or do not commute, - and to cache the results (so that a similar check in the future will no longer require matrix - multiplication). - - For example we can now do:: - - from qiskit.circuit import QuantumRegister, CommutationChecker - - comm_checker = CommutationChecker() - qr = QuantumRegister(4) - - res = comm_checker.commute(CXGate(), [qr[1], qr[0]], [], CXGate(), [qr[1], qr[2]], []) - - As the two CX gates commute (the first CX gate is over qubits ``qr[1]`` and ``qr[0]``, and the - second CX gate is over qubits ``qr[1]`` and ``qr[2]``), we will have that ``res`` is ``True``. - - This commutativity checking is over-conservative for conditional and parameterized gates, - and may return ``False`` even when such gates commute. - -.. releasenotes/notes/0.22/commutative-inverse-cancellation-a10e72d8e42ac74b.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new transpiler pass :class:`.CommutativeInverseCancellation` that cancels pairs of - inverse gates exploiting commutation relations between gates. This pass is a generalization - of the transpiler pass :class:`.InverseCancellation` as it detects a larger set of inverse - gates, and as it takes commutativity into account. The pass also avoids some problems - associated with the transpiler pass :class:`.CommutativeCancellation`. - - For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.transpiler import PassManager - from qiskit.transpiler.passes import CommutativeInverseCancellation - - circuit = QuantumCircuit(2) - circuit.z(0) - circuit.x(1) - circuit.cx(0, 1) - circuit.z(0) - circuit.x(1) - - passmanager = PassManager(CommutativeInverseCancellation()) - new_circuit = passmanager.run(circuit) - - cancels the pair of self-inverse `Z`-gates, and the pair of self-inverse `X`-gates (as the - relevant gates commute with the `CX`-gate), producing a circuit consisting of a single `CX`-gate. - - The inverse checking is over-conservative for conditional and parameterized gates, - and may not cancel some of such gates. - -.. releasenotes/notes/0.22/compose-meas-no-meas-492ce91167d54154.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- :meth:`.QuantumCircuit.compose` will now accept an operand with classical - bits if the base circuit has none itself. The pattern of composing a - circuit with measurements onto a quantum-only circuit is - now valid. For example:: - - from qiskit import QuantumCircuit - - base = QuantumCircuit(3) - terminus = QuantumCircuit(3, 3) - terminus.measure_all() - - # This will now succeed, though it was previously a CircuitError. - base.compose(terminus) - -.. releasenotes/notes/0.22/control-flow-depth-size-b598a4eb9d8888eb.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`.DAGCircuit` methods :meth:`~.DAGCircuit.depth` and - :meth:`~.DAGCircuit.size` have a new ``recurse`` keyword argument for use with - circuits that contain control-flow operations (such as :class:`~.IfElseOp`, - :class:`~.WhileLoopOp`, and :class:`~.ForLoopOp`). By default this is ``False`` - and will raise an error if control-flow operations are present, to avoid poorly - defined results. If set to ``True``, a proxy value that attempts to fairly weigh - each control-flow block relative to its condition is returned, even though the - depth or size of a concrete run is generally unknowable. See each method's - documentation for how each control-flow operation affects the output. - -.. releasenotes/notes/0.22/control-flow-depth-size-b598a4eb9d8888eb.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- :meth:`.DAGCircuit.count_ops` gained a ``recurse`` keyword argument for - recursing into control-flow blocks. By default this is ``True``, and all - operations in all blocks will be returned, as well as the control-flow - operations themselves. - -.. releasenotes/notes/0.22/dag_dependency_speedup-f6298348cb3d8746.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added an argument ``create_preds_and_succs`` to the functions - :func:`~qiskit.converters.circuit_to_dagdependency` and - :func:`~qiskit.converters.dag_to_dagdependency` - that convert from :class:`~qiskit.circuit.QuantumCircuit` and - :class:`~qiskit.dagcircuit.DAGCircuit`, respectively, to - :class:`~qiskit.dagcircuit.DAGDependency`. - When the value of ``create_preds_and_succs`` is False, the transitive - predecessors and successors for nodes in :class:`~qiskit.dagcircuit.DAGDependency` - are not constructed, making the conversions faster and significantly less - memory-intensive. The direct predecessors and successors for nodes in - :class:`~qiskit.dagcircuit.DAGDependency` are constructed as usual. - - For example:: - - from qiskit.converters import circuit_to_dagdependency - from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit - - circuit_in = QuantumCircuit(2) - circuit_in.h(qr[0]) - circuit_in.h(qr[1]) - - dag_dependency = circuit_to_dagdependency(circuit_in, create_preds_and_succs=False) - -.. releasenotes/notes/0.22/deprecate-stabilizer-table-9efd08c7de1a5b4d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added new attributes :attr:`.Clifford.symplectic_matrix`, :attr:`.Clifford.tableau`, - :attr:`.Clifford.z`, :attr:`.Clifford.x`, :attr:`.Clifford.phase`, - :attr:`.Clifford.stab`, :attr:`.Clifford.stab_z`, :attr:`.Clifford.stab_x`, :attr:`.Clifford.stab_phase`, - :attr:`.Clifford.destab`, :attr:`.Clifford.destab_z`, :attr:`.Clifford.destab_x`, :attr:`.Clifford.destab_phase` - to the :class:`~.Clifford` class. These can be used instead of :attr:`.Clifford.table`, that will be deprecated in the future. - :class:`.StabilizerTable` and :class:`.PauliTable` are pending deprecation and - will be deprecated in the future release and subsequently removed after that. - -.. releasenotes/notes/0.22/edge-coloring-e55700fcf8902c79.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`.Commuting2qGateRouter` constructor now has a new keyword - argument, ``edge_coloring``. This argument is used to provide an edge - coloring of the coupling map to determine the order in which the - commuting gates are applied. - -.. releasenotes/notes/0.22/evolution-framework-primitives-c86779b5d0dffd25.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new algorithms interface for creating time evolution algorithms - using the primitives :class:`~.BaseSampler` and :class:`~.BaseEstimator`. - This new interface consists of: - - * :class:`~qiskit.algorithms.TimeEvolutionProblem` - * :class:`~qiskit.algorithms.TimeEvolutionResult` - * :class:`~qiskit.algorithms.ImaginaryTimeEvolver` - * :class:`~qiskit.algorithms.RealTimeEvolver` - - This new interface is an alternative to the previously existing time - evolution algorithms interface available defined with - :class:`~.EvolutionProblem`, :class:`~.EvolutionResult`, - :class:`~.RealEvolver`, and :class:`~.ImaginaryEvolver` which worked - with a :class:`~.QuantumInstance` object instead of primitives. This - new interface supersedes the previous interface which will eventually - be deprecated and subsequently removed in future releases. - -.. releasenotes/notes/0.22/fake_auckland-deadbeef.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added new backend classes to :mod:`qiskit.providers.fake_provider`: - - * :class:`~.FakeAuckland` - * :class:`~.FakeOslo` - * :class:`~.FakeGeneva` - * :class:`~.FakePerth` - - These new classes implement the :class:`~.BackendV2` interface and - are created using stored snapshots of the backend information from the - IBM Quantum systems ``ibm_auckland``, ``ibm_oslo``, ``ibm_geneva``, and - ``ibm_perth`` systems respectively. - -.. releasenotes/notes/0.22/implements_two_step_tapering-f481a8cac3990cd5.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`~qiskit.opflow.primitive_ops.Z2Symmetries` class has two new methods, - :meth:`~qiskit.opflow.primitive_ops.Z2Symmetries.convert_clifford` and - :meth:`~qiskit.opflow.primitive_ops.Z2Symmetries.taper_clifford`. These two methods are the two - operations necessary for taperng an operator based on the Z2 symmetries - in the object and were previously performed internally via the - :meth:`~qiskit.opflow.primitive_ops.Z2Symmetries.taper` method. However, these methods are now - public methods of the class which can be called individually if needed. - -.. releasenotes/notes/0.22/improve-basepauli-evolve-clifford-d714b2eee475334b.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The runtime performance for conjugation of a long :class:`.PauliList` - object by a :class:`.Clifford` using the :meth:`.PauliList.evolve` - has significantly improved. It will now run significantly faster than - before. - -.. releasenotes/notes/0.22/introduce-classical-io-channel-0a616e6ca75b7687.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new abstract class :class:`~.ClassicalIOChannel` to the - :mod:`qiskit.pulse.channels` module. This class is used to represent - classical I/O channels and differentiate these channels from other - subclasses of :class:`~qiskit.pulse.channels.Channel`. This new class is - the base class for the :class:`~.MemorySlot`, :class:`~.RegisterSlot`, - and :class:`~.SnapshotChannel` classes. Accordingly, the - :func:`~qiskit.pulse.transforms.pad` canonicalization pulse transform in - :mod:`qiskit.pulse.transforms` will not introduce delays to any instances - of :class:`~.ClassicalIOChannel` - -.. releasenotes/notes/0.22/multiple-parallel-rusty-sabres-32bc93f79ae48a1f.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`~.SabreSwap` transpiler pass has a new keyword argument on its - constructor, ``trials``. The ``trials`` argument is used to specify the - number of random seed trials to attempt. The output from the - `SABRE algorithm `__ can differ greatly - based on the seed used for the random number. :class:`~.SabreSwap` will - now run the algorithm with ``trials`` number of random seeds and pick the - best (with the fewest swaps inserted). If ``trials`` is not specified the - pass will default to use the number of physical CPUs on the local system. - -.. releasenotes/notes/0.22/multiple-parallel-rusty-sabres-32bc93f79ae48a1f.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`~.SabreLayout` transpiler pass has a new keyword argument on - its constructor, ``swap_trials``. The ``swap_trials`` argument is used - to specify how many random seed trials to run on the :class:`~.SabreSwap` - pass internally. It corresponds to the ``trials`` arugment on the - :class:`~.SabreSwap` pass. When set, each iteration of - :class:`~.SabreSwap` will be run internally ``swap_trials`` times. - If ``swap_trials`` is not specified the will default to use - the number of physical CPUs on the local system. - -.. releasenotes/notes/0.22/observable-eval-primitives-e1fd989e15c7760c.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new function, :func:`~.estimate_observables` which uses an - implementation of the :class:`~.BaseEstimator` interface (e.g. - :class:`~.Estimator`, :class:`~.BackendEstimator`, or any provider - implementations such as those as those present in ``qiskit-ibm-runtime`` - and ``qiskit-aer``) to calculate the expectation values, their means and - standard deviations from a list or dictionary of observables. This - serves a similar purpose to the pre-existing function - :func:`~.eval_observables` which performed the calculation using - a :class:`~.QuantumInstance` object and has been superseded (and will be - deprecated and subsequently removed in future releases) by this - new function. - -.. releasenotes/notes/0.22/operation-abstract-base-class-c5efe020aa9caf46.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new :class:`.Operation` base class which provides a lightweight abstract interface - for objects that can be put on :class:`.QuantumCircuit`. This allows to store "higher-level" - objects directly on a circuit (for instance, :class:`.Clifford` objects), to directly combine such objects - (for instance, to compose several consecutive :class:`.Clifford` objects over the same qubits), and - to synthesize such objects at run time (for instance, to synthesize :class:`.Clifford` in - a way that optimizes depth and/or exploits device connectivity). - Previously, only subclasses of :class:`qiskit.circuit.Instruction` could be put on - :class:`.QuantumCircuit`, but this interface has become unwieldy and includes too many methods - and attributes for general-purpose objects. - - The new :class:`.Operation` interface includes ``name``, ``num_qubits`` and ``num_clbits`` - (in the future this may be slightly adjusted), but importantly does not include ``definition`` - (and thus does not tie synthesis to the object), does not include ``condition`` - (this should be part of separate classical control flow), and does not include ``duration`` and - ``unit`` (as these are properties of the output of the transpiler). - - As of now, :class:`.Operation` includes :class:`.Gate`, :class:`.Reset`, :class:`.Barrier`, - :class:`.Measure`, and "higher-level" objects such as :class:`.Clifford`. This list of - "higher-level" objects will grow in the future. - -.. releasenotes/notes/0.22/operation-abstract-base-class-c5efe020aa9caf46.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- A :class:`.Clifford` is now added to a quantum circuit as an :class:`.Operation`, without first - synthesizing a subcircuit implementing this Clifford. The actual synthesis is postponed - to a later :class:`.HighLevelSynthesis` transpilation pass. - - For example, the following code:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import random_clifford - - qc = QuantumCircuit(3) - cliff = random_clifford(2) - qc.append(cliff, [0, 1]) - - no longer converts ``cliff`` to :class:`qiskit.circuit.Instruction`, which includes - synthesizing the clifford into a circuit, when it is appended to ``qc``. - -.. releasenotes/notes/0.22/operation-abstract-base-class-c5efe020aa9caf46.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new transpiler pass :class:`.OptimizeCliffords` that collects blocks of consecutive - :class:`.Clifford` objects in a circuit, and replaces each block with a single :class:`.Clifford`. - - For example, the following code:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import random_clifford - from qiskit.transpiler.passes import OptimizeCliffords - from qiskit.transpiler import PassManager - - qc = QuantumCircuit(3) - cliff1 = random_clifford(2) - cliff2 = random_clifford(2) - qc.append(cliff1, [2, 1]) - qc.append(cliff2, [2, 1]) - qc_optimized = PassManager(OptimizeCliffords()).run(qc) - - first stores the two Cliffords ``cliff1`` and ``cliff2`` on ``qc`` as "higher-level" objects, - and then the transpiler pass :class:`.OptimizeCliffords` optimizes the circuit by composing - these two Cliffords into a single Clifford. Note that the resulting Clifford is still stored - on ``qc`` as a higher-level object. This pass is not yet included in any of preset pass - managers. - -.. releasenotes/notes/0.22/operation-abstract-base-class-c5efe020aa9caf46.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new transpiler pass :class:`.HighLevelSynthesis` that synthesizes higher-level objects - (for instance, :class:`.Clifford` objects). - - For example, the following code:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import random_clifford - from qiskit.transpiler import PassManager - from qiskit.transpiler.passes import HighLevelSynthesis - - qc = QuantumCircuit(3) - qc.h(0) - cliff = random_clifford(2) - qc.append(cliff, [0, 1]) - - qc_synthesized = PassManager(HighLevelSynthesis()).run(qc) - - will synthesize the higher-level Clifford stored in ``qc`` using the default - :func:`~qiskit.quantum_info.decompose_clifford` function. - - This new transpiler pass :class:`.HighLevelSynthesis` is integrated into the preset pass managers, - running right after :class:`.UnitarySynthesis` pass. Thus, :func:`.transpile` will - synthesize all higher-level Cliffords present in the circuit. - - It is important to note that the work done to store :class:`.Clifford` objects as "higher-level" - objects and to transpile these objects using :class:`.HighLevelSynthesis` pass should be completely - transparent, and no code changes are required. - -.. releasenotes/notes/0.22/operator-parameters-c81b7c05bffb740b.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- :class:`.SparsePauliOp`\ s can now be constructed with coefficient arrays - that are general Python objects. This is intended for use with - :class:`.ParameterExpression` objects; other objects may work, but do not - have first-class support. Some :class:`.SparsePauliOp` methods (such as - conversion to other class representations) may not work when using - ``object`` arrays, if the desired target cannot represent these general - arrays. - - For example, a :class:`.ParameterExpression` :class:`.SparsePauliOp` could - be constructed by:: - - import numpy as np - from qiskit.circuit import Parameter - from qiskit.quantum_info import SparsePauliOp - - print(SparsePauliOp(["II", "XZ"], np.array([Parameter("a"), Parameter("b")]))) - - which gives - - .. code-block:: text - - SparsePauliOp(['II', 'XZ'], - coeffs=[ParameterExpression(1.0*a), ParameterExpression(1.0*b)]) - -.. releasenotes/notes/0.22/plot-hist-797bfaeea2156c53.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new function :func:`~.plot_distribution` for plotting distributions over quasi-probabilities. - This is suitable for ``Counts``, ``QuasiDistribution`` and ``ProbDistribution``. - Raw `dict` can be passed as well. For example: - - .. code-block:: python - - from qiskit.visualization import plot_distribution - - quasi_dist = {'0': .98, '1': -.01} - plot_distribution(quasi_dist) - -.. releasenotes/notes/0.22/pluggable-high-level-synthesis-3af9976b22e012d9.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Introduced a new high level synthesis plugin interface which is used to enable - using alternative synthesis techniques included in external packages - seamlessly with the :class:`~qiskit.transpiler.passes.HighLevelSynthesis` - transpiler pass. These alternative synthesis techniques can be specified for - any "higher-level" objects of type :class:`~.Operation`, as for example for - :class:`~.Clifford` and :class:`~.LinearFunction` objects. This plugin interface - is similar to the one for unitary synthesis. In the latter case, the details on writing - a new plugin appear in the :mod:`qiskit.transpiler.passes.synthesis.plugin` module documentation. - -.. releasenotes/notes/0.22/pluggable-high-level-synthesis-3af9976b22e012d9.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Introduced a new class :class:`~.HLSConfig` which can be used to specify alternative synthesis - algorithms for "higher-level" objects of type :class:`~.Operation`. - For each higher-level object of interest, an object :class:`~.HLSConfig` specifies a list of - synthesis methods and their arguments. - This object can be passed to :class:`.HighLevelSynthesis` transpiler pass or specified - as a parameter ``hls_config`` in :func:`~qiskit.compiler.transpile`. - - As an example, let us assume that ``op_a`` and ``op_b`` are names of two higher-level objects, - that ``op_a``-objects have two synthesis methods ``default`` which does require any additional - parameters and ``other`` with two optional integer parameters ``option_1`` and ``option_2``, - that ``op_b``-objects have a single synthesis method ``default``, and ``qc`` is a quantum - circuit containing ``op_a`` and ``op_b`` objects. The following code snippet:: - - hls_config = HLSConfig(op_b=[("other", {"option_1": 7, "option_2": 4})]) - pm = PassManager([HighLevelSynthesis(hls_config=hls_config)]) - transpiled_qc = pm.run(qc) - - shows how to run the alternative synthesis method ``other`` for ``op_b``-objects, while using the - ``default`` methods for all other high-level objects, including ``op_a``-objects. - -.. releasenotes/notes/0.22/primitive-run-5d1afab3655330a6.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added new methods for executing primitives: :meth:`.BaseSampler.run` and :meth:`.BaseEstimator.run`. - These methods execute asynchronously and return :class:`.JobV1` objects which - provide a handle to the exections. These new run methods can be passed :class:`~.QuantumCircuit` - objects (and observables for :class:`~.BaseEstimator`) that are not registered in the constructor. - For example:: - - estimator = Estimator() - result = estimator.run(circuits, observables, parameter_values).result() - - This provides an alternative to the previous execution model (which is now deprecated) for the - :class:`~.BaseSampler` and :class:`~.BaseEstimator` primitives which would take all the inputs via - the constructor and calling the primitive object with the combination of those input parameters - to use in the execution. - -.. releasenotes/notes/0.22/primitive-shots-option-ed320872d048483e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added ``shots`` option for reference implementations of primitives. - Random numbers can be fixed by giving ``seed_primitive``. For example:: - - from qiskit.primitives import Sampler - from qiskit import QuantumCircuit - - bell = QuantumCircuit(2) - bell.h(0) - bell.cx(0, 1) - bell.measure_all() - - with Sampler(circuits=[bell]) as sampler: - result = sampler(circuits=[0], shots=1024, seed_primitive=15) - print([q.binary_probabilities() for q in result.quasi_dists]) - -.. releasenotes/notes/0.22/primitives-run_options-eb4a360c3f1e197d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The constructors for the :class:`~.BaseSampler` and - :class:`~.BaseEstimator` primitive classes have a new optional keyword - argument, ``options`` which is used to set the default values for the - options exposed via the :attr:`~.BaseSampler.options` attribute. - -.. releasenotes/notes/0.22/project-dynamics-primitives-6003336d0866ca19.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added the :class:`~.PVQD` class to the time evolution framework in :mod:`qiskit.algorithms`. - This class implements the projected Variational Quantum Dynamics (p-VQD) algorithm - `Barison et al. `_. - - In each timestep this algorithm computes the next state with a Trotter formula and projects it - onto a variational form. The projection is determined by maximizing the fidelity of the - Trotter-evolved state and the ansatz, using a classical optimization routine. - - .. code-block:: python - - import numpy as np - - from qiskit.algorithms.state_fidelities import ComputeUncompute - from qiskit.algorithms.evolvers import EvolutionProblem - from qiskit.algorithms.time_evolvers.pvqd import PVQD - from qiskit.primitives import Estimator, Sampler - from qiskit import BasicAer - from qiskit.circuit.library import EfficientSU2 - from qiskit.quantum_info import Pauli, SparsePauliOp - from qiskit.algorithms.optimizers import L_BFGS_B - - sampler = Sampler() - fidelity = ComputeUncompute(sampler) - estimator = Estimator() - hamiltonian = 0.1 * SparsePauliOp([Pauli("ZZ"), Pauli("IX"), Pauli("XI")]) - observable = Pauli("ZZ") - ansatz = EfficientSU2(2, reps=1) - initial_parameters = np.zeros(ansatz.num_parameters) - - time = 1 - optimizer = L_BFGS_B() - - # setup the algorithm - pvqd = PVQD( - fidelity, - ansatz, - initial_parameters, - estimator, - num_timesteps=100, - optimizer=optimizer, - ) - - # specify the evolution problem - problem = EvolutionProblem( - hamiltonian, time, aux_operators=[hamiltonian, observable] - ) - - # and evolve! - result = pvqd.evolve(problem) - -.. releasenotes/notes/0.22/qnspsa-primitification-29a9dcae055bf2b4.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :meth:`.QNSPSA.get_fidelity` static method now supports an optional - ``sampler`` argument which is used to provide an implementation of the - :class:`~.BaseSampler` interface (such as :class:`~.Sampler`, - :class:`~.BackendSampler`, or any provider implementations such as those - present in ``qiskit-ibm-runtime`` and ``qiskit-aer``) to compute the - fidelity of a :class:`~.QuantumCircuit`. For example:: - - from qiskit.primitives import Sampler - from qiskit.algorithms.optimizers import QNSPSA - - fidelity = QNSPSA.get_fidelity(my_circuit, Sampler()) - -.. releasenotes/notes/0.22/qpe-algorithms-primitives-3605bdfa5ab1bfef.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new keyword argument ``sampler`` to the constructors of the - phase estimation classes: - - * :class:`~qiskit.algorithms.IterativePhaseEstimation` - * :class:`~qiskit.algorithms.PhaseEstimation` - * :class:`~qiskit.algorithms.HamiltonianPhaseEstimation` - - This argument is used to provide an implementation of the - :class:`~qiskit.primitives.BaseSampler` interface such as :class:`~.Sampler`, - :class:`~.BackendSampler`, or any provider implementations such as those - as those present in ``qiskit-ibm-runtime`` and ``qiskit-aer``. - - For example: - - .. code-block:: python - - from qiskit.primitives import Sampler - from qiskit.algorithms.phase_estimators import HamiltonianPhaseEstimation - from qiskit.synthesis import MatrixExponential - from qiskit.quantum_info import SparsePauliOp - from qiskit.opflow import PauliSumOp - - - sampler = Sampler() - num_evaluation_qubits = 6 - phase_est = HamiltonianPhaseEstimation( - num_evaluation_qubits=num_evaluation_qubits, sampler=sampler - ) - - hamiltonian = PauliSumOp(SparsePauliOp.from_list([("X", 0.5), ("Y", 0.6), ("I", 0.7)])) - result = phase_est.estimate( - hamiltonian=hamiltonian, - state_preparation=None, - evolution=MatrixExponential(), - bound=1.05, - ) - -.. releasenotes/notes/0.22/rabre-rwap-ae51631bec7450df.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`~.SabreSwap` transpiler pass has significantly improved - runtime performance due to a rewrite of the algorithm in Rust. - -.. releasenotes/notes/0.22/remove-symbolic-pulse-subclasses-77314a1654521852.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Symbolic pulse subclasses :class:`.Gaussian`, :class:`.GaussianSquare`, - :class:`.Drag` and :class:`.Constant` have been upgraded to - instantiate :class:`SymbolicPulse` rather than the subclass itself. - All parametric pulse objects in pulse programs must be symbolic pulse instances, - because subclassing is no longer neccesary. Note that :class:`SymbolicPulse` can - uniquely identify a particular envelope with the symbolic expression object - defined in :attr:`SymbolicPulse.envelope`. - -.. releasenotes/notes/0.22/sampled_expval-85e300e0fb5fa5ea.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new function, :func:`~.sampled_expectation_value`, that allows - for computing expectation values for diagonal operators from - distributions such as :class:`~.Counts` and :class:`~.QuasiDistribution`. - Valid operators for use with this function are: ``str``, :class:`~.Pauli`, - :class:`~.PauliOp`, :class:`~.PauliSumOp`, and - :class:`~.SparsePauliOp`. - -.. releasenotes/notes/0.22/sampling-vqe-and-qaoa-ecfb36a0a300f69b.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- A :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` class is introduced, which is - optimized for diagonal hamiltonians and leverages a ``sampler`` primitive. A - :class:`~qiskit.algorithms.minimum_eigensolvers.QAOA` class is also added that subclasses - ``SamplingVQE``. - - To use the new ``SamplingVQE`` with a reference primitive, one can do, for example: - - .. code-block:: python - - from qiskit.algorithms.minimum_eigensolvers import SamplingVQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.primitives import Sampler - from qiskit.opflow import PauliSumOp - from qiskit.quantum_info import SparsePauliOp - - operator = PauliSumOp(SparsePauliOp(["ZZ", "IZ", "II"], coeffs=[1, -0.5, 0.12])) - - sampler = Sampler() - ansatz = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - optimizer = SLSQP() - - sampling_vqe = SamplingVQE(sampler, ansatz, optimizer) - result = sampling_vqe.compute_minimum_eigenvalue(operator) - eigenvalue = result.eigenvalue - - Note that the evaluated auxillary operators are now obtained via the - ``aux_operators_evaluated`` field on the results. This will consist of a list or dict of - tuples containing the expectation values for these operators, as we well as the metadata from - primitive run. ``aux_operator_eigenvalues`` is no longer a valid field. - -.. releasenotes/notes/0.22/sparse-pauli-equiv-atol-58f5dfe7f39b70ee.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new ``atol`` keyword argument to the :meth:`.SparsePauliOp.equiv` - method to adjust to tolerance of the equivalence check, - -.. releasenotes/notes/0.22/stage-plugin-interface-47daae40f7d0ad3c.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Introduced a new plugin interface for transpiler stages which is used to - enable alternative :class:`~.PassManager` objects from an external package - in a particular stage as part of :func:`~.transpile` or the - :class:`~.StagedPassManager` output from - :func:`~.generate_preset_pass_manager`, :func:`~.level_0_pass_manager`, - :func:`~.level_1_pass_manager`, :func:`~.level_2_pass_manager`, and - :func:`~.level_3_pass_manager`. Users can select a plugin to use for a - transpiler stage with the ``init_method``, ``layout_method``, - ``routing_method``, ``translation_method``, ``optimization_method``, and - ``scheduling_method`` keyword arguments on :func:`~.transpile` and - :func:`~.generate_preset_pass_manager`. A full list of plugin names - currently installed can be found with the :func:`.list_stage_plugins` - function. For creating plugins refer to the - :mod:`qiskit.transpiler.preset_passmanagers.plugin` module documentation - which includes a guide for writing stage plugins. - -.. releasenotes/notes/0.22/stage-plugin-interface-47daae40f7d0ad3c.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :func:`~.transpile` has two new keyword arguments, ``init_method`` and - ``optimization_method`` which are used to specify alternative plugins to - use for the ``init`` stage and ``optimization`` stages respectively. - -.. releasenotes/notes/0.22/stage-plugin-interface-47daae40f7d0ad3c.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`~.PassManagerConfig` class has 2 new attributes, - :attr:`~.PassManagerConfig.init_method` and - :attr:`~.PassManagerConfig.optimization_method` - along with matching keyword arguments on the constructor methods. These represent - the user specified ``init`` and ``optimization`` plugins to use for - compilation. - -.. releasenotes/notes/0.22/steppable-optimizers-9d9b48ba78bd58bb.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`~.SteppableOptimizer` class is added. It allows one to perfore classical - optimizations step-by-step using the :meth:`~.SteppableOptimizer.step` method. These - optimizers implement the "ask and tell" interface which (optionally) allows to manually compute - the required function or gradient evaluations and plug them back into the optimizer. - For more information about this interface see: `ask and tell interface - `_. - A very simple use case when the user might want to do the optimization step by step is for - readout: - - .. code-block:: python - - import random - import numpy as np - from qiskit.algorithms.optimizers import GradientDescent - - def objective(x): - return (np.linalg.norm(x) - 1) ** 2 - - def grad(x): - return 2 * (np.linalg.norm(x) - 1) * x / np.linalg.norm(x) - - - initial_point = np.random.normal(0, 1, size=(100,)) - - optimizer = GradientDescent(maxiter=20) - optimizer.start(x0=initial_point, fun=objective, jac=grad) - - for _ in range(maxiter): - state = optimizer.state - # Here you can manually read out anything from the optimizer state. - optimizer.step() - - result = optimizer.create_result() - - A more complex case would be error handling. Imagine that the function you are evaluating has - a random chance of failing. In this case you can catch the error and run the function again - until it yields the desired result before continuing the optimization process. In this case - one would use the ask and tell interface. - - .. code-block:: python - - import random - import numpy as np - from qiskit.algorithms.optimizers import GradientDescent - - def objective(x): - if random.choice([True, False]): - return None - else: - return (np.linalg.norm(x) - 1) ** 2 - - def grad(x): - if random.choice([True, False]): - return None - else: - return 2 * (np.linalg.norm(x) - 1) * x / np.linalg.norm(x) - - - initial_point = np.random.normal(0, 1, size=(100,)) - - optimizer = GradientDescent(maxiter=20) - optimizer.start(x0=initial_point, fun=objective, jac=grad) - - while optimizer.continue_condition(): - ask_data = optimizer.ask() - evaluated_gradient = None - - while evaluated_gradient is None: - evaluated_gradient = grad(ask_data.x_center) - optimizer.state.njev += 1 - - optmizer.state.nit += 1 - - cf = TellData(eval_jac=evaluated_gradient) - optimizer.tell(ask_data=ask_data, tell_data=tell_data) - - result = optimizer.create_result() - - Transitioned :class:`GradientDescent` to be a subclass of :class:`.SteppableOptimizer`. - -.. releasenotes/notes/0.22/tensored-subset-fitter-bd28e6e6ec5bdaae.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The ``subset_fitter`` method is added to the :class:`.TensoredMeasFitter` - class. The implementation is restricted to mitigation patterns in which each - qubit is mitigated individually, e.g. ``[[0], [1], [2]]``. This is, however, - the most widely used case. It allows the :class:`.TensoredMeasFitter` to - be used in cases where the numberical order of the physical qubits does not - match the index of the classical bit. - -.. releasenotes/notes/0.22/transpiler-control-flow-708896bfdb51961d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Control-flow operations are now supported through the transpiler at - optimization levels 0 and 1 (e.g. calling :func:`.transpile` or - :func:`.generate_preset_pass_manager` with keyword argument - ``optimization_level=1``). One can now construct a circuit such as - - .. code-block:: python - - from qiskit import QuantumCircuit - - qc = QuantumCircuit(2, 1) - qc.h(0) - qc.measure(0, 0) - with qc.if_test((0, True)) as else_: - qc.x(1) - with else_: - qc.y(1) - - and successfully transpile this, such as by:: - - from qiskit import transpile - from qiskit_aer import AerSimulator - - backend = AerSimulator(method="statevector") - transpiled = transpile(qc, backend) - - The available values for the keyword argument ``layout_method`` are - "trivial" and "dense". For ``routing_method``, "stochastic" and "none" are - available. Translation (``translation_method``) can be done using - "translator" or "unroller". Optimization levels 2 and 3 are not yet - supported with control flow, nor is circuit scheduling (i.e. providing a - value to ``scheduling_method``), though we intend to expand support for - these, and the other layout, routing and translation methods in subsequent - releases of Qiskit Terra. - - In order for transpilation with control-flow operations to succeed with a - backend, the backend must have the requisite control-flow operations in its - stated basis. Qiskit Aer, for example, does this. If you simply want to try - out such transpilations, consider overriding the ``basis_gates`` argument - to :func:`.transpile`. - -.. releasenotes/notes/0.22/transpiler-control-flow-708896bfdb51961d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The following transpiler passes have all been taught to understand - control-flow constructs in the form of :class:`.ControlFlowOp` instructions - in a circuit: - - .. rubric:: Layout-related - - - :class:`.ApplyLayout` - - :class:`.DenseLayout` - - :class:`.EnlargeWithAncilla` - - :class:`.FullAncillaAllocation` - - :class:`.SetLayout` - - :class:`.TrivialLayout` - - :class:`.VF2Layout` - - :class:`.VF2PostLayout` - - .. rubric:: Routing-related - - - :class:`.CheckGateDirection` - - :class:`.CheckMap` - - :class:`.GateDirection` - - :class:`.StochasticSwap` - - .. rubric:: Translation-related - - - :class:`.BasisTranslator` - - :class:`.ContainsInstruction` - - :class:`.GatesInBasis` - - :class:`.UnitarySynthesis` - - :class:`.Unroll3qOrMore` - - :class:`.UnrollCustomDefinitions` - - :class:`.Unroller` - - .. rubric:: Optimization-related - - - :class:`.BarrierBeforeFinalMeasurements` - - :class:`.Depth` - - :class:`.FixedPoint` - - :class:`.Size` - - :class:`.Optimize1qGatesDecomposition` - - :class:`.CXCancellation` - - :class:`.RemoveResetInZeroState` - - These passes are most commonly used via the preset pass managers (those used - internally by :func:`.transpile` and :func:`.generate_preset_pass_manager`), - but are also available for other uses. These passes will now recurse into - control-flow operations where appropriate, updating or analysing the - internal blocks. - -.. releasenotes/notes/0.22/trotter-qrte-primitives-8b3e495738b57fc3.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a new :class:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE` class - that implements the :class:`~.RealTimeEvolver` interface that uses an - :class:`qiskit.primitives.BaseEstimator` to perform the calculation. This - new class supersedes the previously available :class:`qiskit.algorithms.TrotterQRTE` - class (which will be deprecated and subsequenty removed in future releases) that used - a :class:`~.Backend` or :class:`~QuantumInstance` to perform the calculation. - -.. releasenotes/notes/0.22/update-DAGCircuit.substitute_node_with_dag-3a44d16b1a82df41.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- :meth:`.DAGCircuit.substitute_node_with_dag` now takes ``propagate_condition`` - as a keyword argument. This defaults to ``True``, which was the previous - behavior, and copies any condition on the node to be replaced onto every - operation node in the replacement. If set to ``False``, the condition will - not be copied, which allows replacement of a conditional node with a sub-DAG - that already faithfully implements the condition. - -.. releasenotes/notes/0.22/update-DAGCircuit.substitute_node_with_dag-3a44d16b1a82df41.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- :meth:`.DAGCircuit.substitute_node_with_dag` can now take a mapping for its - ``wires`` parameter as well as a sequence. The mapping should map bits in - the replacement DAG to the bits in the DAG it is being inserted into. This - permits an easier style of construction for callers when the input node has - both classical bits and a condition, and the replacement DAG may use these - out-of-order. - -.. releasenotes/notes/0.22/vqe-with-estimator-primitive-7cbcc462ad4dc593.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added the :class:`qiskit.algorithms.minimum_eigensolvers` package to include interfaces for - primitive-enabled algorithms. :class:`~qiskit.algorithms.minimum_eigensolvers.VQE` has been - refactored in this implementation to leverage primitives. - - To use the new implementation with a reference primitive, one can do, for example: - - .. code-block:: python - - from qiskit.algorithms.minimum_eigensolvers import VQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.primitives import Estimator - from qiskit.quantum_info import SparsePauliOp - - h2_op = SparsePauliOp( - ["II", "IZ", "ZI", "ZZ", "XX"], - coeffs=[ - -1.052373245772859, - 0.39793742484318045, - -0.39793742484318045, - -0.01128010425623538, - 0.18093119978423156, - ], - ) - - estimator = Estimator() - ansatz = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - optimizer = SLSQP() - - vqe = VQE(estimator, ansatz, optimizer) - result = vqe.compute_minimum_eigenvalue(h2_op) - eigenvalue = result.eigenvalue - - Note that the evaluated auxillary operators are now obtained via the - ``aux_operators_evaluated`` field on the results. This will consist of a list or dict of - tuples containing the expectation values for these operators, as we well as the metadata from - primitive run. ``aux_operator_eigenvalues`` is no longer a valid field. - -.. _Release Notes_0.22.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.22/fix-target-control-flow-representation-09520e2838f0657e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- For :class:`~.Target` objects that only contain globally defined 2 qubit - operations without any connectivity constaints the return from the - :meth:`.Target.build_coupling_map` method will now return ``None`` instead - of a :class:`~.CouplingMap` object that contains ``num_qubits`` nodes - and no edges. This change was made to better reflect the actual - connectivity constraints of the :class:`~.Target` because in this case - there are no connectivity constraints on the backend being modeled by - the :class:`~.Target`, not a lack of connecitvity. If you desire the - previous behavior for any reason you can reproduce it by checking for a - ``None`` return and manually building a coupling map, for example:: - - from qiskit.transpiler import Target, CouplingMap - from qiskit.circuit.library import CXGate - - target = Target(num_qubits=3) - target.add_instruction(CXGate()) - cmap = target.build_coupling_map() - if cmap is None: - cmap = CouplingMap() - for i in range(target.num_qubits): - cmap.add_physical_qubit(i) - -.. releasenotes/notes/0.22/add-reverse-linear-entanglement-nlocal-38581e4ffb7a7c68.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The default value for the ``entanglement`` keyword argument on the constructor for the - :class:`~.RealAmplitudes` and :class:`~.EfficientSU2` classes has changed from ``"full"`` to - ``"reverse_linear"``. This change was made because the output circuit is equivalent but - uses only :math:`n-1` instead of :math:`\frac{n(n-1)}{2}` :class:`~.CXGate` gates. If you - desire the previous default you can explicity set ``entanglement="full"`` when calling either - constructor. - -.. releasenotes/notes/0.22/add-sampler-error-check-38426fb186db44d4.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Added a validation check to :meth:`.BaseSampler.run`. - It raises an error if there is no classical bit. - -.. releasenotes/notes/0.22/add-schedule-block-reference-mechanism-8a7811e17b4fead3.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Behavior of the :func:`~qiskit.pulse.builder.call` pulse builder function has been upgraded. - When a :class:`.ScheduleBlock` instance is called by this method, it internally creates - a :class:`.Reference` in the current context, and immediately assigns the called program to - the reference. Thus, the :class:`.Call` instruction is no longer generated. - Along with this change, it is prohibited to call different blocks with the same ``name`` - argument. Such operation will result in an error. - -.. releasenotes/notes/0.22/begin-tweedledum-removal-25bb68fc72804f00.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- For most architectures starting in the following release of Qiskit Terra, - 0.23, the ``tweedledum`` package will become an optional dependency, instead - of a requirement. This is currently used by some classical phase-oracle - functions. If your application or library needs this functionality, you may - want to prepare by adding ``tweedledum`` to your package's dependencies - immediately. - - ``tweedledum`` is no longer a requirement on macOS arm64 (M1) with immediate - effect in Qiskit Terra 0.22. This is because the provided wheels for this - platform are broken, and building from the sdist is not reliable for most - people. If you manually install a working version of ``tweedledum``, all - the dependent functionality will continue to work. - -.. releasenotes/notes/0.22/fix-Opertor.from_circuit-transpile-5c056968ee40025e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The ``._layout`` attribute of the :class:`~.QuantumCircuit` object has - been changed from storing a :class:`~.Layout` object to storing a - data class with 2 attributes, ``initial_layout`` which contains a - :class:`~.Layout` object for the initial layout set during compilation - and ``input_qubit_mapping`` which contains a dictionary mapping qubits - to position indices in the original circuit. This change was necessary to - provide all the information for a post-transpiled circuit to be able to - fully reverse the permutation caused by initial layout in all situations. While - this attribute is private and shouldn't be used externally, it is - the only way to track the initial layout through :func:`~.transpile` - so the change is being documented in case you're relying on it. If - you have a use case for the ``_layout`` attribute that is not being - addressed by the Qiskit API please open an issue so we can address this - feature gap. - -.. releasenotes/notes/0.22/introduce-classical-io-channel-0a616e6ca75b7687.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The constructors for the :class:`~.SetPhase`, :class:`~.ShiftPhase`, - :class:`~.SetFrequency`, and :class:`~.ShiftFrequency` classes will now - raise a :class:`~.PulseError` if the value passed in via the ``channel`` - argument is not an instance of :class:`~.PulseChannel`. This change was - made to validate the input to the constructors are valid as the - instructions are only valid for pulse channels and not other types of - channels. - -.. releasenotes/notes/0.22/plot-hist-797bfaeea2156c53.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :func:`~.plot_histogram` function has been modified to return an actual - histogram of discrete binned values. The previous behavior for the function - was despite the name to actually generate a visualization of the distribution - of the input. Due to this disparity between the name of the function and the behavior - the function behavior was changed so it's actually generating a proper histogram - of discrete data now. If you wish to preserve the previous behavior of plotting a - probability distribution of the counts data you can leverage the :func:`~.plot_distribution` to generate an - equivalent graph. For example, the previous behavior of - ``plot_histogram({'00': 512, '11': 500})`` can be re-created with: - - .. code-block:: python - - from qiskit.visualization import plot_distribution - import matplotlib.pyplot as plt - - ax = plt.subplot() - plot_distribution({'00': 512, '11': 500}, ax=ax) - ax.set_ylabel('Probabilities') - -.. releasenotes/notes/0.22/qiskit.pulse.builder-ddefe88dca5765b9.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The ``qiskit.pulse.builder`` contexts ``inline`` and ``pad`` have been - removed. These were first deprecated in Terra 0.18.0 (July 2021). There is - no replacement for ``inline``; one can simply write the pulses in the - containing scope. The ``pad`` context manager has had no effect since it - was deprecated. - -.. releasenotes/notes/0.22/rabre-rwap-ae51631bec7450df.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The output from the :class:`~.SabreSwap` transpiler pass (including when - ``optimization_level=3`` or ``routing_method`` or ``layout_method`` are - set to ``'sabre'`` when calling :func:`~.transpile`) with a fixed - seed value may change from previous releases. This is caused by a new - random number generator being used as part of the rewrite of the - :class:`~.SabreSwap` pass in Rust which significantly improved the - performance. If you rely on having consistent output you can run - the pass in an earlier version of Qiskit and leverage :mod:`qiskit.qpy` - to save the circuit and then load it using the current version. - -.. releasenotes/notes/0.22/register-add-fix-e29fa2ee47aa6d05.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :meth:`.Layout.add` behavior when not specifying a ``physical_bit`` - has changed from previous releases. In previous releases, a new physical - bit would be added based on the length of the :class:`~.Layout` object. For - example if you had a :class:`~.Layout` with the physical bits 1 and 3 - successive calls to :meth:`~.Layout.add` would add physical bits 2, 4, 5, 6, - etc. While if the physical bits were 2 and 3 then successive calls would - add 4, 5, 6, 7, etc. This has changed so that instead :meth:`.Layout.add` - will first add any missing physical bits between 0 and the max physical bit - contained in the :class:`~.Layout`. So for the 1 and 3 example it now - adds 0, 2, 4, 5 and for the 2 and 3 example it adds 0, 1, 4, 5 to the - :class:`~.Layout`. This change was made for both increased predictability - of the outcome, and also to fix a class of bugs caused by the unexpected - behavior. As physical bits on a backend always are contiguous sequences from - 0 to :math:`n` adding new bits when there are still unused physical bits - could potentially cause the layout to use more bits than available on the - backend. If you desire the previous behavior, you can specify the desired - physical bit manually when calling :meth:`.Layout.add`. - -.. releasenotes/notes/0.22/remove-deprecated-methods-in-pauli-c874d463ba1f7a0e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The deprecated method ``SparsePauliOp.table`` attribute has been removed. - It was originally deprecated in Qiskit Terra 0.19. Instead the - :meth:`~.SparsePauliOp.paulis` method should be used. - -.. releasenotes/notes/0.22/remove-deprecated-methods-in-pauli-c874d463ba1f7a0e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Support for returning a :class:`~.PauliTable` from the - :func:`~.pauli_basis` function has been removed. Similarly, the - ``pauli_list`` argument on the :func:`~.pauli_basis` function which was - used to switch to a :class:`~.PauliList` (now the only return type) has - been removed. This functionality was deprecated in the Qiskit Terra 0.19 release. - -.. releasenotes/notes/0.22/remove-pulse-defs-old-20q-4ed46085b4a15678.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The fake backend objects :class:`~.FakeJohannesburg`, - :class:`~.FakeJohannesburgV2`, :class:`~.FakeAlmaden`, - :class:`~.FakeAlmadenV2`, :class:`~.FakeSingapore`, and - :class:`~.FakeSingaporeV2` no longer contain the pulse defaults payloads. - This means for the :class:`~.BackendV1` based classes the - :meth:`.BackendV1.defaults` method and pulse simulation via - :meth:`.BackendV1.run` is no longer available. For :class:`~.BackendV2` - based classes the :attr:`~InstructionProperties.calibration` property for - instructions in the :class:`~.Target` is no longer populated. This - change was done because these systems had exceedingly large pulse defaults - payloads (in total ~50MB) due to using sampled waveforms instead of - parameteric pulse definitions. These three payload files took > 50% of the - disk space required to install qiskit-terra. When weighed against the - potential value of being able to compile with pulse awareness or pulse - simulate these retired devices the file size is not worth the cost. If - you require to leverage these properties you can leverage an older version - of Qiskit and leverage :mod:`~qiskit.qpy` to transfer circuits from - older versions of qiskit into the current release. - -.. releasenotes/notes/0.22/remove-symbolic-pulse-subclasses-77314a1654521852.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- ``isinstance`` check with pulse classes :class:`.Gaussian`, :class:`.GaussianSquare`, - :class:`.Drag` and :class:`.Constant` will be invalidated because - these pulse subclasses are no longer instantiated. They will still work in Terra 0.22, - but you should begin transitioning immediately. - Instead of using type information, :attr:`SymbolicPulse.pulse_type` should be used. - This is assumed to be a unique string identifer for pulse envelopes, - and we can use string equality to investigate the pulse types. For example, - - .. code-block:: python - - from qiskit.pulse.library import Gaussian - - pulse = Gaussian(160, 0.1, 40) - - if isinstance(pulse, Gaussian): - print("This is Gaussian pulse.") - - This code should be upgraded to - - .. code-block:: python - - from qiskit.pulse.library import Gaussian - - pulse = Gaussian(160, 0.1, 40) - - if pulse.pulse_type == "Gaussian": - print("This is Gaussian pulse.") - - With the same reason, the class attributes such as ``pulse.__class__.__name__`` - should not be accessed to get pulse type information. - -.. releasenotes/notes/0.22/remove_QiskitIndexError-098fa04f0afe440b.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The exception ``qiskit.exceptions.QiskitIndexError`` has been - removed and no longer exists as per the deprecation notice from qiskit-terra - 0.18.0 (released on Jul 12, 2021). - -.. releasenotes/notes/0.22/remove_optimizers_L_BFGS_B_epsilon-03f997aff50c394c.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The deprecated arguments ``epsilon`` and ``factr`` for the constructor of - the :class:`~.L_BFGS_B` optimizer class have been removed. These arguments - were originally deprecated as part of the 0.18.0 release (released on - July 12, 2021). Instead the ``ftol`` argument should be used, you - can refer to the `scipy docs `__ - on the optimizer for more detail on the relationship between these arguments. - -.. releasenotes/notes/0.22/sabres-for-everyone-3148ccf2064ccb0d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The preset pass managers for levels 1 and 2, which will be used when - ``optimization_level=1`` or ``optimization_level=2`` with - :func:`~.transpile` or :func:`~.generate_preset_pass_manager` and output - from :func:`~.level_1_pass_manager` and :func:`~.level_2_pass_manager`, - will now use :class:`~.SabreLayout` and :class:`~SabreSwap` by default - instead of the previous defaults :class:`~.DenseLayout` and - :class:`~.StochasticSwap`. This change was made to improve the output - quality of the transpiler, the :class:`~.SabreLayout` and - :class:`~SabreSwap` combination typically results in fewer - :class:`~.SwapGate` objects being inserted into the output circuit. - If you would like to use the previous default passes you can set - ``layout_method='dense'`` and ``routing_method='stochastic'`` on - :func:`~.transpile` or :func:`~.generate_preset_pass_manager` to - leverage :class:`~.DenseLayout` and :class:`~.StochasticSwap` respectively. - -.. releasenotes/notes/0.22/turn-off-approx-degree-df3d39eb69f7f09f.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The implicit use of ``approximation_degree!=1.0`` by default in - in the :func:`~.transpile` function when ``optimization_level=3`` is set has been disabled. The transpiler should, by default, - preserve unitarity of the input up to known transformations such as one-sided permutations - and similarity transformations. This was broken by the previous use of ``approximation_degree=None`` - leading to incorrect results in cases such as Trotterized evolution with many time steps where - unitaries were being overly approximated leading to incorrect results. It was decided that - transformations that break unitary equivalence should be explicitly activated by the user. - If you desire the previous default behavior where synthesized :class:`~UnitaryGate` instructions - are approximated up to the error rates of the target backend's native instructions you can explicitly - set ``approximation_degree=None`` when calling :func:`~.transpile` with ``optimization_level=3``, for - example:: - - transpile(circuit, backend, approximation_degree=None, optimization_level=3) - -.. releasenotes/notes/0.22/update-bfgs-optimizer-29b4ffa6724fbf38.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Change the default of maximum number of allowed function evaluations (``maxfun``) in - :class:`.L_BFGS_B` from 1000 to 15000 to match the SciPy default. - This number also matches the default number of iterations (``maxiter``). - -.. releasenotes/notes/0.22/update-prob-quasi-2044285a46219d14.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Updated :class:`~qiskit.result.ProbDistribution` and :class:`~qiskit.result.QuasiDistribution` - to store the information of the number of bits if bitstrings without prefix "0b" are given. - :meth:`.ProbDistribution.binary_probabilities` and - :meth:`.QuasiDistribution.binary_probabilities` use the stored number of bits - as the default value of the number of bits. - - .. code-block: python - - import qiskit.result import ProbDistribution, QuasiDistribution - - prob = ProbDistribution({"00": 0.5, "01": 0.5}) - quasi = QuasiDistribution({"00": 0.5, "01": 0.5}) - - print(prob.binary_probabilities()) - # {'00': 0.5, '01': 0.5} - - print(quasi.binary_probabilities()) - # {'00': 0.5, '01': 0.5} - -.. releasenotes/notes/0.22/upgrade_rzx_builder_skip_direct_cx-d0beff9b2b86ab8d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- :class:`.RZXCalibrationBuilder` and :class:`.RZXCalibrationBuilderNoEcho` - have been upgraded to skip stretching CX gates implemented by - non-echoed cross resonance (ECR) sequence to avoid termination of the pass - with unexpected errors. - These passes take new argument ``verbose`` that controls whether the passes - warn when this occurs. If ``verbose=True`` is set, pass raises user warning - when it enconters non-ECR sequence. - -.. releasenotes/notes/0.22/visualization-reorganisation-9e302239705c7842.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The visualization module :mod:`qiskit.visualization` has seen some internal - reorganisation. This should not have affected the public interface, but if - you were accessing any internals of the circuit drawers, they may now be in - different places. The only parts of the visualization module that are - considered public are the components that are documented in this online - documentation. - - -.. _Release Notes_0.22.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.22/begin-tweedledum-removal-25bb68fc72804f00.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Importing the names ``Int1``, ``Int2``, ``classical_function`` and - ``BooleanExpression`` directly from :mod:`qiskit.circuit` is deprecated. - This is part of the move to make ``tweedledum`` an optional dependency rather - than a full requirement. Instead, you should import these names from - :mod:`qiskit.circuit.classicalfunction`. - -.. releasenotes/notes/0.22/deprecate-linear-solvers-factorizers-bbf5302484cb6831.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Modules :mod:`qiskit.algorithms.factorizers` and - :mod:`qiskit.algorithms.linear_solvers` are deprecated and will - be removed in a future release. - They are replaced by tutorials in the Qiskit Textbook: - `Shor `__ - `HHL `__ - -.. releasenotes/notes/0.22/deprecate-stabilizer-table-9efd08c7de1a5b4d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :func:`.random_stabilizer_table` has been deprecated and will be removed in a future - release. Instead the :func:`~.random_pauli_list` function should be used. - -.. releasenotes/notes/0.22/deprecated-pulse-deprecator-394ec75079441cda.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The pulse-module function ``qiskit.pulse.utils.deprecated_functionality`` is - deprecated and will be removed in a future release. This was a primarily - internal-only function. The same functionality is supplied by - ``qiskit.utils.deprecate_function``, which should be used instead. - -.. releasenotes/notes/0.22/primitive-run-5d1afab3655330a6.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The method of executing primitives has been changed. - The :meth:`.BaseSampler.__call__` and - :meth:`.BaseEstimator.__call__` methods were deprecated. - For example:: - - estimator = Estimator(...) - result = estimator(circuits, observables, parameters) - - sampler = Sampler(...) - result = sampler(circuits, observables, parameters) - - should be rewritten as - - .. code-block:: python - - estimator = Estimator() - result = estimator.run(circuits, observables, parameter_values).result() - - sampler = Sampler() - result = sampler.run(circuits, parameter_values).result() - - Using primitives as context managers is deprecated. - Not all primitives have a context manager available. When available (e.g. in ``qiskit-ibm-runtime``), - the session's context manager provides equivalent functionality. - - ``circuits``, ``observables``, and ``parameters`` in the constructor was deprecated. - ``circuits`` and ``observables`` can be passed from ``run`` methods. - ``run`` methods do not support ``parameters``. Users need to resort parameter values by themselves. - -.. releasenotes/notes/0.22/upgrade_rzx_builder_skip_direct_cx-d0beff9b2b86ab8d.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The unused argument ``qubit_channel_mapping`` in the - :class:`.RZXCalibrationBuilder` and :class:`.RZXCalibrationBuilderNoEcho` - transpiler passes have been deprecated and will be removed in a future - release. This argument is no longer used and has no effect on the - operation of the passes. - -.. _Release Notes_0.22.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.22/fix_8438-159e67ecb6765d08.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue where :meth:`.Pauli.evolve` and :meth:`.PauliList.evolve` would - raise a dtype error when evolving by certain Clifford gates which - modified the Pauli's phase. - Fixed `#8438 `__ - -.. releasenotes/notes/0.22/circuit-initialize-and-prepare-single-qubit-e25dacc8f873bc01.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed a bug in :meth:`.QuantumCircuit.initialize` and :meth:`.QuantumCircuit.prepare_state` - that caused them to not accept a single :class:`Qubit` as argument to initialize. - -.. releasenotes/notes/0.22/condition-in-while-loop-d6be0d6d6a1429da.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The method :meth:`.QuantumCircuit.while_loop` will now resolve classical bit - references in its condition in the same way that :meth:`.QuantumCircuit.if_test` - and :meth:`.InstructionSet.c_if` do. - -.. releasenotes/notes/0.22/control-flow-depth-size-b598a4eb9d8888eb.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`.DAGCircuit` methods :meth:`~.DAGCircuit.depth`, - :meth:`~.DAGCircuit.size` and :meth:`.DAGCircuit.count_ops` would previously - silently return results that had little-to-no meaning if control-flow was - present in the circuit. The :meth:`~.DAGCircuit.depth` and - :meth:`~.DAGCircuit.size` methods will now correctly throw an error in these - cases, but have a new ``recurse`` keyword argument to allow the calculation - of a proxy value, while :meth:`~.DAGCircuit.count_ops` will by default - recurse into the blocks and count the operations within them. - -.. releasenotes/notes/0.22/denselayout-loose-bits-3e66011432bc6232.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue in the :class:`~.DenseLayout` transpiler pass where any - loose :class:`~.circuit.Qubit` objects (i.e. not part of a :class:`~.QuantumRegister`) - that were part of a :class:`~.QuantumCircuit` would not be included in the - output :class:`~.Layout` that was generated by the pass. - -.. releasenotes/notes/0.22/fix-Opertor.from_circuit-transpile-5c056968ee40025e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :meth:`.Operator.from_circuit` constructor method has been updated - so that it can handle the layout output from :func:`~.transpile` and - correctly reverse the qubit permutation caused by layout in all cases. - Previously, if your transpiled circuit used loose :class:`~.circuit.Qubit` objects, - multiple :class:`~.QuantumRegister` objects, or a single - :class:`~.QuantumRegister` with a name other than ``"q"`` the constructor - would have failed to create an :class:`~.Operator` from the circuit. - Fixed `#8800 `__. - -.. releasenotes/notes/0.22/fix-decomp-1q-1c-84f369f9a897a5b7.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed a bug where decomposing an instruction with one qubit and one classical bit - containing a single quantum gate failed. Now the following decomposes as expected:: - - block = QuantumCircuit(1, 1) - block.h(0) - - circuit = QuantumCircuit(1, 1) - circuit.append(block, [0], [0]) - - decomposed = circuit.decompose() - -.. releasenotes/notes/0.22/fix-empty-string-pauli-list-init-4d978fb0eaf1bc70.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed initialization of empty symplectic matrix in :meth:`~.PauliList.from_symplectic` in :class:`~.PauliList` class - For example:: - - from qiskit.quantum_info.operators import PauliList - - x = np.array([], dtype=bool).reshape((1,0)) - z = np.array([], dtype=bool).reshape((1,0)) - pauli_list = PauliList.from_symplectic(x, z) - -.. releasenotes/notes/0.22/fix-flipping-cz-gate-fd08305ca12d9a79.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fix a problem in the :class:`~.GateDirection` transpiler pass for the - :class:`~.CZGate`. The CZ gate is symmetric, so flipping the qubit - arguments is allowed to match the directed coupling map. - -.. releasenotes/notes/0.22/fix-gradient-wrapper-2f9ab45941739044.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed issues with the :func:`.DerivativeBase.gradient_wrapper` method - when reusing a circuit sampler between the calls and binding nested - parameters. - -.. releasenotes/notes/0.22/fix-idle-wires-display-de0ecc60d4000ca0.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue in the ``mpl`` and ``latex`` circuit drawers, when - setting the ``idle_wires`` option to False when there was - a ``barrier`` in the circuit would cause the drawers to - fail, has been fixed. - Fixed `#8313 `__ - -.. releasenotes/notes/0.22/fix-latex-split-filesystem-0c38a1ade2f36e85.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue in :func:`~.circuit_drawer` and :meth:`.QuantumCircuit.draw` - with the ``latex`` method where an ``OSError`` would be raised on systems - whose temporary directories (*e.g* ``/tmp``) are on a different - filesystem than the working directory. - Fixes `#8542 `__ - -.. releasenotes/notes/0.22/fix-nested-flow-controllers-a2a5f03eed482fa2.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Nesting a :class:`.FlowController` inside another in a :class:`.PassManager` - could previously cause some transpiler passes to become "forgotten" during - transpilation, if the passes returned a new :class:`.DAGCircuit` rather than - mutating their input. Nested :class:`.FlowController`\ s will now affect - the transpilation correctly. - -.. releasenotes/notes/0.22/fix-nondeterministic-dagcircuit-eq-7caa9041093c6e4c.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Comparing :class:`.QuantumCircuit` and :class:`.DAGCircuit`\ s for equality - was previously non-deterministic if the circuits contained more than one - register of the same type (*e.g.* two or more :class:`.QuantumRegister`\ s), - sometimes returning ``False`` even if the registers were identical. It will - now correctly compare circuits with multiple registers. - -.. releasenotes/notes/0.22/fix-qasm2-identity-as-unitary-aa2feeb05707a597.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now correctly - define the qubit parameters for :class:`~.library.UnitaryGate` operations that do - not affect all the qubits they are defined over. - Fixed `#8224 `__. - -.. releasenotes/notes/0.22/fix-text-drawer-compression-a80a5636957e8eec.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- There were two bugs in the ``text`` circuit drawer that were fixed. - These appeared when ``vertical_compression`` was set to ``medium``, - which is the default. The first would sometimes cause text to overwrite - other text or gates, and the second would sometimes cause the connections - between a gate and its controls to break. - See `#8588 `__. - -.. releasenotes/notes/0.22/fix-unitary-synth-1q-circuit-756ad4ed209a313f.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue with the :class:`~.UnitarySynthesis` pass where a circuit - with 1 qubit gates and a :class:`~.Target` input would sometimes fail - instead of processing the circuit as expected. - -.. releasenotes/notes/0.22/gate-direction-target-a9f0acd0cf30ed66.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The :class:`.GateDirection` transpiler pass will now respect the available - values for gate parameters when handling parametrised gates with a - :class:`.Target`. - -.. releasenotes/notes/0.22/improve-error-message-snobfit-missing-bounds-748943a87e682d82.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue in the :class:`~.SNOBFIT` optimizer class when an - internal error would be raised during the execution of the - :meth:`~.SNOBFIT.minimize` method if no input bounds where specified. - This is now checked at call time to quickly raise a ``ValueError`` if - required bounds are missing from the :meth:`~.SNOBFIT.minimize` call. - Fixes `#8580 `__ - -.. releasenotes/notes/0.22/make-use-of-callback-in-vqd-99e3c85f03181298.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue in the output callable from the - :meth:`~qiskit.algorithms.VQD.get_energy_evaluation` method of - the :class:`~qiskit.algorithms.VQD` class will now correctly call - the specified ``callback`` when run. Previously the callback would - incorrectly not be used in this case. - Fixed `#8575 `__ - -.. releasenotes/notes/0.22/no_warning_with_reverse_bits-b47cb1e357201593.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue when :func:`~circuit_drawer` was used with ``reverse_bits=True`` on a - circuit without classical bits that would cause a potentially confusing warning about - ``cregbundle`` to be emitted. - Fixed `#8690 `__ - -.. releasenotes/notes/0.22/qasm3-fix-conditional-measurement-2d938cad74a9024a.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The OpenQASM 3 exporter (:mod:`qiskit.qasm3`) will now correctly handle - OpenQASM built-ins (such as ``reset`` and ``measure``) that have a classical - condition applied by :meth:`~.InstructionSet.c_if`. Previously the condition - would have been ignored. - -.. releasenotes/notes/0.22/qiskit-nature-797-8f1b0975309b8756.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue with the :class:`~.SPSA` class where internally it was - trying to batch jobs into even sized batches which would raise an - exception if creating even batches was not possible. This has been fixed - so it will always batch jobs successfully even if they're not evenly sized. - -.. releasenotes/notes/0.22/register-add-fix-e29fa2ee47aa6d05.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed the behavior of :meth:`.Layout.add` which was potentially causing the - output of :meth:`~.transpile` to be invalid and contain more Qubits than - what was available on the target backend. Fixed: - `#8667 `__ - -.. releasenotes/notes/0.22/rh1_state_to_latex_fix-e36e47cbdb25033e.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fixed an issue with the :func:`~.visualization.state_visualization.state_to_latex` - function: passing a latex string to the optional ``prefix`` argument of the function - would raise an error. Fixed `#8460 `__ - -.. releasenotes/notes/0.22/state_to_latex_for_none-da834de3811640ce.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- The function :func:`~qiskit.visualization.state_visualization.state_to_latex` produced not valid LaTeX in - presence of close-to-zero values, resulting in errors when :func:`~qiskit.visualization.state_visualization.state_drawer` is called. - Fixed `#8169 `__. - -.. releasenotes/notes/0.22/steppable-optimizers-9d9b48ba78bd58bb.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- :class:`.GradientDescent` will now correctly count the number of iterations, function evaluations and - gradient evaluations. Also the documentation now correctly states that the gradient is approximated - by a forward finite difference method. - -.. releasenotes/notes/0.22/switched-to-StandardScaler-43d24a7918e96c14.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - -- Fix deprecation warnings in :class:`.NaturalGradient`, which now uses the - :class:`~sklearn.preprocessing.StandardScaler` to scale the data - before fitting the model if the ``normalize`` parameter is set to ``True``. - -Aer 0.11.0 -========== - -No change - -IBM Q Provider 0.19.2 -===================== - -No change - - -############# -Qiskit 0.38.0 -############# - -Terra 0.21.2 -============ - -No change - -.. _Release Notes_Aer_0.11.0: - -Aer 0.11.0 -========== - -.. _Release Notes_Aer_0.11.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.11/prepare-0.11-63503170f57ab66d.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -The Qiskit Aer 0.11.0 release highlights are: - -* The migration to a new self-contained Python namespace ``qiskit_aer`` -* The introduction of the :class:`~.AerStatevector` class -* The introduction of Aer implementations of :mod:`~qiskit.primitives`, - :class:`~qiskit_aer.primitives.Sampler` and :class:`~qiskit_aer.primitives.Estimator` -* Introduction of support for running with `cuQuantum `__ - - -.. _Release Notes_Aer_0.11.0_New Features: - -New Features ------------- - -.. releasenotes/notes/0.11/add-backendv2-support-to-noise-model-78fe515040918793.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Added support for :class:`~.BackendV2` to - :meth:`~.NoiseModel.from_backend`. - Now it can generate a :class:`~.NoiseModel` object from an - input :class:`~.BackendV2` instance. When a :class:`~.BackendV2` - input is used on :meth:`~.NoiseModel.from_backend` the two deprecated - options, ``standard_gates`` and ``warnings``, are gracefully ignored. - -.. releasenotes/notes/0.11/add-primitives-65bf67ea8f0c29b1.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Added Aer implementation of :mod:`~qiskit.primitives`, - :class:`~.qiskit_aer.primitives.Sampler` and :class:`~.qiskit_aer.primitives.Estimator. - Thes implementations of the :class:`~qiskit.primitives.BaseSampler` and - :class:`~qiskit.primitives.BaseEstimator` interfaces leverage qiskit aer to - efficiently perform the computation of the primitive operations. You can - refer to the :mod:`qiskit.primitives` docs for a more detailed description - of the primitives API. - -.. releasenotes/notes/0.11/add_aer_runtime_library-6a0efd6a75a510b9.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Added a shared library to Qiskit Aer that allows external programs to use - Aer's simulation methods. This is an experimental feature and its API - may be changed without the deprecation period. - -.. releasenotes/notes/0.11/arm64-macos-wheels-3778e83a8d036168.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Added support for M1 macOS systems. Precompiled binaries for supported - Python versions >=3.8 on arm64 macOS will now be published on PyPI for this - and future releases. - -.. releasenotes/notes/0.11/cuQuantum-support-d33abe5b1cb778a8.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Added support for cuQuantum, NVIDIA's APIs for quantum computing, - to accelerate statevector, density matrix and unitary simulators - by using GPUs. - This is experiemental implementation for cuQuantum Beta 2. (0.1.0) - cuStateVec APIs are enabled to accelerate instead of Aer's implementations - by building Aer by setting path of cuQuantum to ``CUSTATEVEC_ROOT``. - (binary distribution is not available currently.) - cuStateVector is enabled by setting ``device='GPU'`` and - ``cuStateVec_threshold`` options. cuStateVec is enabled when number of - qubits of input circuit is equal or greater than ``cuStateVec_threshold``. - -.. releasenotes/notes/0.11/non-x86_ibm_cpu-493e51313ba222a6.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Added partial support for running on ppc64le and s390x Linux platforms. - This release will start publishing pre-compiled binaries for ppc64le and - s390x Linux platforms on all Python versions. However, unlike other - supported platforms not all of Qiskit's upstream dependencies support these - platforms yet. So a C/C++ compiler may be required to build and install - these dependencies and a simple ``pip install qiskit-aer`` with just a - working Python environment will not be sufficient to install Qiskit Aer. - Additionally, these same constraints prevent us from testing the - pre-compiled wheels before publishing them, so the same guarantees around - platform support that exist for the other platforms don't apply to these - platforms. - -.. releasenotes/notes/0.11/support_initialize_with_label-bc08f29928d3e3f3.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Allow initialization with a label, that consists of ``+-rl``. Now the following - code works: - - .. code-block:: python - - import qiskit - from qiskit_aer import AerSimulator - - qc = qiskit.QuantumCircuit(4) - qc.initialize('+-rl') - qc.save_statevector() - - AerSimulator(method="statevector").run(qc) - - -.. _Release Notes_Aer_0.11.0_Known Issues: - -Known Issues ------------- - -.. releasenotes/notes/0.11/non-x86_ibm_cpu-493e51313ba222a6.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- When running on Linux s390x platforms (or other big endian platforms) - running circuits that contain :class:`~.library.UnitaryGate` operations will not - work because of an endianess bug. - See `#1506 `__ for more - details. - - -.. _Release Notes_Aer_0.11.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.11/MPI-chunk-swap-optimization-8e693483ed271583.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- MPI parallelization for large number of qubits is optimized to apply - multiple chunk-swaps as all-to-all communication that can decrease - data size exchanged over MPI processes. This upgrade improve scalability - of parallelization. - -.. releasenotes/notes/0.11/change_default_fusion_parameters-cec337a003208e06.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Set default ``fusion_max_qubit`` and ``fusion_threshold`` depending on the configured - ``method`` for :class:`~AerSimulator`. Previously, the default values of - ``fusion_max_qubit`` and ``fusion_threshold`` were ``5`` and ``14`` respectively for - all simulation methods. However, their optimal values depend on running methods. If you - depended on the previous defaults you can explicitly set ``fusion_max_qubit=5`` or - ``fusion_threshold=14`` to retain the previous default behavior. For example:: - - from qiskit_aer import AerSimulator - - sim = AerSimulator(method='mps', fusion_max_qubit=5, fusion_threshold=14) - -.. releasenotes/notes/0.11/cuQuantum_22.05.0.41_support-cb0e797b57d20c3a.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- This is update to support cuQuantum 22.5.0.41 including bug fix of - thread safety in some cuStateVec APIs. Now Qiskit Aer turns on - multi-threading for multi-shots and multi-chunk parallelization - when enabling cuStateVec. - -.. releasenotes/notes/0.11/drop-python36-61553302523fa240.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Running qiskit-aer with Python 3.6 is no longer supported. Python >= 3.7 - is now required to install and run qiskit-aer. - -.. releasenotes/notes/0.11/new-namespace-9c3b9fd73ed504e6.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- The ``qiskit-aer`` Python package has moved to be a self-contained - namespace, ``qiskit_aer``. Previously, it shared - a namespace with ``qiskit-terra`` by being ``qiskit.providers.aer``. - `This was problematic for several reasons `__, - and this release moves away from it. For the time being ``import qiskit.providers.aer`` - will continue to work and redirect to ``qiskit_aer`` automatically. Imports from the legacy - ``qiskit.provider.aer`` namespace will emit a ``DeprecationWarning`` in the - future. To avoid any potential issues starting with this release, - updating all imports from ``qiskit.providers.aer`` to ``qiskit_aer`` and - from ``qiskit.Aer`` to ``qiskit_aer.Aer`` is recommended. - -.. releasenotes/notes/0.11/remove_snapsho_operations-a78f13f23c7743b6.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Removed snapshot instructions (such as ``SnapshotStatevector``) which were deprecated since 0.9.0. - Applications that use these instructions need to be modified to use corresponding save - instructions (such as :class:`.SaveStatevector`). - -.. releasenotes/notes/0.11/remove_snapsho_operations-a78f13f23c7743b6.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Removed the ``qiskit_aer.extensions`` module completely. With the removal of - the snapshot instructions, this module has become empty and no longer serves - a purpose. - -.. releasenotes/notes/0.11/terra-version-bump-68eac37136428805.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- The required version of Qiskit Terra has been bumped to 0.20.0. - - -.. _Release Notes_Aer_0.11.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.11/MPI_chunk_fixes-1ea74548cd3c3515.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Fixes for MPI chunk distribution. Including fix for global indexing - for Thrust implementations, fix for cache blocking of non-gate operations. - Also savestatevector returns same statevector to all processes - (only 1st process received statevector previously.) - -.. releasenotes/notes/0.11/allow_multiplexer_without_control_qubits-f5cb8bdbe6302e55.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Handles a multiplexer gate as a unitary gate if it has no control qubits. - Previously, if a multiplexer gate does not have control qubits, quantum state - was not updated. - -.. releasenotes/notes/0.11/delay-pass-units-a31341568057fdb3.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Fixes a bug in :class:`.RelaxationNoisePass` where instruction durations - were always assumed to be in *dt* time units, regardless of the actual - unit of the isntruction. Now unit conversion is correctly handled for - all instruction duration units. - - See `#1453 `__ - for details. - -.. releasenotes/notes/0.11/fix-for-loop-no-parameter-aa5b04b1da0e956b.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Fixed simulation of ``for`` loops where the loop parameter was not used in - the body of the loop. For example, previously this code would fail, but - will now succeed: - - .. code-block:: python - - import qiskit - from qiskit_aer import AerSimulator - - qc = qiskit.QuantumCircuit(2) - with qc.for_loop(range(4)) as i: - qc.h(0) - qc.cx(0, 1) - - AerSimulator(method="statevector").run(qc) - -.. releasenotes/notes/0.11/fix-invalid-t2-error-a3685e4a3ad0a1e7.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Fixes a bug in ``NoiseModel.from_backend()`` that raised an error when - T2 value greater than 2 * T1 was supplied by the backend. - After this fix, it becomes to truncate T2 value up to 2 * T1 and - issue a user warning if truncates. - The bug was introduced at #1391 and, before that, ``NoiseModel.from_backend()`` had - truncated the T2 value up to 2 * T1 silently. - - See `Issue 1464 `__ - for details. - -.. releasenotes/notes/0.11/fix-qerror-assemble-9919a93b210ca776.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Fix performance regression in noisy simulations due to large increase in - serialization overhead for loading noise models from Python into C++ - resulting from unintended nested Python multiprocessing calls. - See `issue 1407 `__ - for details. - -.. releasenotes/notes/0.11/fix-seed-generation-MPI-ee1f0ad44e913d4f.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- This is the fix for Issue #1557. Different seed numbers are generated for - each process if `seed_simulator` option is not set. This fix average seed - set in Circuit for all processes to use the same seed number. - -.. releasenotes/notes/0.11/fix_MPI_distribution-23cdf0d15258816f.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- This is a fix of MPI parallelization for multi-chunk parallelization and - multi-shot distribution over parallel processes. There were missing - distribution configuration that prevents MPI distribution, is now fixed. - -.. releasenotes/notes/0.11/fix_cacheblocking__multi_control_gates-f6a7fca4f3db2f61.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- This is fix for cache blocking transpiler and chunk parallelization for - GPUs or MPI. This fix fixes issue with qubits which has many control or - target qubits (> blocking_qubits). From this fix, only target qubits of - the multi-controlled gate is cache blocked in blocking_qubits. - But it does not support case if number of target qubits is still larger - than blocking_qubits (i.e. large unitary matrix multiplication) - -.. releasenotes/notes/0.11/fix_qerror_to_dict-13a7683ac4adddd4.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Fixes a bug in :meth:`.QuantumError.to_dict` where N-qubit circuit - instructions where the assembled instruction always applied to - qubits ``[0, ..., N-1]`` rather than the instruction qubits. This - bug also affected device and fake backend noise models. - - See `Issue 1415 `__ - for details. - -.. releasenotes/notes/0.11/make_random_seed_reproducible-a7abdfc09ec67bd8.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Because a seed was randomly assigned to each circuit if seed_simulator is not set, - multi-circuit simulation was not reproducible with another multi-circuit simulation. - Users needed to run multiple single-circuit simulation with the seed_simulator which - is randomly assigned in the multi-circuit simulation. This fix allows users to reproduce - multi-circuit simulation with another multi-circuit simulation by setting seed_simulator - of the first circuit in the first multi-circuit simulation. This fix also resolve an - issue reported in https://github.com/Qiskit/qiskit-aer/issues/1511, where simulation - with parameter-binds returns identical results for each circuit instance. - -.. releasenotes/notes/0.11/multi-shots-pauli-noise-improvements-87637a02e81806cf.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- Fix performance issue in multi-shots batched optimization for GPU when - using Pauli noise. This fix allows multi-threading to runtime noise - sampling, and uses nested OpenMP parallelization when using multiple GPUs. - This is fix for - `issue 1473 ` - -.. releasenotes/notes/0.11/support_for_cuQuantum0.40-566391cc42be2341.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - -- This is the fix for cuStateVec support, fix for build error - because of specification change of some APIs of cuStateVec - from cuQuantum version 0.40. - -.. releasenotes/notes/fix_bug_in_tail_while-6a9201d1ad6ba6e8.yaml @ b'44b8fbef5d2c353f880f2de94291c85154c0d687' - -- Fixes an issue when while_loop is the tail of QuantumCircuit. while_loop - is translated to jump and mark instructions. However, if a while_loop is - at the end of a circuit, its mark instruction is truncated wrongly. This - fix corrects the truncation algorithm to always remain mark instructions. - - - - -IBM Q Provider 0.19.2 -===================== - -No change - -############# -Qiskit 0.37.2 -############# - -.. _Release Notes_Terra_0.21.2: - -Terra 0.21.2 -============ - -.. _Release Notes_Terra_0.21.2_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.21.2-71dd32f64f50e853.yaml @ b'fdb62bea1eac6822b96e8dcd2fe19e7aee10027e' - -Qiskit Terra 0.21.2 is a primarily a bugfix release, and also comes with several improved documentation pages. - - -.. _Release Notes_Terra_0.21.2_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/backend_name_fix-175e12b5cf902f99.yaml @ b'fdb62bea1eac6822b96e8dcd2fe19e7aee10027e' - -- ``aer_simulator_statevector_gpu`` will now be recognized correctly as statevector - method in some function when using Qiskit Aer's GPU simulators in :class:`.QuantumInstance` - and other algorithm runners. - -.. releasenotes/notes/bugfix-ucgate-inverse-global_phase-c9655c13c22e5cf4.yaml @ b'fdb62bea1eac6822b96e8dcd2fe19e7aee10027e' - -- Fixed the :meth:`.UCGate.inverse` method which previously did not invert the - global phase. - -.. releasenotes/notes/fix-QuantumCircuit.compose-in-control-flow-scopes-a8aad3b87efbe77c.yaml @ b'5a65e507bb2203b75621bb6204aac852af2f587c' - -- :meth:`.QuantumCircuit.compose` will now function correctly when used with - the ``inplace=True`` argument within control-flow builder contexts. - Previously the instructions would be added outside the control-flow scope. - Fixed `#8433 `__. - -.. releasenotes/notes/fix-paramexpr-isreal-8d20348b4ce6cbe7.yaml @ b'fdb62bea1eac6822b96e8dcd2fe19e7aee10027e' - -- Fixed a bug where a bound :class:`.ParameterExpression` was not identified as real - if ``symengine`` was installed and the bound expression was not a plain ``1j``. - For example:: - - from qiskit.circuit import Parameter - - x = Parameter("x") - expr = 1j * x - bound = expr.bind({x: 2}) - print(bound.is_real()) # used to be True, but is now False - -.. releasenotes/notes/fix-qpy-controlledgate-open-control-35c8ccb4c7466f4c.yaml @ b'5e26264e39cf7deaebf2b03696b1bf2d3fb8117a' - -- Fixed QPY serialisation and deserialisation of :class:`.ControlledGate` - with open controls (*i.e.* those whose ``ctrl_state`` is not all ones). - Fixed `#8549 `__. - -.. releasenotes/notes/support-channels-in--fake-backend-v2-82f0650006495fbe.yaml @ b'66c12f28a31159dab227fdf303306819b4a10909' - -- All fake backends in :mod:`qiskit.providers.fake_provider.backends` have been - updated to return the corresponding pulse channel objects with the method call of - :meth:`~BackendV2.drive_channel`, :meth:`~BackendV2.measure_channel`, - :meth:`~BackendV2.acquire_channel`, :meth:`~BackendV2.control_channel`. - -.. releasenotes/notes/taper-performance-6da355c04da5b648.yaml @ b'fdb62bea1eac6822b96e8dcd2fe19e7aee10027e' - -- Fixed support for running ``Z2Symmetries.taper()`` on larger problems. - Previously, the method would require a large amount of memory which would - typically cause failures for larger problem. As a side effect of this fix - the performance has significantly improved. - - -Aer 0.10.4 -========== - -No change - -IBM Q Provider 0.19.2 -===================== - -No change - -############# -Qiskit 0.37.1 -############# - -.. _Release Notes_Terra_0.21.1: - -Terra 0.21.1 -============ - -.. _Release Notes_Terra_0.21.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/decompose-fix-993f7242eaa69407.yaml @ b'01a7aa6f9f8b8a87e2f149111c8fc78a14e7df8c' - -- Fixed an issue in :meth:`.QuantumCircuit.decompose` method when passing in a list of ``Gate`` classes for the - ``gates_to_decompose`` argument. If any gates in the circuit had a label set this argument wouldn't be handled - correctly and caused the output decomposition to incorrectly skip gates explicitly in the ``gates_to_decompose`` - list. - -.. releasenotes/notes/fix-evolvedop-to-instruction-c90c4f1aa6b4232a.yaml @ b'664747a66e2199a4b20abb9b7180cccb12c61a3f' - -- Fix :meth:`~.EvolvedOp.to_instruction` which previously tried to create a - :class:`~.library.UnitaryGate` without exponentiating the operator to evolve. - Since this operator is generally not unitary, this raised an error (and if - the operator would have been unitary by chance, it would not have been the expected result). - - Now calling :meth:`~.EvolvedOp.to_instruction` correctly produces a gate - that implements the time evolution of the operator it holds:: - - >>> from qiskit.opflow import EvolvedOp, X - >>> op = EvolvedOp(0.5 * X) - >>> op.to_instruction() - Instruction( - name='unitary', num_qubits=1, num_clbits=0, - params=[array([[0.87758256+0.j, 0.-0.47942554j], [0.-0.47942554j, 0.87758256+0.j]])] - ) - -.. releasenotes/notes/fix-numpy-indices-marginal-dist-45889e49ba337d84.yaml @ b'1dd344442355e33777e178932f478c53bbd169b0' - -- Fixed an issue with the :func:`~.marginal_distribution` function: when - a numpy array was passed in for the ``indices`` argument the function would - raise an error. - Fixed `#8283 `__ - -.. releasenotes/notes/fix-opflow-vector-to-circuit-fn-02cb3424269fa733.yaml @ b'd76e23ec0027e6c687f144c812c4401cc1288dcf' - -- Previously it was not possible to adjoint a :class:`.CircuitStateFn` that has been - constructed from a :class:`.VectorStateFn`. That's because the statevector has been - converted to a circuit with the :class:`~qiskit.extensions.Initialize` instruction, which - is not unitary. This problem is now fixed by instead using the :class:`.StatePreparation` - instruction, which can be used since the state is assumed to start out in the all 0 state. - - For example we can now do:: - - from qiskit import QuantumCircuit - from qiskit.opflow import StateFn - - left = StateFn([0, 1]) - left_circuit = left.to_circuit_op().primitive - - right_circuit = QuantumCircuit(1) - right_circuit.x(0) - - overlap = left_circuit.inverse().compose(right_circuit) # this line raised an error before! - -.. releasenotes/notes/fix-optimizer-settings-881585bfa8130cb7.yaml @ b'd54380fa7a078005081b81a10d5d989124a0be40' - -- Fix a bug in the :class:`~.Optimizer` classes where re-constructing a new optimizer instance - from a previously exisiting :attr:`~.Optimizer.settings` reset both the new and previous - optimizer settings to the defaults. This notably led to a bug if :class:`~.Optimizer` objects - were send as input to Qiskit Runtime programs. - - Now optimizer objects are correctly reconstructed:: - - >>> from qiskit.algorithms.optimizers import COBYLA - >>> original = COBYLA(maxiter=1) - >>> reconstructed = COBYLA(**original.settings) - >>> reconstructed._options["maxiter"] - 1 # used to be 1000! - -.. releasenotes/notes/fix-pulse-limit_amplitude-72b8b501710fe3aa.yaml @ b'01a7aa6f9f8b8a87e2f149111c8fc78a14e7df8c' - -- Fixed an issue where the ``limit_amplitude`` argument on an individual - :class:`~.SymbolicPulse` or :class:`~.Waveform` instance - was not properly reflected by parameter validation. In addition, QPY - schedule :func:`~qiskit.qpy.dump` has been fixed to correctly - store the ``limit_amplitude`` value tied to the instance, rather than - saving the global class variable. - -.. releasenotes/notes/fix-zzmap-pairwise-5653395849fec454.yaml @ b'01a7aa6f9f8b8a87e2f149111c8fc78a14e7df8c' - -- Fix the pairwise entanglement structure for :class:`~.NLocal` circuits. - This led to a bug in the :class:`~.ZZFeatureMap`, where using - ``entanglement="pairwise"`` raised an error. Now it correctly produces the - desired feature map:: - - from qiskit.circuit.library import ZZFeatureMap - encoding = ZZFeatureMap(4, entanglement="pairwise", reps=1) - print(encoding.decompose().draw()) - - The above prints: - - .. parsed-literal:: - - ┌───┐┌─────────────┐ - q_0: ┤ H ├┤ P(2.0*x[0]) ├──■────────────────────────────────────■──────────────────────────────────────────── - ├───┤├─────────────┤┌─┴─┐┌──────────────────────────────┐┌─┴─┐ - q_1: ┤ H ├┤ P(2.0*x[1]) ├┤ X ├┤ P(2.0*(π - x[0])*(π - x[1])) ├┤ X ├──■────────────────────────────────────■── - ├───┤├─────────────┤└───┘└──────────────────────────────┘└───┘┌─┴─┐┌──────────────────────────────┐┌─┴─┐ - q_2: ┤ H ├┤ P(2.0*x[2]) ├──■────────────────────────────────────■──┤ X ├┤ P(2.0*(π - x[1])*(π - x[2])) ├┤ X ├ - ├───┤├─────────────┤┌─┴─┐┌──────────────────────────────┐┌─┴─┐└───┘└──────────────────────────────┘└───┘ - q_3: ┤ H ├┤ P(2.0*x[3]) ├┤ X ├┤ P(2.0*(π - x[2])*(π - x[3])) ├┤ X ├────────────────────────────────────────── - └───┘└─────────────┘└───┘└──────────────────────────────┘└───┘ - -.. releasenotes/notes/global-phase-ucgate-cd61355e314a3e64.yaml @ b'01a7aa6f9f8b8a87e2f149111c8fc78a14e7df8c' - -- Fixed an issue in handling the global phase of the :class:`~.UCGate` class. - -Aer 0.10.4 -========== - -No change - -IBM Q Provider 0.19.2 -===================== - -No change - - -############# -Qiskit 0.37.0 -############# - -This release officially marks the end of support for the Qiskit Ignis project -from Qiskit. It was originally deprecated in the 0.33.0 release and as was -documented in that release the ``qiskit-ignis`` package has been removed from -the Qiskit metapackage, which means in that future release -``pip install qiskit`` will no longer include ``qiskit-ignis``. However, note -because of limitations in python packaging we cannot automatically remove a -pre-existing install of ``qiskit-ignis``. If you are upgrading from a previous -version it's recommended that you manually uninstall Qiskit Ignis with -``pip uninstall qiskit-ignis`` or install the metapackage -in a fresh python environment. - -Qiskit Ignis has been supersceded by the `Qiskit Experiments `__ -project. You can refer to the `migration guide `__ -for details on how to switch from Qiskit Ignis to Qiskit Experiments. - -Terra 0.21.0 -============ - -.. _Release Notes_0.21.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.21/release-0.21.0-4a6c079c6301bde6.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -The Qiskit 0.21.0 release highlights are: - - * Support for serialization of a pulse :class:`~.ScheduleBlock` via - :mod:`qiskit.qpy`. The :ref:`qpy_format` has been updated to version 5 - which includes a definition for including the pulse schedules. - To support this, a new :class:`~.SymbolicPulse` class was introduced to - enable defining parametric pulse waveforms via symbolic expressions. - * Improvements to working with preset pass managers. A new function - :func:`~.generate_preset_pass_manager` enables easily generating - a pass manager equivalent to what :func:`~.transpile` will use internally. - Additionally, preset pass managers are now instances of - :class:`~.StagedPassManager` which makes it easier to modify sections. - * A refactor of the internal data structure of the - :attr:`.QuantumCircuit.data` attribute. It previously was a list of - tuples in the form ``(instruction, qubits, clbits)`` and now is a list of - :class:`~.CircuitInstruction` objects. The :class:`~.CircuitInstruction` - objects is backwards compatible with the previous tuple based access, - however with runtime overhead cost. - -Additionally, the transpiler has been improved to enable better quality -outputs. This includes the introduction of new passes such as -:class:`~.VF2PostLayout` and :class:`~.ToqmSwap`. - -New Features ------------- - -.. releasenotes/notes/0.21/add-full-passmanager-5a377f1b71480f72.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new class, :class:`qiskit.transpiler.StagedPassManager`, which is - a :class:`~qiskit.transpiler.PassManager` subclass that has a pipeline - with defined phases to perform circuit compilation. Each phase is a - :class:`~qiskit.transpiler.PassManager` object that will get executed - in a fixed order. For example:: - - from qiskit.transpiler.passes import * - from qiskit.transpiler import PassManager, StagedPassManager - - basis_gates = ['rx', 'ry', 'rxx'] - init = PassManager([UnitarySynthesis(basis_gates, min_qubits=3), Unroll3qOrMore()]) - translate = PassManager([Collect2qBlocks(), - ConsolidateBlocks(basis_gates=basis_gates), - UnitarySynthesis(basis_gates)]) - - staged_pm = StagedPassManager(stages=['init', 'translation'], init=init, translation=translate) - -.. releasenotes/notes/0.21/add-group-commuting-method-in-PauliList-and-SparsePauliOp-5dec2877c4a97861.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added the methods :meth:`.PauliList.group_commuting` and :meth:`.SparsePauliOp.group_commuting`, - which partition these operators into sublists where each element commutes with all the others. - For example:: - - from qiskit.quantum_info import PauliList, SparsePauliOp - - groups = PauliList(["XX", "YY", "IZ", "ZZ"]).group_commuting() - # 'groups' is [PauliList(['IZ', 'ZZ']), PauliList(['XX', 'YY'])] - - op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ", 2j), ("ZZ", 1j)]) - groups = op.group_commuting() - # 'groups' is [ - # SparsePauliOp(['IZ', 'ZZ'], coeffs=[0.+2.j, 0.+1.j]), - # SparsePauliOp(['XX', 'YY'], coeffs=[2.+0.j, 1.+0.j]), - # ] - -.. releasenotes/notes/0.21/add-marginal-distribution-21060de506ed9cfc.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new function, :func:`~.marginal_distribution`, which is used to - marginalize an input dictionary of bitstrings to an integer (such as - :class:`~.Counts`). This is similar in functionality to the existing - :func:`~.marginal_counts` function with three key differences. The first - is that :func:`~.marginal_counts` works with either a counts dictionary - or a :class:`~.Results` object while :func:`~.marginal_distribution` only - works with a dictionary. The second is that :func:`~.marginal_counts` does - not respect the order of indices in its ``indices`` argument while - :func:`~.marginal_distribution` does and will permute the output bits - based on the ``indices`` order. The third difference is that - :func:`~.marginal_distribution` should be faster as its implementation - is written in Rust and streamlined for just marginalizing a dictionary - input. - -.. releasenotes/notes/0.21/add-overloading@-3fedb7bc2fd4d7f7.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added the ``@`` (``__matmul__``) binary operator to ``BaseOperator`` subclasses - in the :mod:`qiskit.quantum_info` module. This is shorthand to call the - classes' ``dot`` method (``A @ B == A.dot(B)``). - -.. releasenotes/notes/0.21/add-parameters-to-decompose-5a541d1b5afe2c68.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new optional argument, ``reps``, to - :meth:`.QuantumCircuit.decompose`, which allows - repeated decomposition of the circuit. For example:: - - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(1) - circuit.ry(0.5, 0) - - # Equivalent to circuit.decompose().decompose() - circuit.decompose(reps=2) - - # decompose 2 times, but only RY gate 2 times and R gate 1 times - circuit.decompose(gates_to_decompose=['ry','r'], reps=2) - -.. releasenotes/notes/0.21/add-serializable-parametric-pulse-31490c4d2cc49ec6.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new pulse base class :class:`.SymbolicPulse`. This is a - replacement of the conventional :class:`.ParametricPulse`, which will be deprecated. - In the new base class, pulse-envelope and parameter-validation functions are - represented by symbolic-expression objects. - The new class provides self-contained and portable pulse data since these symbolic equations - can be easily serialized through symbolic computation libraries. - -.. releasenotes/notes/0.21/add-support-non-hermitian-op-aerpauliexpectation-653d8e16de4eca07.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added support for non-Hermitian operators in :class:`.AerPauliExpectation`. - This allows the use of Aer's fast snapshot expectation computations in - algorithms such as :class:`~qiskit.algorithms.QEOM`. - -.. releasenotes/notes/0.21/add-textbook-circuit-style-98600038608c8f75.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new circuit drawing style, ``textbook``, which uses the color - scheme of the Qiskit Textbook. - -.. releasenotes/notes/0.21/cleanup-timeline-drawer-a6287bdab4459e6e.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- A new attribute :attr:`.QuantumCircuit.op_start_times` - is populated when one of scheduling analysis passes is run on the circuit. - It can be used to obtain circuit instruction with instruction time, for example:: - - from qiskit import QuantumCircuit, transpile - from qiskit.providers.fake_provider import FakeMontreal - - backend = FakeMontreal() - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - - qct = transpile( - qc, backend, initial_layout=[0, 1], coupling_map=[[0, 1]], scheduling_method="alap" - ) - scheduled_insts = list(zip(qct.op_start_times, qct.data)) - -.. releasenotes/notes/0.21/clear-circuit-b8edd4126f47d75a.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new method :meth:`.QuantumCircuit.clear` which is used to remove all instructions - from a :class:`.QuantumCircuit`. - -.. releasenotes/notes/0.21/clear-circuit-b8edd4126f47d75a.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new method :meth:`.QuantumCircuit.copy_empty_like` which is used to get a cleared copy of a - :class:`~.QuantumCircuit` instance. This is logically equivalent to ``qc.copy().clear()``, but - significantly faster and more memory-efficient. This is useful when one needs a new empty - circuit with all the same resources (qubits, classical bits, metadata, and so on) already - added. - -.. releasenotes/notes/0.21/expand-instruction-supported-c3c9a02b2faa9785.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The :meth:`.Target.instruction_supported` method now supports two new - keyword arguments, ``operation_class`` and ``parameters``. Using these - arguments the :meth:`~.Target.instruction_supported` method can now be used - for checking that a specific operation with parameter values are supported - by a :class:`~.Target` object. For example, if you want to check if a - :class:`~.Target` named ``target`` supports running a :class:`~.RXGate` - with :math:`\theta = \frac{\pi}{2}` you would do something like:: - - from math import pi - from qiskit.circuit.library import RXGate - - target.instruction_supported(operation_class=RXGate, parameters=[pi/2]) - - which will return ``True`` if ``target`` supports running :class:`~.RXGate` - with :math:`\theta = \frac{\pi}{2}` and ``False`` if it does not. - -.. releasenotes/notes/0.21/feature-trotter-qrte-f7b28c4fd4b361d2.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a Trotterization-based quantum real-time evolution algorithm - :class:`qiskit.algorithms.TrotterQRTE`. It is compliant with the new quantum time evolution - framework and makes use of the :class:`.ProductFormula` and - :class:`.PauliEvolutionGate` implementations. - - .. code-block:: python - - from qiskit.algorithms import EvolutionProblem - from qiskit.algorithms.evolvers.trotterization import TrotterQRTE - from qiskit.opflow import X, Z, StateFn, SummedOp - - operator = SummedOp([X, Z]) - initial_state = StateFn([1, 0]) - time = 1 - evolution_problem = EvolutionProblem(operator, time, initial_state) - - trotter_qrte = TrotterQRTE() - evolution_result = trotter_qrte.evolve(evolution_problem) - evolved_state_circuit = evolution_result.evolved_state - -.. releasenotes/notes/0.21/generate_pass_manager_preset-1e6c9641accd5d60.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new function :func:`~.generate_preset_pass_manager` which can - be used to quickly generate a preset :class:`~.PassManager` object that mirrors the - :class:`~.PassManager` used internally by the :func:`~.transpile` function. For example:: - - from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager - from qiskit.providers.fake_provider import FakeWashingtonV2 - - # Generate an optimization level 3 pass manager targeting FakeWashingtonV2 - pass_manager = generate_preset_pass_manager(3, FakeWashingtonV2()) - -.. releasenotes/notes/0.21/marginal-memory-29d9d6586ae78590.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new function :func:`~.marginal_memory` which is used to marginalize - shot memory arrays. Provided with the shot memory array and the indices - of interest, the function will return a maginized shot memory array. This - function differs from the memory support in the :func:`~.marginal_counts` - method which only works on the ``memory`` field in a :class:`~.Results` - object. - -.. releasenotes/notes/0.21/primitive-interface-408b91ed338a5bc4.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The primitives interface has been extended to accept objects in addition to indices - as arguments to the ``__call__`` method. The ``parameter_values`` argument can now be optional. - -.. releasenotes/notes/0.21/qasm3-exporter-delay-ef3003e01412c97e.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The OpenQASM 3 exporter (:mod:`qiskit.qasm3`) now supports exporting circuits - with explicit delays, such as from :meth:`.QuantumCircuit.delay`. - -.. releasenotes/notes/0.21/qiskit-toqm-41bd0f3b6760df6f.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new layout and routing method to :func:`.transpile` based on the paper - `"Time-optimal qubit mapping" `__. - To use it, the optional package - `Qiskit TOQM `__ must be - installed. The ``routing_method`` kwarg of - :func:`~qiskit.compiler.transpile` supports an additional value, ``'toqm'`` - which is used to enable layout and routing via TOQM. - - To install ``qiskit-toqm`` along with Terra, run: - - .. code-block:: - - pip install qiskit-terra[toqm] - -.. releasenotes/notes/0.21/quantum_shannon_decomp-facaa362a3ca8ba3.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new module ``qiskit.quantum_info.synthesis.qsd`` to apply Quantum - Shannon Decomposition of arbitrary unitaries. This functionality replaces - the previous isometry-based approach in the default unitary synthesis - transpiler pass as well as when adding unitaries to a circuit using a - :class:`~.library.UnitaryGate`. - - The Quantum Shannon Decomposition uses about half the cnot gates as the - isometry implementation when decomposing unitary matrices of greater than - two qubits. - -.. releasenotes/notes/0.21/scaler-multiplication-left-side-7bea0d73f9afabe2.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Classes in the :mod:`.quantum_info` module that support scalar multiplication - can now be multiplied by a scalar from either the left or the right. - Previously, they would only accept scalar multipliers from the left. - -.. releasenotes/notes/0.21/speedup-lookahead-swap-4dd162fee2d25d10.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The transpiler pass :class:`.LookaheadSwap` (used by :func:`.transpile` when - ``routing_method="lookahead"``) has seen some performance improvements and - will now be approximately three times as fast. This is purely being more - efficient in its calculations, and does not change the complexity of the - algorithm. In most cases, a more modern routing algorithm like - :class:`.SabreSwap` (``routing_method="sabre"``) will be vastly more - performant. - -.. releasenotes/notes/0.21/swap-strategies-3ab013ca60f02b36.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- New transpiler passes have been added. The transpiler pass :class:`.Commuting2qGateRouter` - uses swap strategies to route a block of commuting gates to the coupling map. Indeed, routing - is a hard problem but is significantly easier when the gates commute as in CZ networks. - Blocks of commuting gates are also typically found in QAOA. Such cases can be dealt with - using swap strategies that apply a predefined set of layers of SWAP gates. Furthermore, the new - transpiler pass :class:`.FindCommutingPauliEvolutions` identifies blocks of Pauli evolutions - made of commuting two-qubit terms. Here, a swap strategy is specified by the class - :class:`.SwapStrategy`. Swap strategies need to be tailored to the coupling map and, ideally, - the circuit for the best results. - -.. releasenotes/notes/0.21/umda-optimizer-9ddcda3d25cd8d9a.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Introduced a new optimizer to Qiskit library, which adds support to the - optimization of parameters of variational quantum algorithms. This is - the Univariate Marginal Distribution Algorithm (UMDA), which is a specific - type of the Estimation of Distribution Algorithms. For example:: - - from qiskit.opflow import X, Z, I - from qiskit import Aer - from qiskit.algorithms.optimizers import UMDA - from qiskit.algorithms import QAOA - from qiskit.utils import QuantumInstance - - H2_op = (-1.052373245772859 * I ^ I) + \ - (0.39793742484318045 * I ^ Z) + \ - (-0.39793742484318045 * Z ^ I) + \ - (-0.01128010425623538 * Z ^ Z) + \ - (0.18093119978423156 * X ^ X) - - p = 2 # Toy example: 2 layers with 2 parameters in each layer: 4 variables - - opt = UMDA(maxiter=100, size_gen=20) - backend = Aer.get_backend('statevector_simulator') - vqe = QAOA(opt, - quantum_instance=QuantumInstance(backend=backend), - reps=p) - - result = vqe.compute_minimum_eigenvalue(operator=H2_op) - -.. releasenotes/notes/0.21/unroll3q-target-bf57cc4365808862.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The constructor for the :class:`~.Unroll3qOrMore` transpiler pass has - two new optional keyword arguments, ``target`` and ``basis_gates``. These - options enable you to specify the :class:`~.Target` or supported basis - gates respectively to describe the target backend. If any of the operations - in the circuit are in the ``target`` or ``basis_gates`` those will not - be unrolled by the pass as the target device has native support for the - operation. - -.. releasenotes/notes/0.21/upgrade-qpy-schedule-f28f6a48a3abb4de.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- QPY serialization has been upgraded to support :class:`.ScheduleBlock`. - Now you can save pulse program in binary and load it at later time:: - - from qiskit import pulse, qpy - - with pulse.build() as schedule: - pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0)) - - with open('schedule.qpy', 'wb') as fd: - qpy.dump(schedule, fd) - - with open('schedule.qpy', 'rb') as fd: - new_schedule = qpy.load(fd)[0] - - This uses the QPY interface common to :class:`.QuantumCircuit`. - See :ref:`qpy_schedule_block` for details of data structure. - -.. releasenotes/notes/0.21/vf2-post-layout-f0213e2c7ebb645c.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Added a new transpiler pass, :class:`~.VF2PostLayout`. This pass is of a - new type to perform a new phase/function in the compilation pipeline, - post-layout or post optimization qubit selection. The idea behind this - pass is after we finish the optimization loop in transpiler we - know what the final gate counts will be on each qubit in the circuit so - we can potentially find a better-performing subset of qubits on a backend - to execute the circuit. The pass will search for an isomorphic subgraph in - the connectivity graph of the target backend and look at the full error - rate of the complete circuit on any subgraph found and return the - layout found with the lowest error rate for the circuit. - - This pass is similar to the :class:`~.VF2Layout` pass and both internally - use the same VF2 implementation from - `retworkx `__. However, - :class:`~.VF2PostLayout` is deisgned to run after initial layout, routing, - basis translation, and any optimization passes run and will only work if - a layout has already been applied, the circuit has been routed, and all - gates are in the target basis. This is required so that when a new layout - is applied the circuit can still be run on the target device. :class:`~.VF2Layout` - on the other hand is designed to find a perfect initial layout and can - work with any circuit. - -.. releasenotes/notes/0.21/vf2-post-layout-f0213e2c7ebb645c.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The :class:`~.ApplyLayout` transpiler pass now has support for updating - a layout on a circuit after a layout has been applied once before. If - the ``post_layout`` field is present (in addition to the required - ``layout`` field) the ``property_set`` when the :class:`~.ApplyLayout` pass - is run the pass will update the layout to apply the new layout. This will - return a :class:`~.DAGCircuit` with the qubits in the new physical order - and the ``layout`` property set will be updated so that it maps the - virtual qubits from the original layout to the physical qubits in the new - ``post_layout`` field. - -.. releasenotes/notes/0.21/vf2-post-layout-f0213e2c7ebb645c.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The preset pass managers generated by :func:`~.level_1_pass_manager`, - :func:`~.level_2_pass_manager`, and :func:`~.level_3_pass_manager` which - correspond to ``optimization_level`` 1, 2, and 3 respectively on the - :func:`~.transpile` function now run the :class:`~.VF2PostLayout` pass - after running the routing pass. This enables the transpiler to - potentially find a different set of physical qubits on the target backend - to run the circuit on which have lower error rates. The - :class:`~.VF2PostLayout` pass will not be run if you manually specify a - ``layout_method``, ``routing_method``, or ``initial_layout`` arguments - to :func:`~.transpile`. If the pass can find a better performing subset of - qubits on backend to run the physical circuit it will adjust the layout of - the circuit to use the alternative qubits instead. - -.. releasenotes/notes/0.21/vqd-implementation-details-09b0ead8b42cacda.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The algorithm iteratively computes each eigenstate by starting from the ground - state (which is computed as in VQE) and then optimising a modified cost function - that tries to compute eigen states that are orthogonal to the states computed in - the previous iterations and have the lowest energy when computed over the ansatz. - The interface implemented is very similar to that of VQE and is of the form: - - .. code-block:: python - - from qiskit.algorithms import VQD - from qiskit.utils import QuantumInstance - from qiskit.circuit.library import TwoLocal - from qiskit.algorithms.optimizers import COBYLA - from qiskit import BasicAer - from qiskit.opflow import I,Z,X - - h2_op = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - - vqd = VQD(k =2, ansatz = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz"),optimizer = COBYLA(maxiter = 0), quantum_instance = QuantumInstance( - BasicAer.get_backend("qasm_simulator"), shots = 2048) - ) - vqd_res = vqd.compute_eigenvalues(op) - - This particular code snippet generates 2 eigenvalues (ground and 1st excited state) - Tests have also been implemented. - - -.. _Release Notes_0.21.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.21/add-serializable-parametric-pulse-31490c4d2cc49ec6.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The pulse classes in :mod:`qiskit.pulse.library` are now subclasses of - :class:`.SymbolicPulse` rather than :class:`.ParametricPulse`. The available - classes remain unchanged as - :class:`~qiskit.pulse.library.Gaussian`, - :class:`~qiskit.pulse.library.GaussianSquare`, - :class:`~qiskit.pulse.library.Drag`, and - :class:`~qiskit.pulse.library.Constant`. - :class:`.SymbolicPulse` has full backward compatibility, and there should be - no loss of functionality. - -.. releasenotes/notes/0.21/change-instruction-data-scalar-81f2066ca2435933.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The data type of each element in :attr:`.QuantumCircuit.data` has changed. - It used to be a simple 3-tuple of an :class:`~.circuit.Instruction`, a list - of :class:`~.circuit.Qubit`\ s, and a list of :class:`.Clbit`\ s, whereas it is now - an instance of :class:`.CircuitInstruction`. - - The attributes of this new class are :attr:`~.CircuitInstruction.operation`, - :attr:`~.CircuitInstruction.qubits` and :attr:`~.CircuitInstruction.clbits`, - corresponding to the elements of the previous tuple. However, - :attr:`~.CircuitInstruction.qubits` and :attr:`~.CircuitInstruction.clbits` - are now ``tuple`` instances, not ``list``\ s. - - This new class will behave exactly like the old 3-tuple if one attempts to - access its index its elements, or iterate through it. This includes casting - the :attr:`~.CircuitInstruction.qubits` and :attr:`~.CircuitInstruction.clbits` - elements to lists. This is to assist backwards compatibility. Starting from - Qiskit Terra 0.21, this is no longer the preferred way to access these elements. - Instead, you should use the attribute-access form described above. - - This has been done to allow further developments of the :class:`.QuantumCircuit` - data structure in Terra, without constantly breaking backwards compatibility. - Planned developments include dynamic parameterized circuits, and an overall - reduction in memory usage of deep circuits. - -.. releasenotes/notes/0.21/constraint-optional-b6a2b2ee21211ccd.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The ``python-constraint`` dependency, which is used solely by the - :class:`~.CSPLayout` transpiler pass, is no longer in the requirements list - for the Qiskit Terra package. This is because the :class:`~.CSPLayout` pass - is no longer used by default in any of the preset pass managers for - :func:`~.transpile`. While the pass is still available, if you're using it - you will need to manually install ``python-contraint`` or when you - install ``qiskit-terra`` you can use the ``csp-layout`` extra, for example:: - - pip install "qiskit-terra[csp-layout]" - -.. releasenotes/notes/0.21/fix-qpy-controlled-gates-e653cbeee067f90b.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The QPY version format version emitted by :func:`.qpy.dump` has been - increased to version 5. This new format version is incompatible with the - previous versions and will result in an error when trying to load it with - a deserializer that isn't able to handle QPY version 5. This change was - necessary to fix support for representing controlled gates properly and - representing non-default control states. - -.. releasenotes/notes/0.21/msrv-0b626e1cfb415abf.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Qiskit Terra's compiled Rust extensions now have a minimum supported Rust - version (MSRV) of 1.56.1. This means when building Qiskit Terra from source - the oldest version of the Rust compiler supported is 1.56.1. If you are using - an older version of the Rust compiler you will need to update to a newer - version to continue to build Qiskit from source. This change was necessary - as a number of upstream dependencies have updated their minimum supported - versions too. - -.. releasenotes/notes/0.21/parallelize-circuit-scheduling-972d4616eabb2ccb.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Circuit scheduling now executes in parallel when more than one - circuit is provided to :func:`~.compiler.schedule`. Refer to - `#2695 `__ - for more details. - -.. releasenotes/notes/0.21/remove-basebackend-7beac0abd17144fe.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The previously deprecated ``BaseBackend``, ``BaseJob``, and ``BaseProvider`` - classes have all been removed. They were originally deprecated in the - 0.18.0 release. Instead of these classes you should be using the versioned - providers interface classes, the latest being :class:`~.BackendV2`, - :class:`~.JobV1`, and :class:`~.ProviderV1`. - -.. releasenotes/notes/0.21/remove-basebackend-7beac0abd17144fe.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The previously deprecated ``backend`` argument for the constructor of the - :class:`~.RZXCalibrationBuilder` transpiler pass has been removed. It was - originally deprecated in the 0.19.0 release. Instead you should query - the :class:`~.Backend` object for the ``instruction_schedule_map`` and - ``qubit_channel_mapping`` and pass that directly to the constructor. For - example, with a :class:`~.BackendV1` backend:: - - from qiskit.transpiler.passes import RZXCalibrationBuilder - from qiskit.providers.fake_provider import FakeMumbai - - backend = FakeMumbai() - inst_map = backend.defaults().instruction_schedule_map - channel_map = backend.configuration().qubit_channel_mapping - cal_pass = RZXCalibrationBuilder( - instruction_schedule_map=inst_map, - qubit_channel_mapping=channel_map, - ) - - or with a :class:`~.BackendV2` backend:: - - from qiskit.transpiler.passes import RZXCalibrationBuilder - from qiskit.providers.fake_provider import FakeMumbaiV2 - - backend = FakeMumbaiV2() - inst_map = backend.instruction_schedule_map - channel_map = {bit: backend.drive_channel(bit) for bit in range(backend.num_qubits)} - cal_pass = RZXCalibrationBuilder( - instruction_schedule_map=inst_map, - qubit_channel_mapping=channel_map, - ) - -.. releasenotes/notes/0.21/remove-basicaer-shot-limit-b7cd4e6f6739885c.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The measurement shot limit for the :class:`.BasicAer` backend has been removed. - -.. releasenotes/notes/0.21/remove-dagnode-deprecations-30703a2156d52b8a.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- For the :class:`~DAGNode`, the previously deprecated ``type``, ``op``, - ``qargs``, ``cargs``, and ``wire`` kwargs and attributes have been removed. - These were originally deprecated in the 0.19.0 release. The ``op``, - ``qargs``, and ``cargs`` kwargs and attributes can be accessed only on - instances of :class:`~DAGOpNode`, and the ``wire`` kwarg and attribute are - only on instances of :class:`~DAGInNode` or :class:`~DAGOutNode`. - -.. releasenotes/notes/0.21/remove-deprecate-calsses-methods-7bd69606cc4ad61f.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The deprecated function :func:`.pauli_group` has been removed. - It was originally deprecated in Qiskit Terra 0.17. - -.. releasenotes/notes/0.21/remove-deprecate-calsses-methods-7bd69606cc4ad61f.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Several deprecated methods on :class:`.Pauli` have been removed, which were - originally deprecated in Qiskit Terra 0.17. These were: - - ``sgn_prod`` - Use :meth:`.Pauli.compose` or :meth:`.Pauli.dot` instead. - - ``to_spmatrix`` - Use :meth:`.Pauli.to_matrix` with argument ``sparse=True`` instead. - - ``kron`` - Use :meth:`.Pauli.expand`, but beware that this returns a new object, rather - than mutating the existing one. - - ``update_z`` and ``update_x`` - Set the ``z`` and ``x`` attributes of the object directly. - - ``insert_paulis`` - Use :meth:`.Pauli.insert`. - - ``append_paulis`` - Use :meth:`.Pauli.expand`. - - ``delete_qubits`` - Use :meth:`.Pauli.delete`. - - ``pauli_single`` - Construct the label manually and pass directly to the initializer, such as:: - - Pauli("I" * index + pauli_label + "I" * (num_qubits - index - len(pauli_label))) - - ``random`` - Use :func:`.quantum_info.random_pauli` instead. - -.. releasenotes/notes/0.21/remove-deprecated-optimizer-methods-d580a07112ccaa2d.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Removed the ``optimize`` method from the :class:`~qiskit.algorithms.optimizers.Optimizer` - classes, which is superseded by the :meth:`~.algorithms.optimizers.Optimizer.minimize` method as direct replacement. - The one exception is :class:`~qiskit.algorithms.optimizers.SPSA`, where the - deprecation warning was not triggered so the method there is still kept. - -.. releasenotes/notes/0.21/result-fix-e4eaa021f49b5f99.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- :class:`.Result` was modified so that it always contains ``date``, ``status``, - and ``header`` attributes (set to ``None`` if not specified). - -.. releasenotes/notes/0.21/shared-memory-dependency-1e32e1c55902216f.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- For Python 3.7 `shared-memory38 `__ - is now a dependency. This was added as a dependency for Python 3.7 to enable - leveraging the shared memory constructs in the standard library of newer - versions of Python. If you're running on Python >= 3.8 there is no extra - dependency required. - -.. releasenotes/notes/0.21/type-check-instruction-labels-on-creation-8399dd8b5d72f272.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- :class:`~.circuit.Instruction` labels are now type-checked on instruction creation. - -.. releasenotes/notes/0.21/upgrade-qpy-schedule-f28f6a48a3abb4de.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- QPY serialization has been upgraded to serialize :class:`.QuantumCircuit` - with :attr:`.QuantumCircuit.calibrations`. As of QPY Version 5, only calibration - entries of :class:`.ScheduleBlock` type can be serialized. - -.. releasenotes/notes/0.21/xxplusyy-convention-e181e74271a9e9e1.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The definition of :class:`.XXPlusYYGate` has been changed. - See `#7969 `__ - for details. - -.. releasenotes/notes/0.21/remove-hard-time-limit-vf2-be83830ecc71f72c.yaml @ b'5a8bb42ec02753dce68ea0d28986453d07d071b2' - -- The preset pass managers generated by :func:`~.level_1_pass_manager`, - :func:`~.level_2_pass_manager`, and :func:`~.level_3_pass_manager` and used - by the :func:`~.transpile` function's ``optimization_level`` argument at - 1, 2, and 3 respectively no longer set a hard time limit on the - :class:`~.VF2Layout` transpiler pass. This means that the pass will no - longer stop trying to find a better alternative perfect layout up until a - fixed time limit (100ms for level 1, 10 sec for level 2, and 60 sec for - level 3) as doing this limited the reproducibility of compilation when a - perfect layout was available. This means that the output when using the pass - might be different than before, although in all cases it would only change - if a lower noise set of qubits can be found over the previous output. If - you wish to retain the previous behavior you can create a custom - :class:`~.PassManager` that sets the ``time_limit`` argument on the - constructor for the :class:`~VF2Layout` pass. - - -.. _Release Notes_0.21.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.21/cleanup-timeline-drawer-a6287bdab4459e6e.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Calling :func:`.timeline_drawer` with an unscheduled circuit has been deprecated. - All circuits, even one consisting only of delay instructions, - must be transpiled with the ``scheduling_method`` keyword argument of - :func:`.transpile` set, to generate schedule information being stored in - :attr:`.QuantumCircuit.op_start_times`. - -.. releasenotes/notes/0.21/deprecate-nx-dag-f8a8d947186222c2.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The `NetworkX `__ converter functions for the - :meth:`.DAGCircuit.to_networkx` and - :meth:`~.DAGCircuit.from_networkx`, along with the - :meth:`.DAGDependency.to_networkx` method have been deprecated and will be - removed in a future release. Qiskit has been using - `retworkx `__ as its graph - library since the qiskit-terra 0.12.0 release, and since then the networkx - converter functions have been lossy. They were originally added so - that users could leverage functionality in NetworkX's algorithms library - not present in retworkx. Since that time, retworkx has matured - and offers more functionality, and the :class:`~.DAGCircuit` is tightly - coupled to retworkx for its operation. Having these converter methods - provides limited value moving forward and are therefore going to be - removed in a future release. - -.. releasenotes/notes/0.21/deprecate-old-optional-paths-982466a6336e4794.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Accessing several old toggles (``HAS_MATPLOTLIB``, ``HAS_PDFTOCAIRO``, - ``HAS_PYLATEX`` and ``HAS_PIL``) from the :mod:`qiskit.visualization` module - is now deprecated, and these import paths will be removed in a future - version of Qiskit Terra. The same objects should instead be accessed - through :mod:`qiskit.utils.optionals`, which contains testers for almost all - of Terra's optional dependencies. - -.. releasenotes/notes/0.21/move-qiskit.test.mock-to-qiskit.providers.fake_provider-7f36ff80a05f9a9a.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The ``qiskit.test.mock`` module is now deprecated. The fake backend and fake provider classes - which were previously available in ``qiskit.test.mock`` have been accessible in - :mod:`qiskit.providers.fake_provider` since Terra 0.20.0. This change represents a proper - commitment to support the fake backend classes as part of Qiskit, whereas previously they were - just part of the internal testing suite, and were exposed to users as a side effect. - -.. releasenotes/notes/0.21/primitive-interface-408b91ed338a5bc4.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The arguments' names when calling an :class:`~.primitives.Estimator` or - :class:`~.primitives.Sampler` object as a function are renamed from - ``circuit_indices`` and ``observable_indices`` to ``circuits`` and - ``observables``. - -.. releasenotes/notes/0.21/remove-basebackend-7beac0abd17144fe.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The ``qobj_id`` and ``qobj_header`` keyword arguments for the - :func:`~.execute` function have been deprecated and will be removed in a - future release. Since the removal of the ``BaseBackend`` class these - arguments don't have any effect as no backend supports execution with a - :class:`~.Qobj` object directly and instead only work with - :class:`~.QuantumCircuit` objects directly. - -.. releasenotes/notes/0.21/remove-deprecate-calsses-methods-7bd69606cc4ad61f.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The arguments ``x``, ``z`` and ``label`` to the initializer of - :class:`.Pauli` were documented as deprecated in Qiskit Terra 0.17, but a bug - prevented the expected warning from being shown at runtime. The warning will - now correctly show, and the arguments will be removed in Qiskit Terra 0.23 or - later. A pair of ``x`` and ``z`` should be passed positionally as a single - tuple (``Pauli((z, x))``). A string ``label`` should be passed positionally - in the first argument (``Pauli("XYZ")``). - -.. releasenotes/notes/0.21/remove-deprecated-optimizer-methods-d580a07112ccaa2d.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The :meth:`.SPSA.optimize` method is deprecated in - favor of :meth:`.SPSA.minimize`, which can be used - as direct replacement. Note that this method returns a complete result - object with more information than before available. - -.. releasenotes/notes/0.21/upgrade-qpy-schedule-f28f6a48a3abb4de.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The ``circuits`` argument of :func:`.qpy.dump` has been deprecated and - replaced with ``programs`` since now QPY supports multiple data types other than circuits. - -.. releasenotes/notes/0.21/upgrade-qpy-schedule-f28f6a48a3abb4de.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- :meth:`.AlignmentKind.to_dict` method has been deprecated and will be removed. - - -.. _Release Notes_0.21.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.21/3842-14af3f8d922a7253.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Extra validation was added to :class:`.DiagonalGate` to check the argument has modulus one. - -.. releasenotes/notes/0.21/add_check_from_sparse_list-97f13fde87c7bcb6.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Duplicate qubit indices given to :meth:`.SparsePauliOp.from_sparse_list` will now - correctly raise an error, instead of silently overwriting previous values. - The old behavior can be accessed by passing the new keyword argument ``do_checks=False``. - -.. releasenotes/notes/0.21/cleanup-timeline-drawer-a6287bdab4459e6e.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The :func:`.timeline_drawer` visualization will no longer misalign classical - register slots. - -.. releasenotes/notes/0.21/consistent-validation-for-gaussian-square-pulse-461087a09ff339a4.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Parameter validation for :class:`~.pulse.library.GaussianSquare` is now - consistent before and after construction. - Refer to `#7882 `__ for more details. - -.. releasenotes/notes/0.21/delay-fake-backends-3f68c074e85d531f.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The :class:`~.BackendV2`\ -based fake backends in - the :mod:`qiskit.providers.fake_provider` module, such as - :class:`.FakeMontrealV2`, now support the :class:`~qiskit.circuit.Delay` operation - in their :attr:`~.BackendV2.target` attributes. Previously, :class:`.QuantumCircuit` objects - that contained delays could not be compiled to these backends. - -.. releasenotes/notes/0.21/fix-eigs_bounds-function-in-TridiagonalToeplitz-class-52cfad8f72ae7341.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed a bug in :meth:`.TridiagonalToeplitz.eigs_bounds`, which caused - incorrect eigenvalue bounds to be returned in some cases with negative - eigenvalues. Refer to `#7939 `__ - for more details. - -.. releasenotes/notes/0.21/fix-latex-ket-max-size-f11c3a89215a49e7.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed a bug in which the LaTeX statevector drawer ignored the ``max_size`` - parameter. - -.. releasenotes/notes/0.21/fix-pm-config-backend_v2-ac8b83267105a47d.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed support in the :meth:`.PassManagerConfig.from_backend` constructor - method for building a :class:`~.PassManagerConfig` object from a - :class:`~.BackendV2` instance. Previously this wasn't handled correctly - and would fail when running with a :class:`~.BackendV2` object. - -.. releasenotes/notes/0.21/fix-qpy-controlled-gates-e653cbeee067f90b.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed support for QPY serialization (:func:`.qpy.dump`) and deserialization - (:func:`.qpy.load`) of a :class:`~.QuantumCircuit` object containing custom - :class:`~.ControlledGate` objects. Previously, an exception would be raised - by :func:`.qpy.load` when trying to reconstruct the custom - :class:`~.ControlledGate`. - Fixed `#7999 `__. - -.. releasenotes/notes/0.21/fix-qpy-controlled-gates-e653cbeee067f90b.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed support for QPY serialization (:func:`.qpy.dump`) and deserialization - (:func:`.qpy.load`) of a :class:`~.QuantumCircuit` object containing custom - :class:`~.MCPhaseGate` objects. Previously, an exception would be raised - by :func:`.qpy.load` when trying to reconstruct the :class:`~.MCPhaseGate`. - -.. releasenotes/notes/0.21/fix-qpy-controlled-gates-e653cbeee067f90b.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed support for QPY serialization (:func:`.qpy.dump`) and deserialization - (:func:`.qpy.load`) of a :class:`~.QuantumCircuit` object containing - controlled gates with an open control state. Previously, the open control - state would be lost by the serialization process and the reconstructed - circuit. - -.. releasenotes/notes/0.21/fix-reverse_bits-with-registerless-bits-6d17597b99640fb0.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed :meth:`.QuantumCircuit.reverse_bits` with circuits containing registerless - :class:`~.circuit.Qubit` and :class:`.Clbit`. For example, the following will now work:: - - from qiskit.circuit import QuantumCircuit, Qubit, Clbit - - qc = QuantumCircuit([Qubit(), Clbit()]) - qc.h(0).c_if(qc.clbits[0], 0) - qc.reverse_bits() - -.. releasenotes/notes/0.21/fix-t2-configurablefakebackend-8660ab3d7a57a824.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed the :attr:`.ConfigurableFakeBackend.t2` attribute, - which was previously incorrectly set based on the provided ``t1`` value. - -.. releasenotes/notes/0.21/fix-target-dt-4d306f1e9b07f819.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed an issue with :class:`~.BackendV2`\ -based fake backend classes from the - :mod:`qiskit.providers.fake_provider` module such as :class:`.FakeMontrealV2` where the - value for the :attr:`~.BackendV2.dt` attribute (and the :attr:`.Target.dt` attribute) - were not properly being converted to seconds. This would cause issues when - using these fake backends with scheduling. - -.. releasenotes/notes/0.21/fix_plot_histogram_number-a0a4a023dfad3c70.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed a bug in :func:`~qiskit.visualization.plot_histogram` when the - ``number_to_keep`` argument was smaller that the number of keys. The - following code will no longer throw errors and will be properly aligned:: - - from qiskit.visualization import plot_histogram - data = {'00': 3, '01': 5, '11': 8, '10': 11} - plot_histogram(data, number_to_keep=2) - -.. releasenotes/notes/0.21/param-table-perf-72cd1c40533b3882.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Improved the performance of building and working with parameterized - :class:`~qiskit.circuit.QuantumCircuit` instances with many gates - that share a relatively small number of parameters. - -.. releasenotes/notes/0.21/qasm3-fix-basis-gates-c96a0b357dfdcb47.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The OpenQASM 3 exporter (:mod:`qiskit.qasm3`) will no longer attempt to produce - definitions for non-standard gates in the ``basis_gates`` option. - -.. releasenotes/notes/0.21/remove-deprecated-optimizer-methods-d580a07112ccaa2d.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed the getter of :attr:`.OptimizerResult.nit`, which - previously returned the number of Jacobian evaluations instead of the number of iterations. - -.. releasenotes/notes/0.21/result-fix-e4eaa021f49b5f99.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed a bug in the string representation of :class:`.Result` objects that - caused the attributes to be specified incorrectly. - -.. releasenotes/notes/0.21/shared-memory-dependency-1e32e1c55902216f.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed an issue with :func:`~.transpile` where in some cases providing a - list of basis gate strings with the ``basis_gates`` keyword argument or - implicitly via a :class:`~.Target` input via the ``target`` keyword - argument would not be interpreted correctly and result in a subset of the - listed gates being used for each circuit. - -.. releasenotes/notes/0.21/shared-memory-dependency-1e32e1c55902216f.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- Fixed an issue in the :class:`~.UnitarySynthesis` transpiler pass which - would result in an error when a :class:`~.Target` that didn't have any - qubit restrictions on the operations (e.g. in the case of an ideal - simulator target) was specified with the ``target`` keyword argument for the - constructor. - -.. releasenotes/notes/0.21/fix-marginal_counts-on-pulse-backend.yaml @ b'0f377f7a2cdbd7eaa46e8e2b5de974c8c22b9612' - -- The method :meth:`qiskit.result.marginal_counts`, when passed a :class:`.Result` from a - pulse backend, would fail, because it contains an array of - :class:`.ExperimentResult` objects, each of which have an :class:`QobjExperimentHeader`, and those - :class:`ExperimentHeaders` lack `creg_sizes` instance-variables. If the :class:`Result` came - from a simulator backend (e.g. Aer), that instance-variable would be there. - We fix :class:`marginal_counts` so that it skips logic that needs `creg_sizes` if the - field is not present, or non-None. - -.. releasenotes/notes/fix-qasm2-identity-as-unitary-aa2feeb05707a597.yaml @ b'd7f932c8b242f69c5577afd5593bf36f839657f7' - -- The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now correctly - define the qubit parameters for :class:`~.library.UnitaryGate` operations that do - not affect all the qubits they are defined over. - Fixed `#8224 `__. - -.. releasenotes/notes/0.21/remove-hard-time-limit-vf2-be83830ecc71f72c.yaml @ b'5a8bb42ec02753dce68ea0d28986453d07d071b2' - -- Fixed an issue with reproducibility of the :func:`~.transpile` function - when running with ``optimization_level`` 1, 2, and 3. Previously, under - some conditions when there were multiple perfect layouts (a layout that - doesn't require any SWAP gates) available the selected layout and output - circuit could vary regardless of whether the ``seed_transpiler`` argument - was set. - - -Aer 0.10.4 -========== - -No change - -.. _Release Notes_0.19.2_IBMQ: - -IBM Q Provider 0.19.2 -===================== - -.. _Release Notes_0.19.2_IBMQ_Bug Fixes: - -Bug Fixes ---------- - -- In the upcoming terra release there will be a release candidate tagged - prior to the final release. However changing the version string for the - package is blocked on the qiskit-ibmq-provider right now because it is trying - to parse the version and is assuming there will be no prelease suffix on - the version string (see `#8200 `__ - for the details). PR `#1135 `__ - fixes this version parsing to use the regex from the - pypa/packaging project which handles all the PEP440 package versioning - include pre-release suffixes. This will enable terra to release an - 0.21.0rc1 tag without breaking the qiskit-ibmq-provider. - -- ``threading.currentThread`` and ``notifyAll`` were deprecated in Python 3.10 (October 2021) - and will be removed in Python 3.12 (October 2023). - PR `#1133 `__ replaces them - with ``threading.current_thread``, ``notify_all`` added in Python 2.6 (October 2008). - - -############# -Qiskit 0.36.2 -############# - -.. _Release Notes_Terra_0.20.2: - -Terra 0.20.2 -============ - -.. _Release Notes_Terra_0.20.2_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.20.2-0fb90e19e89fe4ac.yaml @ b'f9327925f6d82c2807e7811c4b16eee0f1076c9f' - -Qiskit Terra 0.20.2 is a bugfix release, addressing some minor issues identified since the last patch release. - - -.. _Release Notes_Terra_0.20.2_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix-fake-backend-v2-dtm-unit-392a8fe3fcc9b793.yaml @ b'f9327925f6d82c2807e7811c4b16eee0f1076c9f' - -- Fixed an issue with :class:`~.BackendV2`\ -based fake backend classes from the - ``qiskit.providers.fake_provider`` module such as ``FakeMontrealV2``, where the - values for the :attr:`~.BackendV2.dtm` and :attr:`~.BackendV2.dt` attributes - and the associated attribute :attr:`.Target.dt` would not be properly - converted to seconds. This would cause issues when using these fake backends - with scheduling. See `#8018 `__. - -.. releasenotes/notes/fix-marginal_counts-zero-memory-0f6710d6923c8ad7.yaml @ b'f9327925f6d82c2807e7811c4b16eee0f1076c9f' - -- :func:`.marginal_counts` will now succeed when asked to marginalize memory - with an ``indices`` parameter containing non-zero elements. Previously, - shots whose hexadecimal result representation was sufficiently small could - raise a ``ValueError``. See `#8044 `__. - -.. releasenotes/notes/fix-qasm3-global-statement-order-ca8bdb35e0fb8dec.yaml @ b'f9327925f6d82c2807e7811c4b16eee0f1076c9f' - -- The OpenQASM 3 exporter (:mod:`qiskit.qasm3`) will now output ``input`` or - ``output`` declarations before gate declarations. This is more consistent - with the current reference ANTLR grammar from the OpenQASM 3 team. - See `#7964 `__. - -.. releasenotes/notes/fix-rzx-builder-pulse-amp-ba5c876ddea17c41.yaml @ b'f9327925f6d82c2807e7811c4b16eee0f1076c9f' - -- Fixed a bug in the :class:`.RZXCalibrationBuilder` transpiler pass where - the scaled cross-resonance pulse amplitude could appear to be parametrized - even after assignment. This could cause the pulse visualization tools to - use the parametrized format instead of the expected numeric one. - See `#8031 `__. - -.. releasenotes/notes/fix-transpile-backendv2-durations-dbc85688564cc271.yaml @ b'f9327925f6d82c2807e7811c4b16eee0f1076c9f' - -- Fixed an issue with the :func:`~.transpile` function when run with a - :class:`~.BackendV2`\ -based backend and setting the ``scheduling_method`` - keyword argument. Previously, the function would not correctly process - the default durations of the instructions supported by the backend which - would lead to an error. - -.. releasenotes/notes/pulse-round-a014390e414c79c8.yaml @ b'f9327925f6d82c2807e7811c4b16eee0f1076c9f' - -- Fixed a bug in the :class:`~.RZXCalibrationBuilder` transpiler pass that was - causing pulses to sometimes be constructed with incorrect durations. - See `#7994 `__. - -.. releasenotes/notes/sabreswap-fix-condition-593f36e855f9064c.yaml @ b'a094757d9c15b0cfd885016d82ec19bc775086cd' - -- The :class:`.SabreSwap` transpiler pass, used in :func:`.transpile` when - ``routing_method="sabre"`` is set, will no longer sporadically drop - classically conditioned gates and their successors from circuits during the - routing phase of transpilation. See - `#8040 `__. - -.. releasenotes/notes/statevector-enable-iter-4652d7ce87f4d459.yaml @ b'8827c554982d779bc1fa5f01f1f09d91c3854a6f' - -- :class:`.Statevector` will now allow direct iteration through its values - (such as ``for coefficient in statevector``) and - correctly report its length under ``len``. Previously it would try and - and access out-of-bounds data and raise a :class:`.QiskitError`. See - `#8039 `__. - -Aer 0.10.4 -========== - -No change - -.. _Release Notes_Ignis_0.7.1: - -Ignis 0.7.1 -=========== - -.. _Release Notes_Ignis_0.7.1_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.7.1-520c1e0dba0521f7.yaml @ b'3176f61a827c9b00ba006cdaad787fca55acc3a1' - -This is a bugfix release that primarily fixes a packaging issue that was -causing the ``docs/`` directory, which contains the source files used to -build the qiskit-ignis documentation, to get included in the Python package. - -IBM Q Provider 0.19.1 -===================== - -No change - -############# -Qiskit 0.36.1 -############# - -Terra 0.20.1 -============ - -.. _Release Notes_Terra_0.20.1_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.20.1-72b215a1ca1f34c8.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -Qiskit Terra 0.20.1 is a bugfix release resolving issues identified in release 0.20.0. - - -.. _Release Notes_Terra_0.20.1_Known Issues: - -Known Issues ------------- - -.. releasenotes/notes/ucr-gates-qpy-b8f6fb1e34fae258.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- QPY deserialization with the :func:`.qpy.load` function of a directly - instantiated :class:`~.library.UCPauliRotGate` object in a circuit will fail - because the rotation axis argument to the class isn't stored in a standard - place. To workaround this you can instead use the subclasses: - :class:`~.library.UCRXGate`, :class:`~.library.UCRYGate`, or :class:`~.library.UCRZGate` (based on - whether you're using a rotation axis of ``"X"``, ``"Y"``, or ``"Z"`` - respectively) which embeds the rotation axis in the class constructor and - will work correctly in QPY. - -.. releasenotes/notes/xxplusyy-doc-c6ddcc45044dcdcd.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Since its original introduction in Qiskit Terra 0.20, :class:`.XXPlusYYGate` - has used a negative angle convention compared to all other rotation gates. - In Qiskit Terra 0.21, this will be corrected to be consistent with the - other rotation gates. This does not affect any other rotation gates, nor - :class:`.XXMinusYYGate`. - - -.. _Release Notes_Terra_0.20.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/clifford_delay-be1a835413e2531e.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Fixed :class:`.Clifford`, :class:`.Pauli` and :class:`.CNOTDihedral` - operator initialization from compatible circuits that contain - :class:`~qiskit.circuit.Delay` instructions. These instructions are - treated as identities when converting to operators. - -.. releasenotes/notes/fix-aux-ops-evaluator-83ce1606d1ad19b3.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Fixed an issue where the :func:`~qiskit.algorithms.eval_observables` function would raise an - error if its ``quantum_state`` argument was of type :class:`~qiskit.opflow.StateFn`. - ``eval_observables`` now correctly supports all input types denoted by its type hints. - -.. releasenotes/notes/fix-dag-drawer-no-reg-6eee9d1f6e4b9261.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Fixed an issue with the visualization function :func:`~.dag_drawer` and - method :meth:`.DAGCircuit.draw` where previously the drawer would fail - when attempting to generate a visualization for a :class:`~.DAGCircuit` - object that contained a :class:`~.circuit.Qubit` or :class:`~.Clbit` which wasn't - part of a :class:`~QuantumRegister` or :class:`~ClassicalRegister`. - Fixed `#7915 `__. - -.. releasenotes/notes/fix-drag-pulse-validation-905f9b6353a0f2d1.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Fixed parameter validation for class :class:`~Drag`. Previously, it was not - sensitive to large beta values with negative signs, which may have resulted in - waveform samples with a maximum value exceeding the amplitude limit of 1.0. - -.. releasenotes/notes/fix-hard-coded-sleep-run-circuits-a1588164e61d5336.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- The :class:`~qiskit.utils.QuantumInstance` class used by many algorithms (like ``VQE``) - was hard-coding the value for a sleep while it looped waiting for the job status to be updated. - It now respects the configured sleep value as set per the ``wait`` attribute in the - initializer of :class:`~qiskit.utils.QuantumInstance`. - -.. releasenotes/notes/fix-list-input-schedule-14fc48895a061735.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Fixed an issue with the :class:`~qiskit.compiler.schedule` function where - callers specifying a ``list`` of :class:`~qiskit.circuit.QuantumCircuit` - objects with a single entry would incorrectly be returned a single - :class:`~.Schedule` object instead of a ``list``. - -.. releasenotes/notes/fix-plot-error-map-f3b4cc754b589d8f.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Fixed an issue with the :class:`~.plot_error_map` visualization function - which prevented it from working when run with a backend that had readout - error defined in the provided backend's :class:`~.BackendProperties` or - when running with a :class:`~.BackendV2` backend. - Fixed `#7879 `__. - -.. releasenotes/notes/fix-primitive-init-observable-pauli-e312c05d1c3bd804.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Fixed a bug that could result in exponential runtime and nontermination when - a :class:`~qiskit.quantum_info.Pauli` instance is given to method - :meth:`~qiskit.primitives.utils.init_observables`. - -.. releasenotes/notes/fix-sabreswap-clbits-428eb5f3a46063da.yaml @ b'35645aaba47e317a5eb36748fd3900aaf4e45597' - -- Fixed :class:`.SabreSwap`, and by extension :func:`.transpile` with - ``optimization_level=3``, occasionally re-ordering measurements invalidly. - Previously, if two measurements wrote to the same classical bit, - :class:`.SabreSwap` could (depending on the coupling map) re-order them to - produce a non-equivalent circuit. This behaviour was stochastic, so may - not have appeared reliably. - Fixed `#7950 `__ - -.. releasenotes/notes/sabreswap-loop-230ef99e61358105.yaml @ b'a75c9a609b77a4807fcafc4c111d99edb434048e' - -- The :class:`.SabreSwap` transpiler pass, and by extension - :class:`.SabreLayout` and :func:`.transpile` at ``optimization_level=3``, - now has an escape mechanism to guarantee that it can never get stuck in an - infinite loop. Certain inputs previously could, with a great amount of bad - luck, get stuck in a stable local minimum of the search space and the pass - would never make further progress. It will now force a series of swaps that - allow the routing to continue if it detects it has not made progress - recently. Fixed `#7707 `__. - -.. releasenotes/notes/ucr-gates-qpy-b8f6fb1e34fae258.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - -- Fixed an issue with QPY deserialization via the :func:`.qpy.load` function - of the :class:`~.library.UCRXGate`, :class:`~.library.UCRYGate`, and :class:`~.library.UCRZGate` - classes. - Previously, a QPY file that contained any of these gates would error - when trying to load the file. - Fixed `#7847 `__. - -Aer 0.10.4 -========== - -No change - -Ignis 0.7.0 -=========== - -No change - -IBM Q Provider 0.19.1 -===================== - -.. _Release Notes_0.19.1_IBMQ: - -0.19.1 -====== - -.. _Release Notes_0.19.1_IBMQ_Bug Fixes: - -Bug Fixes ---------- - -- PR `#1129 `__ updates - :meth:`~qiskit.providers.ibmq.least_busy` method to no longer support `BaseBackend` as a valid - input or output type since it has been long deprecated in qiskit-terra and has recently - been removed. - -############# -Qiskit 0.36.0 -############# - -Terra 0.20.0 -============ - -No change - -.. _Release Notes_Aer_0.10.4: - -Aer 0.10.4 -========== - -.. _Release Notes_Aer_0.10.4_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/no-fast-math-1de357a9650094f3.yaml @ b'4f0cd3db74f922a6a3922d106498bb37d9ae1aaa' - -- Qiskit Aer is no longer compiled with unsafe floating-point optimisations. - While most of the effects should have been localised to Qiskit Aer, some - aspects of subnormal handling may previously have been leaked into user code - by the library incorrectly setting the "flush to zero" mode. This will not - happen any more. - - -.. _Release Notes_Aer_0.10.4_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/density-multi-chunk-fix-e9effc67d0365418.yaml @ b'346ec243d31192eef100663e9a7b90055cb84f6b' - -- Fix cache blocking transpiler to recognize superop to be cache blocked. - This is fix for - `issue 1479 ` - now density_matrix with noise models can be parallelized. - New test, test_noise.TestNoise.test_kraus_gate_noise_on_QFT_cache_blocking - is added to verify this issue. - Also this fix include fix for - `issue 1483 ` - discovered by adding new test case. - This fixes measure over chunks for statevector. - -.. releasenotes/notes/fix-invalid-t2-error-a3685e4a3ad0a1e7.yaml @ b'80478fec494bdf942f056cef704d3df3f6a1ac99' - -- Fixes a bug in ``NoiseModel.from_backend()`` that raised an error when - T2 value greater than 2 * T1 was supplied by the backend. - After this fix, it becomes to truncate T2 value up to 2 * T1 and - issue a user warning if truncates. - The bug was introduced at #1391 and, before that, ``NoiseModel.from_backend()`` had - truncated the T2 value up to 2 * T1 silently. - - See `Issue 1464 `__ - for details. - -.. releasenotes/notes/fix-thrust-cpu-threads-67db86b2edcf06b3.yaml @ b'61e91e2277b72ff6e0feaf85054c06821fb1a6a0' - -- device=Thrust was very slow for small number of qubits because OpenMP - threading was always applied. This fix applies OpenMP threads as same - as device=CPU by using statevector_parallel_threshold. - -.. releasenotes/notes/no-fast-math-1de357a9650094f3.yaml @ b'4f0cd3db74f922a6a3922d106498bb37d9ae1aaa' - -- Qiskit Aer will no longer set the floating-point mode to "flush to zero" - when loaded. Downstream users may previously have seen warnings from Numpy - such as: - - The value of the smallest subnormal for type is zero. - - These will now no longer be emitted, and the floating-point handling will be - correct. - -.. releasenotes/notes/remove_circuit_metadata_from_qobj-324e7ea9b369ee67.yaml @ b'23f7c4b52119ceaa7332f638d6115472c08129d5' - -- Fixed a potential issue with running simulations on circuits that have the - :attr:`.QuantumCircuit.metadata` attribute set. The :attr:`~.QuantumCircuit.metadata` - attribute can be any python dictionary and previously qiskit-aer would attempt to - JSON serialize the contents of the attribute to process it with the rest of the rest - of the circuit input, even if the contents were not JSON serializable. This no longer - occurs as the :attr:`.QuantumCircuit.metadata` attribute is not used to run the - simulation so now the contents are no serialized and instead are directly attached - to the :class:`qiskit.result.Result` object without attempting to JSON serialize - the contents. - Fixed `#1435 `__ - -Ignis 0.7.0 -=========== - -No change - -.. _Release Notes_0.19.0_IBMQ: - -IBM Q Provider 0.19.0 -===================== - -.. _Release Notes_0.19.0_IBMQ_New Features: - -New Features ------------- - -- The qiskit-ibmq-provider package now supports IBM Quantum LiveData features. - These features allow users to observe the real-time behavior of IBM Quantum - backends while executing jobs. Specifically, the provider now includes a - new tab in the backend Jupyter-related widget and supports the execution of - jobs (via :meth:`qiskit.providers.ibmq.IBMQBackend.run` method) with the - `live_data_enabled=True` parameter in allowed IBM Quantum backends. - -- You can now specify a different logging level in the ``options`` keyword - when submitting a Qiskit Runtime job with the - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.run` method. - - -.. _Release Notes_0.19.0_IBMQ_Upgrade Notes: - -Upgrade Notes -------------- - -- Python 3.6 support has been dropped since it has reached end of life in Dec 2021. - -- `qiskit.providers.ibmq.random`, the random number service which was used to access the CQC - randomness extractor is no longer supported and has been removed. - - -.. _Release Notes_0.19.0_IBMQ_Deprecation Notes: - -Deprecation Notes ------------------ - -- The ``image`` keyword in the - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.run` method is - deprecated. You should instead specify the image to use in the ``options`` - keyword. - - -.. _Release Notes_0.19.0_IBMQ_Bug Fixes: - -Bug Fixes ---------- - -- Fixes issue `#190 `__. - Now :class:`qiskit.providers.ibmq.runtime.RuntimeEncoder` and - :class:`qiskit.providers.ibmq.runtime.RuntimeDecoder` have been updated to handle - instances of the `Instruction` class. - -- Fixes issue `#74 `__ - where numpy ndarrays with object types could not be - serialized. :class:`qiskit.providers.ibmq.runtime.RuntimeEncoder` and - :class:`qiskit.providers.ibmq.runtime.RuntimeDecoder` have been updated - to handle these ndarrays. - -############# -Qiskit 0.35.0 -############# - -.. _Release Notes_0.20.0: - -Terra 0.20.0 -============ - -.. _Release Notes_0.20.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.20/prepare-0.20-79918ed0fc5b496e.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -The Qiskit Terra 0.20.0 release highlights are: - -* The introduction of multithreaded modules written in Rust to accelerate - the performance of certain portions of Qiskit Terra and improve scaling - with larger numbers of qubits. However, when building Qiskit from source a - `Rust `__ compiler is now required. - -* More native support for working with a :class:`~.Target` in the transpiler. - Several passes now support working directly with a :class:`~.Target` object - which makes the transpiler robust in the types of backends it can target. - -* The introduction of the :mod:`qiskit.primitives` module. These APIs - provide different abstraction levels for computing outputs of interest from - :class:`~.QuantumCircuit` and using backends. For - example, the :class:`~qiskit.primitives.BaseEstimator` defines an abstract - interface for estimating an expectation value of an observable. - This can then be used to construct higher level algorithms and applications - that are built using the estimation of expectation values without having - to worry about the implementation of computing the expectation value. - This decoupling allows the implementation to improve in speed and quality - while adhering to the defined abstract interface. - Likewise, the :class:`~qiskit.primitives.BaseSampler` computes - quasi-probability distributions from circuit measurements. Other primitives will - be introduced in the future. - -This release no longer has support for Python 3.6. With this release, -Python 3.7 through Python 3.10 are required. - - -.. _Release Notes_0.20.0_New Features: - -New Features ------------- - -.. releasenotes/notes/0.20/Operator-from_circuit-25b20d4b3ad5c398.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new constructor method for the :class:`.Operator` class, - :meth:`.Operator.from_circuit` for creating a new :class:`.Operator` - object from a :class:`.QuantumCircuit`. While this was possible normally - using the default constructor, the :meth:`.Operator.from_circuit` method - provides additional options to adjust how the operator is created. Primarily - this lets you permute the qubit order based on a set :class:`.Layout`. For, - example:: - - from qiskit.circuit import QuantumCircuit - from qiskit import transpile - from qiskit.transpiler import CouplingMap - from qiskit.quantum_info import Operator - - circuit = QuantumCircuit(3) - circuit.h(0) - circuit.cx(0, 1) - circuit.cx(1, 2) - - cmap = CouplingMap.from_line(3) - out_circuit = transpile(circuit, initial_layout=[2, 1, 0], coupling_map=cmap) - operator = Operator.from_circuit(out_circuit) - - the ``operator`` variable will have the qubits permuted based on the - layout so that it is identical to what is returned by ``Operator(circuit)`` - before transpilation. - -.. releasenotes/notes/0.20/_copy_circuit_metadata-a9d03e699118dba2.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new method :meth:`.DAGCircuit.copy_empty_like` - to the :class:`~.DAGCircuit` class. This method is used to create a new - copy of an existing :class:`.DAGCircuit` object with the - same structure but empty of any instructions. This method is the same as - the private method ``_copy_circuit_metadata()``, but instead is now - part of the public API of the class. - -.. releasenotes/notes/0.20/access-backends-from-mock-d3897ecb8490219a.yaml @ None - -- The fake backend and fake provider classes which were previously available - in ``qiskit.test.mock`` are now also accessible in a new module: - ``qiskit.providers.fake_provider``. This new module supersedes the previous - module ``qiskit.test.mock`` which will be deprecated in Qiskit 0.21.0. - -.. releasenotes/notes/0.20/add-linear-functions-904c8403ef7ab464.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new gate class, :class:`.LinearFunction`, that efficiently encodes - a linear function (i.e. a function that can be represented by a sequence - of :class:`.CXGate` and :class:`.SwapGate` gates). - -.. releasenotes/notes/0.20/add-linear-functions-904c8403ef7ab464.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new transpiler pass :class:`.CollectLinearFunctions` that collects - blocks of consecutive :class:`.CXGate` and :class:`.SwapGate` gates in a - circuit, and replaces each block with a :class:`.LinearFunction` gate. - -.. releasenotes/notes/0.20/add-linear-functions-904c8403ef7ab464.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new transpiler pass :class:`.LinearFunctionsSynthesis` - that synthesizes any :class:`.LinearFunction` gates in using the - `Patel-Markov-Hayes algorithm `__. - When combined with the :class:`.CollectLinearFunctions` transpiler pass - this enables to collect blocks of consecutive :class:`.CXGate` and - :class:`.SwapGate` gates in a circuit, and re-synthesize them using the - `Patel-Markov-Hayes algorithm `__. - -.. releasenotes/notes/0.20/add-linear-functions-904c8403ef7ab464.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new transpiler pass :class:`.LinearFunctionsToPermutations` that - replaces a :class:`.LinearFunction` gate by a :class:`.Permutation` circuit - whenever possible. - -.. releasenotes/notes/0.20/add-nested-conditionals-pass-manager-db7b8b9874018d0d.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- :class:`~.FlowController` classes (such as :class:`~.ConditionalController`) - can now be nested inside a :class:`~.PassManager` instance when using the - :meth:`.PassManager.append` method. This enables the use of nested logic to - control the execution of passes in the :class:`~.PassManager`. For example:: - - from qiskit.transpiler import ConditionalController, PassManager - from qiskit.transpiler.passes import ( - BasisTranslator, GatesInBasis, Optimize1qGatesDecomposition, FixedPoint, Depth - ) - from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - - pm = PassManager() - - def opt_control(property_set): - return not property_set["depth_fixed_point"] - - def unroll_condition(property_set): - return not property_set["all_gates_in_basis"] - - depth_check = [Depth(), FixedPoint("depth")] - opt = [Optimize1qGatesDecomposition(['rx', 'ry', 'rz', 'rxx'])] - unroll = [BasisTranslator(sel, ['rx', 'ry', 'rz', 'rxx'])] - unroll_check = [GatesInBasis(['rx', 'ry', 'rz', 'rxx'])] - flow_unroll = [ConditionalController(unroll, condition=unroll_condition)] - - pm.append(depth_check + opt + unroll_check + flow_unroll, do_while=opt_control) - - The ``pm`` :class:`~.PassManager` object will only execute the - :class:`.BasisTranslator` pass (in the ``unroll`` step) in each loop - iteration if the ``unroll_condition`` is met. - -.. releasenotes/notes/0.20/add-parameter-prefix-support-to-ZFeatureMap-ZZFeatureMap-ba13832b9a832e88.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The constructors for the :class:`~qiskit.circuit.library.ZFeatureMap` and - :class:`~qiskit.circuit.library.ZZFeatureMap` classes have a new keyword - argument ``parameter_prefix``. This new argument is used to set the prefix - of parameters of the data encoding circuit. For example: - - .. code-block:: python - - from qiskit.circuit.library import ZFeatureMap - - feature_map = ZFeatureMap(feature_dimension=4, parameter_prefix="my_prefix") - feature_map.decompose().draw('mpl') - - the generated :class:`~qiskit.circuit.library.ZFeatureMap` circuit has - prefixed all its internal parameters with the prefix ``"my_prefix"``. - -.. releasenotes/notes/0.20/add-parameters-to-template-substitution-a1379cdbfcc10b5c.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`.TemplateOptimization` transpiler pass can now work - with :class:`~.Gate` objects that have :class:`.ParameterExpression` - parameters. An illustrative example of using :class:`.Parameter`\s - with :class:`.TemplateOptimization` is the following:: - - from qiskit import QuantumCircuit, transpile, schedule - from qiskit.circuit import Parameter - - from qiskit.transpiler import PassManager - from qiskit.transpiler.passes import TemplateOptimization - - # New contributions to the template optimization - from qiskit.transpiler.passes.calibration import RZXCalibrationBuilder, rzx_templates - - from qiskit.test.mock import FakeCasablanca - backend = FakeCasablanca() - - phi = Parameter('φ') - - qc = QuantumCircuit(2) - qc.cx(0,1) - qc.p(2*phi, 1) - qc.cx(0,1) - print('Original circuit:') - print(qc) - - pass_ = TemplateOptimization(**rzx_templates.rzx_templates(['zz2'])) - qc_cz = PassManager(pass_).run(qc) - print('ZX based circuit:') - print(qc_cz) - - # Add the calibrations - pass_ = RZXCalibrationBuilder(backend) - cal_qc = PassManager(pass_).run(qc_cz.bind_parameters({phi: 0.12})) - - # Transpile to the backend basis gates - cal_qct = transpile(cal_qc, backend) - qct = transpile(qc.bind_parameters({phi: 0.12}), backend) - - # Compare the schedule durations - print('Duration of schedule with the calibration:') - print(schedule(cal_qct, backend).duration) - print('Duration of standard with two CNOT gates:') - print(schedule(qct, backend).duration) - - outputs - - .. parsed-literal:: - - Original circuit: - - q_0: ──■──────────────■── - ┌─┴─┐┌────────┐┌─┴─┐ - q_1: ┤ X ├┤ P(2*φ) ├┤ X ├ - └───┘└────────┘└───┘ - ZX based circuit: - ┌─────────────┐ » - q_0: ────────────────────────────────────┤0 ├────────────» - ┌──────────┐┌──────────┐┌──────────┐│ Rzx(2.0*φ) │┌──────────┐» - q_1: ┤ Rz(-π/2) ├┤ Rx(-π/2) ├┤ Rz(-π/2) ├┤1 ├┤ Rx(-2*φ) ├» - └──────────┘└──────────┘└──────────┘└─────────────┘└──────────┘» - « - «q_0: ──────────────────────────────────────────────── - « ┌──────────┐┌──────────┐┌──────────┐┌──────────┐ - «q_1: ┤ Rz(-π/2) ├┤ Rx(-π/2) ├┤ Rz(-π/2) ├┤ P(2.0*φ) ├ - « └──────────┘└──────────┘└──────────┘└──────────┘ - Duration of schedule with the calibration: - 1600 - Duration of standard with two CNOT gates: - 6848 - -.. releasenotes/notes/0.20/add-repr-for-dag-nodes-2d0a95fecd3dd3db.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`.DAGOpNode`, :class:`.DAGInNode` and :class:`.DAGOutNode` - classes now define a custom ``__repr__`` method which outputs a - representation. Per the - `Python documentation `__ - the output is a string representation that is roughly equivalent to the - Python string used to create an equivalent object. - -.. releasenotes/notes/0.20/add-sparsepauliop-equiv-7a8a1420117dba21.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The performance of the :meth:`.SparsePauliOp.simplify` method has - greatly improved by replacing the use of ``numpy.unique`` to compute unique - elements of an array by a new similar function implemented in Rust that - doesn't pre-sort the array. - -.. releasenotes/notes/0.20/add-sparsepauliop-equiv-7a8a1420117dba21.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new method :meth:`~qiskit.quantum_info.SparsePauliOp.equiv` to the - :class:`~.SparsePauliOp` class for testing the equivalence of a - :class:`~.SparsePauliOp` with another :class:`.SparsePauliOp` object. - Unlike the ``==`` operator which compares operators element-wise, - :meth:`~qiskit.quantum_info.SparsePauliOp.equiv` compares whether two - operators are equivalent or not. For example:: - - op = SparsePauliOp.from_list([("X", 1), ("Y", 1)]) - op2 = SparsePauliOp.from_list([("X", 1), ("Y", 1), ("Z", 0)]) - op3 = SparsePauliOp.from_list([("Y", 1), ("X", 1)]) - - print(op == op2) # False - print(op == op3) # False - print(op.equiv(op2)) # True - print(op.equiv(op3)) # True - -.. releasenotes/notes/0.20/add-v2-mocked-backend-4ca2e4cfdf077c60.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added new fake backend classes from snapshots of the IBM Quantum systems - based on the :class:`~.BackendV2` interface and provided a - :class:`~qiskit.transpiler.Target` for each backend. :class:`~.BackendV2` - based versions of all the existing backends are added except for three old - backends ``FakeRueschlikon``, ``FakeTenerife`` and ``FakeTokyo`` as they - do not have snapshots files available which are required for creating - a new fake backend class based on :class:`~.BackendV2`. - - These new V2 fake backends will enable testing and development of new - features introduced by :class:`~qiskit.providers.backend.BackendV2` and - :class:`~qiskit.transpiler.Target` such as improving the transpiler. - -.. releasenotes/notes/0.20/add-xxminusyy-gate-63e6530c23500de9.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new gate class :class:`~qiskit.circuit.library.XXMinusYYGate` - to the circuit library (:mod:`qiskit.circuit.library`) for the XX-YY - interaction. This gate can be used to implement the - `bSwap gate `__ and its powers. It also - arises in the simulation of superconducting fermionic models. - -.. releasenotes/notes/0.20/add-xy-gate-e3ac32084273136a.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added new gate class, :class:`~qiskit.circuit.library.XXPlusYYGate`, to - the circuit library (:mod:`qiskit.circuit.library`). This gate is a - 2-qubit parameterized XX+YY interaction, also known as an XY gate, and is - based on the gate described in https://arxiv.org/abs/1912.04424. - -.. releasenotes/notes/0.20/bogota-manila-rome-santiago-as-fakepulsebackends-2907dec149997a27.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The ``FakeBogota``, ``FakeManila``, ``FakeRome``, and ``FakeSantiago`` fake - backends which can be found in the ``qiskit.providers.fake_provider`` module can now be - used as backends in Pulse experiments as they now include a - :class:`~qiskit.providers.models.PulseDefaults` created from a snapshot of - the equivalent IBM Quantum machine's properties. - -.. releasenotes/notes/0.20/consolidate-blocks-target-aware-6482e65d6ee2d18c.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.transpiler.passes.ConsolidateBlocks` pass has a new - keyword argument on its constructor, ``target``. This argument is used to - specify a :class:`~qiskit.transpiler.Target` object representing the - compilation target for the pass. If it is specified it supersedes the - ``basis_gates`` kwarg. If a target is specified, the pass will respect the - gates and qubits for the instructions defined in the - :class:`~qiskit.transpiler.Target` when deciding which gates to consolidate - into a unitary. - -.. releasenotes/notes/0.20/consolidate-blocks-target-aware-6482e65d6ee2d18c.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.transpiler.Target` class has a new method, - :meth:`~qiskit.transpiler.Target.instruction_supported` which is used - to query the target to see if an instruction (the combination of an - operation and the qubit(s) it is executed on) is supported on the backend - modelled by the :class:`~qiskit.transpiler.Target`. - -.. releasenotes/notes/0.20/custom-serializers-qpy-0097ab79f239fcfc.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new kwarg, ``metadata_serializer``, to the - :func:`.qpy.dump` function for specifying a custom - ``JSONEncoder`` subclass for use when serializing the - :attr:`.QuantumCircuit.metadata` attribute and a dual kwarg - ``metadata_deserializer`` to the :func:`.qpy.load` function - for specifying a ``JSONDecoder`` subclass. By default the - :func:`~qiskit.qpy.dump` and - :func:`~qiskit.qpy.load` functions will attempt to - JSON serialize and deserialize with the stdlib default json encoder and - decoder. Since :attr:`.QuantumCircuit.metadata` can contain any Python - dictionary, even those with contents not JSON serializable by the default - encoder, will lead to circuits that can't be serialized. The new - ``metadata_serializer`` argument for - :func:`~qiskit.qpy.dump` enables users to specify a - custom ``JSONEncoder`` that will be used with the internal ``json.dump()`` - call for serializing the :attr:`.QuantumCircuit.metadata` dictionary. This - can then be paired with the new ``metadata_deserializer`` argument of the - :func:`.qpy.load` function to decode those custom JSON - encodings. If ``metadata_serializer`` is specified on - :func:`~qiskit.qpy.dump` but ``metadata_deserializer`` - is not specified on :func:`~qiskit.qpy.load` calls - the QPY will be loaded, but the circuit metadata may not be reconstructed - fully. - - For example if you wanted to define a custom serialization for metadata and - then load it you can do something like:: - - from qiskit.qpy import dump, load - from qiskit.circuit import QuantumCircuit, Parameter - import json - import io - - class CustomObject: - """Custom string container object.""" - - def __init__(self, string): - self.string = string - - def __eq__(self, other): - return self.string == other.string - - class CustomSerializer(json.JSONEncoder): - """Custom json encoder to handle CustomObject.""" - - def default(self, o): - if isinstance(o, CustomObject): - return {"__type__": "Custom", "value": o.string} - return json.JSONEncoder.default(self, o) - - class CustomDeserializer(json.JSONDecoder): - """Custom json decoder to handle CustomObject.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, object_hook=self.object_hook, **kwargs) - - def object_hook(self, o): - """Hook to override default decoder.""" - if "__type__" in o: - obj_type = o["__type__"] - if obj_type == "Custom": - return CustomObject(o["value"]) - return o - - theta = Parameter("theta") - qc = QuantumCircuit(2, global_phase=theta) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - circuits = [qc, qc.copy()] - circuits[0].metadata = {"key": CustomObject("Circuit 1")} - circuits[1].metadata = {"key": CustomObject("Circuit 2")} - with io.BytesIO() as qpy_buf: - dump(circuits, qpy_buf, metadata_serializer=CustomSerializer) - qpy_buf.seek(0) - new_circuits = load(qpy_buf, metadata_deserializer=CustomDeserializer) - -.. releasenotes/notes/0.20/dense-layout-target-aware-2b330ccee948d31a.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.transpiler.passes.DenseLayout` pass has a new keyword - argument on its constructor, ``target``. This argument is used to specify a - :class:`~qiskit.transpiler.Target` object representing the compilation - target for the pass. If it is specified it supersedes the other arguments - on the constructor, ``coupling_map`` and ``backend_prop``. - -.. releasenotes/notes/0.20/dense-layout-target-aware-2b330ccee948d31a.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.transpiler.Target` class has a new method, - :meth:`~qiskit.transpiler.Target.operation_names_for_qargs`. This method is - used to get the operation names (i.e. lookup key in the target) for the - operations on a given ``qargs`` tuple. - -.. releasenotes/notes/0.20/dynamical-decoupling-with-alignment-9c1e5ee909eab0f7.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- A new pass :class:`~.DynamicalDecouplingPadding` has been added to the - :mod:`qiskit.transpiler.passes` module. This new pass supersedes the - existing :class:`~.DynamicalDecoupling` pass to work with the new - scheduling workflow in the transpiler. It is a subclass of the - :class:`~.BasePadding` pass and depends on having scheduling and alignment - analysis passes run prior to it in a :class:`~.PassManager`. - This new pass can take a ``pulse_alignment`` argument which represents a - hardware constraint for waveform start timing. The spacing between gates - comprising a dynamical decoupling sequence is now adjusted to satisfy this - constraint so that the circuit can be executed on hardware with the constraint. - This value is usually found in :attr:`.BackendConfiguration.timing_constraints`. - Additionally the pass also has an ``extra_slack_distribution`` option has been - to control how to distribute the extra slack when the duration of the - created dynamical decoupling sequence is shorter than the idle time of your circuit - that you want to fill with the sequence. This defaults to ``middle`` which is - identical to conventional behavior. The new strategy ``split_edges`` - evenly divide the extra slack into the beginning and end of the sequence, - rather than adding it to the interval in the middle of the sequence. - This might result in better noise cancellation especially when ``pulse_alignment`` > 1. - -.. releasenotes/notes/0.20/expose-tolerances-z2symmetries-9c444a7b1237252e.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.opflow.primitive_ops.Z2Symmetries` class now exposes - the threshold tolerances used to chop small real and imaginary parts of - coefficients. With this one can control how the coefficients of the tapered - operator are simplified. For example:: - - from qiskit.opflow import Z2Symmetries - from qiskit.quantum_info import Pauli - - z2_symmetries = Z2Symmetries( - symmetries=[Pauli("IIZI"), Pauli("IZIZ"), Pauli("ZIII")], - sq_paulis=[Pauli("IIXI"), Pauli("IIIX"), Pauli("XIII")], - sq_list=[1, 0, 3], - tapering_values=[1, -1, -1], - tol=1e-10, - ) - - By default, coefficients are chopped with a tolerance of ``tol=1e-14``. - -.. releasenotes/notes/0.20/expose-tolerances-z2symmetries-9c444a7b1237252e.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a :meth:`~qiskit.quantum_info.SparsePauliOp.chop` method to the - :class:`~qiskit.quantum_info.SparsePauliOp` class that truncates real and - imaginary parts of coefficients individually. This is different - from the :meth:`.SparsePauliOp.simplify` method which - removes a coefficient only if the absolute value is close to 0. For - example:: - - >>> from qiskit.quantum_info import SparsePauliOp - >>> op = SparsePauliOp(["X", "Y", "Z"], coeffs=[1+1e-17j, 1e-17+1j, 1e-17]) - >>> op.simplify() - SparsePauliOp(['X', 'Y'], - coeffs=[1.e+00+1.e-17j, 1.e-17+1.e+00j]) - >>> op.chop() - SparsePauliOp(['X', 'Y'], - coeffs=[1.+0.j, 0.+1.j]) - - Note that the chop method does not accumulate the coefficents of the same Paulis, e.g. - - .. code-block:: - - >>> op = SparsePauliOp(["X", "X"], coeffs=[1+1e-17j, 1e-17+1j) - >>> op.chop() - SparsePauliOp(['X', 'X'], - coeffs=[1.+0.j, 0.+1.j]) - -.. releasenotes/notes/0.20/gates_in_basis_target_aware-9bcd698adc3ecc28.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new kwarg, ``target``, to the constructor for the - :class:`.GatesInBasis` transpiler pass. This new argument can be used to - optionally specify a :class:`.Target` object that represents the backend. - When set this :class:`.Target` will be used for determining whether - a :class:`.DAGCircuit` contains gates outside the basis set and the - ``basis_gates`` argument will not be used. - -.. releasenotes/notes/0.20/ibm-cpu-arch-support-3289377f3834f29e.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added partial support for running on ppc64le and s390x Linux platforms. - This release will start publishing pre-compiled binaries for ppc64le and - s390x Linux platforms on all Python versions. However, unlike other - supported platforms not all of Qiskit's upstream dependencies support these - platforms yet. So a C/C++ compiler may be required to build and install - these dependencies and a simple ``pip install qiskit-terra`` with just a - working Python environment will not be sufficient to install Qiskit. - Additionally, these same constraints prevent us from testing the - pre-compiled wheels before publishing them, so the same guarantees around - platform support that exist for the other platforms don't apply here. - -.. releasenotes/notes/0.20/imag_gradients-3dabcd11343062a8.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.opflow.gradients.Gradient` and - :class:`~qiskit.opflow.gradients.QFI` classes can now calculate the imaginary - part of expectation value gradients. When using a different measurement basis, - i.e. ``-Y`` instead of ``Z``, we can measure the imaginary part of gradients - The measurement basis can be set with the ``aux_meas_op`` argument. - - For the gradients, ``aux_meas_op = Z`` computes ``0.5Re[(⟨ψ(ω)|)O(θ)|dωψ(ω)〉]`` - and ``aux_meas_op = -Y`` computes ``0.5Im[(⟨ψ(ω)|)O(θ)|dωψ(ω)〉]``. - For the QFIs, ``aux_meas_op = Z`` computes ``4Re[(dω⟨<ψ(ω)|)(dω|ψ(ω)〉)]`` - and ``aux_meas_op = -Y`` computes ``4Im[(dω⟨<ψ(ω)|)(dω|ψ(ω)〉)]``. - For example:: - - from qiskit import QuantumRegister, QuantumCircuit - from qiskit.opflow import CircuitStateFn, Y - from qiskit.opflow.gradients.circuit_gradients import LinComb - from qiskit.circuit import Parameter - - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - op = CircuitStateFn(primitive=qc, coeff=1.0) - - aux_meas_op = -Y - - prob_grad = LinComb(aux_meas_op=aux_meas_op).convert(operator=op, params=params) - -.. releasenotes/notes/0.20/instruction-durations-8d98369f89b48279.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~.InstructionDurations` class now has support for working - with parameters of an instruction. Each entry in an - :class:`~.InstructionDurations` object now consists of a tuple of - ``(inst_name, qubits, duration, parameters, unit)``. This enables an - :class:`~.InstructionDurations` to define durations for an instruction - given a certain parameter value to account for different durations with - different parameter values on an instruction that takes a numeric parameter. - -.. releasenotes/notes/0.20/iqx-dark-3dd0a500e1801673.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new value for the ``style`` keyword argument on the circuit drawer - function :func:`~.circuit_drawer` and :meth:`.QuantumCircuit.draw` method, - ``iqx_dark``. When ``style`` is set to ``iqx_dark`` with the ``mpl`` drawer - backend, the output visualization will use a color scheme similar to the - the dark mode color scheme used by the IBM Quantum composer. For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - from matplotlib.pyplot import show - - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.cx(0, 1) - circuit.p(0.2, 1) - - circuit.draw("mpl", style="iqx-dark") - -.. releasenotes/notes/0.20/lazy-dependency-checkers-d1f3ce7a14383484.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Several lazy dependency checkers have been added to the new module - :mod:`qiskit.utils.optionals`, which can be used to query if certain Qiskit - functionality is available. For example, you can ask if Qiskit has detected - the presence of ``matplotlib`` by asking - ``if qiskit.utils.optionals.HAS_MATPLOTLIB``. These objects only attempt to - import their dependencies when they are queried, so you can use them in - runtime code without affecting import time. - -.. releasenotes/notes/0.20/lazy-dependency-checkers-d1f3ce7a14383484.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Import time for :mod:`qiskit` has been significantly improved, especially - for those with many of Qiskit Terra's optional dependencies installed. - -.. releasenotes/notes/0.20/marginal_counts_act_on_memory-0a9b58d0b95046dd.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :func:`~.marginal_counts` function now supports marginalizing the - ``memory`` field of an input :class:`~.Result` object. For example, if - the input ``result`` argument is a qiskit :class:`~.Result` object - obtained from a 4-qubit measurement we can marginalize onto the first qubit - with:: - - print(result.results[0].data.memory) - marginal_result = marginal_counts(result, [0]) - print(marginal_result.results[0].data.memory) - - The output is:: - - ['0x0', '0x1', '0x2', '0x3', '0x4', '0x5', '0x6', '0x7'] - ['0x0', '0x1', '0x0', '0x1', '0x0', '0x1', '0x0', '0x1'] - -.. releasenotes/notes/0.20/multithreaded-stochastic-swap-6c2f13d7bd566284.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The internals of the :class:`.StochasticSwap` algorithm have been reimplemented - to be multithreaded and are now written in the - `Rust `__ programming language instead of Cython. - This significantly increases the run time performance of the compiler pass - and by extension :func:`~.transpile` when run with ``optimization_level`` 0, - 1, and 2. By default the pass will use up to the number of logical CPUs on your - local system but you can control the number of threads used by the pass by setting - the ``RAYON_NUM_THREADS`` environment variable to an integer value. For example, - setting ``RAYON_NUM_THREADS=4`` will run the :class:`.StochasticSwap` with 4 - threads. - -.. releasenotes/notes/0.20/multithreaded-stochastic-swap-6c2f13d7bd566284.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- A new environment variable ``QISKIT_FORCE_THREADS`` is available for users to - directly control whether potentially multithreaded portions of Qiskit's code - will run in multiple threads. Currently this is only used by the - :class:`~.StochasticSwap` transpiler pass but it likely will be used other - parts of Qiskit in the future. When this env variable is set to ``TRUE`` any - multithreaded code in Qiskit Terra will always use multiple threads regardless - of any other runtime conditions that might have otherwise caused the function - to use a single threaded variant. For example, in :class:`~.StochasticSwap` if - the pass is being run as part of a :func:`~.transpile` call with > 1 circuit - that is being executed in parallel with ``multiprocessing`` via - :func:`~.parallel_map` the :class:`~.StochasticSwap` will not use multiple - threads to avoid potentially oversubscribing CPU resources. However, if you'd - like to use multiple threads in the pass along with multiple processes you - can set ``QISKIT_FORCE_THREADS=TRUE``. - -.. releasenotes/notes/0.20/new-fake-backends-04ea9cb26374e385.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- New fake backend classes are available under ``qiskit.providers.fake_provider``. These - include mocked versions of ``ibm_cairo``, ``ibm_hanoi``, - ``ibmq_kolkata``, ``ibm_nairobi``, and ``ibm_washington``. As with the other fake backends, - these include snapshots of calibration and error data taken from the real - system, and can be used for local testing, compilation and simulation. - -.. releasenotes/notes/0.20/new-state-preparation-class-f8c0617a0c988f12.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Introduced a new class :class:`~qiskit.circuit.library.StatePreparation`. - This class allows users to prepare a desired state in the same fashion as - :class:`~qiskit.extensions.Initialize` without the reset being - automatically applied. - - For example, to prepare a qubit in the state :math:`(|0\rangle - |1\rangle) / \sqrt{2}`:: - - import numpy as np - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(1) - circuit.prepare_state([1/np.sqrt(2), -1/np.sqrt(2)], 0) - circuit.draw() - - The output is as:: - - ┌─────────────────────────────────────┐ - q_0: ┤ State Preparation(0.70711,-0.70711) ├ - └─────────────────────────────────────┘ - -.. releasenotes/notes/0.20/optimization-u2-gates-with-parameters-322b6c523251108c.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`.Optimize1qGates` transpiler pass now has support for optimizing :class:`.U1Gate`, - :class:`.U2Gate`, and :class:`.PhaseGate` gates with unbound parameters in a circuit. - Previously, if these gates had unbound parameters the pass would not use them. For example:: - - from qiskit import QuantumCircuit - from qiskit.circuit import Parameter - from qiskit.transpiler import PassManager - from qiskit.transpiler.passes import Optimize1qGates, Unroller - - phi = Parameter('φ') - alpha = Parameter('α') - - qc = QuantumCircuit(1) - qc.u1(2*phi, 0) - qc.u1(alpha, 0) - qc.u1(0.1, 0) - qc.u1(0.2, 0) - - pm = PassManager([Unroller(['u1', 'cx']), Optimize1qGates()]) - nqc = pm.run(qc) - - will be combined to the circuit with only one single-qubit gate:: - - qc = QuantumCircuit(1) - qc.u1(2*phi + alpha + 0.3, 0) - -.. releasenotes/notes/0.20/pauli_evolve_clifford-3885e8d7d8e8b424.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The methods :meth:`.Pauli.evolve` and :meth:`.PauliList.evolve` - now have a new keyword argument, ``frame``, which is used to - perform an evolution of a Pauli by a Clifford. If ``frame='h'`` (default) - then it does the Heisenberg picture evolution of a Pauli by a Clifford - (:math:`P' = C^\dagger P C`), and if ``frame='s'`` then it does the - Schrödinger picture evolution of a Pauli by a Clifford - (:math:`P' = C P C^\dagger`). The latter option yields a faster calculation, - and is also useful in certain cases. This new option makes the calculation - of the greedy Clifford decomposition method in :class:`.decompose_clifford` - significantly faster. - -.. releasenotes/notes/0.20/primitives-fb4515ec0f4cbd8e.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new module to Qiskit: :mod:`qiskit.primitives`. The primitives - module is where APIs are defined which provide different - abstractions around computing certain common functions from - :class:`~.QuantumCircuit`s. The concept behind a primitive is to provide a higher - level object that can be used to perform common computations using a given - :class:`~.QuantumCircuit` which abstracts away the details of the underlying - execution on a :class:`~Backend`. This enables higher level algorithms and - applications to concentrate on performing the computation and not need to - worry about the execution and processing of results and have a standardized - interface for common computations. For example, estimating an expectation - value of a quantum circuit and observable can be performed by any class - implementing the :class:`~.BaseEstimator` class and consumed in a - standardized manner regardless of the underlying implementation. - Applications can then be written using the primitive interface directly. - - - To start the module contains two types of primitives, - the :class:`~.Sampler` (see :class:`~.BaseSampler` for the abstract - class definition) and :class:`~.Estimator` (see :class:`~.BaseEstimator` - for the abstract class definition). Reference implementations are included - in the :mod:`qiskit.primitives` module and are built using the - :mod:`qiskit.quantum_info` module which perform ideal simulation of - primitive operation. The expectation is that provider packages will offer - their own implementations of these interfaces for providers which can - efficiently implement the protocol natively (typically using a classical - runtime). Additionally, in the future for providers which do not offer a - native implementation of the primitives a method will be provided which - will enable constructing primitive objects from a :class:`~.Backend`. - -.. releasenotes/notes/0.20/qpy-module-c2ff2cc086b52fc6.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new module, :mod:`qiskit.qpy`, which contains the functionality - previously exposed in :mod:`qiskit.circuit.qpy_serialization`. The public - functions previously exposed at :mod:`qiskit.circuit.qpy_serialization`, - :func:`~qiskit.qpy.dump` and :func:`~qiskit.qpy.load` are now available - from this new module (although they are still accessible from - :mod:`qiskit.circuit.qpy_serialization` but this will be deprecated in - a future release). This new module was added in the interest of the future - direction of the QPY file format, which in future versions will support - representing :mod:`~qiskit.pulse` :class:`~.Schedule` and - :class:`~.ScheduleBlock` objects in addition to the - :class:`~.QuantumCircuit` objects it supports today. - -.. releasenotes/notes/0.20/qubit-properties-target-6b1fb155a46cb942.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new attribute, :attr:`~.Target.qubit_properties` to the - :class:`~.Target` class. This attribute contains a list of - :class:`~.QubitProperties` objects for each qubit in the target. - For example:: - - target.qubit_properties[2] - - will contain the :class:`~.QubitProperties` for qubit number 2 in the - target. - - For :class:`~.BackendV2` authors, if you were previously defining - :class:`~.QubitProperties` directly on your :class:`~.BackendV2` - implementation by overriding :meth:`.BackendV2.qubit_properties` this - will still work fine. However, if you do move the definition to the - underlying :class:`~.Target` object and remove the specialized - :meth:`.BackendV2.qubit_properties` implementation which will enable - using qubit properties in the transpiler and also maintain API compatibility - with your previous implementation. - -.. releasenotes/notes/0.20/refactor-aux-operators-79d790f8a693a7c0.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new function, :func:`qiskit.algorithms.eval_observables`, which is - used to evaluate observables given a bound - :class:`~qiskit.circuit.QuantumCircuit`. It originates from a private - method, ``_eval_aux_ops()``, of the :class:`qiskit.algorithms.VQE` class but - the new :func:`~qiskit.algorithms.eval_observables` function is now more - general so that it can be used in other algorithms, for example time - evolution algorithms. - -.. releasenotes/notes/0.20/rework-basis-translator-a83dc46cbc71c3b1.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The basis search strategy in :class:`~.BasisTranslator` transpiler pass - has been modified into a variant of Dijkstra search which greatly improves - the runtime performance of the pass when attempting to target an unreachable - basis. - -.. releasenotes/notes/0.20/rust-denselayout-bc0f08874ad778d6.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~.DenseLayout` transpiler pass is now multithreaded, which - greatly improves the runtime performance of the pass. By default, it will - use the number of logical CPUs on your local system, but you can control - the number of threads used by the pass by setting the - ``RAYON_NUM_THREADS`` environment variable to an integer value. For - example, setting ``RAYON_NUM_THREADS=4`` will run the - :class:`~.DenseLayout` pass with 4 threads. - -.. releasenotes/notes/0.20/rust-pauli-expval-f2aa06c5bab85768.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The internal computations of :meth:`.Statevector.expectation_value` and - :meth:`.DensityMatrix.expectation_value` methods have been reimplemented - in the Rust programming language. This new implementation is multithreaded - and by default for a :class:`~.Statevector` or :class:`~.DensityMatrix` - >= 19 qubits will spawn a thread pool with the number of logical CPUs - available on the local system. You can you can control the number of - threads used by setting the ``RAYON_NUM_THREADS`` environment variable to - an integer value. For example, setting ``RAYON_NUM_THREADS=4`` will only - use 4 threads in the thread pool. - -.. releasenotes/notes/0.20/sparsepauliop-from-index-list-4660fdaa492cd8b2.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new :meth:`.SparsePauliOp.from_sparse_list` constructor that takes - an iterable, where the elements represent Pauli terms that are themselves - sparse, so that ``"XIIIIIIIIIIIIIIIX"`` can now be written as - ``("XX", [0, 16])``. For example, the operator - - .. math:: - - H = X_0 Z_3 + 2 Y_1 Y_4 - - can now be constructed as - - .. code-block:: python - - op = SparsePauliOp.from_sparse_list([("XZ", [0, 3], 1), ("YY", [1, 4], 2)], num_qubits=5) - # or equivalently, as previously - op = SparsePauliOp.from_list([("IZIIX", 1), ("YIIYI", 2)]) - - This facilitates the construction of very sparse operators on many qubits, - as is often the case for Ising Hamiltonians. - -.. releasenotes/notes/0.20/unitary-synth-target-aware-eac86b1faa2d71fd.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.transpiler.passes.UnitarySynthesis` transpiler pass has - a new keyword argument on its constructor, ``target``. This can be used to - optionally specify a :class:`~qiskit.transpiler.Target` object which - represents the compilation target for the pass. When it's specified it will - supersede the values set for ``basis_gates``, ``coupling_map``, and - ``backend_props``. - -.. releasenotes/notes/0.20/unitary-synth-target-aware-eac86b1faa2d71fd.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin` - abstract plugin class has a new optional attribute implementations can add, - :attr:`~qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin.supports_target`. - If a plugin has this attribute set to ``True`` a :class:`~qiskit.transpiler.Target` - object will be passed in the ``options`` payload under the ``target`` field. The - expectation is that this :class:`~qiskit.transpiler.Target` object will be used - in place of ``coupling_map``, ``gate_lengths``, ``basis_gates``, and ``gate_errors``. - -.. releasenotes/notes/0.20/update-instruction-alignment-passes-ef0f20d4f89f95f3.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Introduced a new transpiler pass workflow for building :class:`~.PassManager` objects - for scheduling :class:`~.QuantumCircuit` objects in the transpiler. In the new - workflow scheduling and alignment passes are all :class:`~.AnalysisPass` objects that - only update the property set of the pass manager, specifically new property set item - ``node_start_time``, which holds the absolute start time of each opnode. A separate - :class:`~.TransformationPass` such as :class:`~.PadDelay` is subsequently used - to apply scheduling to the DAG. This new workflow is both more efficient and can - correct for additional timing constraints exposed by a backend. - - Previously, the pass chain would have been implemented as ``scheduling -> alignment`` - which were both transform passes thus there were multiple :class:`~.DAGCircuit` - instances recreated during each pass. In addition, scheduling occured in each pass - to obtain instruction start time. Now the required pass chain becomes - ``scheduling -> alignment -> padding`` where the :class:`~.DAGCircuit` update only - occurs at the end with the ``padding`` pass. - - For those who are creating custom :class:`~.PassManager` objects that involve - circuit scheduling you will need to adjust your :class:`~.PassManager` - to insert one of the :class:`~.BasePadding` passes (currently - either :class:`~.PadDelay` or :class:`~.PadDynamicalDecoupling` can be used) - at the end of the scheduling pass chain. Without the padding pass the scheduling - passes will not be reflected in the output circuit of the :meth:`~.PassManager.run` - method of your custom :class:`~.PassManager`. - - For example, if you were previously building your :class:`~.PassManager` - with something like:: - - from qiskit.transpiler import PassManager - from qiskit.transpiler.passes import TimeUnitConversion, ALAPSchedule, ValidatePulseGates, AlignMeasures - - pm = PassManager() - scheduling = [ - ALAPSchedule(instruction_durations), PadDelay()), - ValidatePulseGates(granularity=timing_constraints.granularity, min_length=timing_constraints.min_length), - AlignMeasures(alignment=timing_constraints.acquire_alignment), - ] - pm.append(scheduling) - - you can instead use:: - - from qiskit.transpiler import PassManager - from qiskit.transpiler.passes import TimeUnitConversion, ALAPScheduleAnalysis, ValidatePulseGates, AlignMeasures, PadDelay - - pm = PassManager() - scheduling = [ - ALAPScheduleAnalysis(instruction_durations), PadDelay()), - ConstrainedReschedule(acquire_alignment=timing_constraints.acquire_alignment, pulse_alignment=timing_constraints.pulse_alignment), - ValidatePulseGates(granularity=timing_constraints.granularity, min_length=timing_constraints.min_length), - PadDelay() - ] - pm.append(scheduling) - - which will both be more efficient and also align instructions based on any hardware constraints. - -.. releasenotes/notes/0.20/update-instruction-alignment-passes-ef0f20d4f89f95f3.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new transpiler pass :class:`~.ConstrainedReschedule` pass. - The :class:`~.ConstrainedReschedule` pass considers both hardware - alignment constraints that can be definied in a :class:`.BackendConfiguration` - object, ``pulse_alignment`` and ``acquire_alignment``. This new class supersedes - the previosuly existing :class:`~.AlignMeasures` as it performs the same alignment - (via the property set) for measurement instructions in addition to general instruction - alignment. By setting the ``acquire_alignment`` constraint argument for the - :class:`~.ConstrainedReschedule` pass it is a drop-in replacement of - :class:`~.AlignMeasures` when paired with a new :class:`~.BasePadding` pass. - -.. releasenotes/notes/0.20/update-instruction-alignment-passes-ef0f20d4f89f95f3.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added two new transpiler passes :class:`~.ALAPScheduleAnalysis` and :class:`~.ASAPScheduleAnalysis` - which superscede the :class:`~.ALAPSchedule` and :class:`~.ASAPSchedule` as part of the - reworked transpiler workflow for schedling. The new passes perform the same scheduling but - in the property set and relying on a :class:`~.BasePadding` pass to adjust the circuit - based on all the scheduling alignment analysis. - - The standard behavior of these passes also aligns timing ordering with the topological - ordering of the DAG nodes. This change may affect the scheduling outcome if it includes - conditional operations, or simultaneously measuring two qubits with the same classical - register (edge-case). To reproduce conventional behavior, set ``clbit_write_latency`` - identical to the measurement instruction length. - - For example, consider scheduling an input circuit like: - - .. parsed-literal:: - - ┌───┐┌─┐ - q_0: ┤ X ├┤M├────────────── - └───┘└╥┘ ┌───┐ - q_1: ──────╫────┤ X ├────── - ║ └─╥─┘ ┌─┐ - q_2: ──────╫──────╫─────┤M├ - ║ ┌────╨────┐└╥┘ - c: 1/══════╩═╡ c_0=0x1 ╞═╩═ - 0 └─────────┘ 0 - - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.transpiler import InstructionDurations, PassManager - from qiskit.transpiler.passes import ALAPScheduleAnalysis, PadDelay, SetIOLatency - from qiskit.visualization.timeline import draw - - circuit = QuantumCircuit(3, 1) - circuit.x(0) - circuit.measure(0, 0) - circuit.x(1).c_if(0, 1) - circuit.measure(2, 0) - - durations = InstructionDurations([("x", None, 160), ("measure", None, 800)]) - - pm = PassManager( - [ - SetIOLatency(clbit_write_latency=800, conditional_latency=0), - ALAPScheduleAnalysis(durations), - PadDelay(), - ] - ) - draw(pm.run(circuit)) - - As you can see in the timeline view, the measurement on ``q_2`` starts before - the conditional X gate on the ``q_1``, which seems to be opposite to the - topological ordering of the node. This is also expected behavior - because clbit write-access happens at the end edge of the measure instruction, - and the read-access of the conditional gate happens the begin edge of the instruction. - Thus topological ordering is preserved on the timeslot of the classical register, - which is not captured by the timeline view. - However, this assumes a paticular microarchitecture design, and the circuit is - not necessary scheduled like this. - - By using the default configuration of passes, the circuit is schedule like below. - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.transpiler import InstructionDurations, PassManager - from qiskit.transpiler.passes import ALAPScheduleAnalysis, PadDelay - from qiskit.visualization.timeline import draw - - circuit = QuantumCircuit(3, 1) - circuit.x(0) - circuit.measure(0, 0) - circuit.x(1).c_if(0, 1) - circuit.measure(2, 0) - - durations = InstructionDurations([("x", None, 160), ("measure", None, 800)]) - - pm = PassManager([ALAPScheduleAnalysis(durations), PadDelay()]) - draw(pm.run(circuit)) - - Note that clbit is locked throughout the measurement instruction interval. - This behavior is designed based on the Qiskit Pulse, in which the acquire instruction takes - ``AcquireChannel`` and ``MemorySlot`` which are not allowed to overlap with other instructions, - i.e. simultaneous memory access from the different instructions is prohibited. - This also always aligns the timing ordering with the topological node ordering. - -.. releasenotes/notes/0.20/update-instruction-alignment-passes-ef0f20d4f89f95f3.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new transpiler pass :class:`~.PadDynamicalDecoupling` - which supersedes the :class:`~.DynamicalDecoupling` pass as part of the - reworked transpiler workflow for scheduling. This new pass will insert dynamical decoupling - sequences into the circuit per any scheduling and alignment analysis that occured in earlier - passes. - -.. releasenotes/notes/0.20/update-plot-gate-map-9ed6ad5490bafbbf.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :func:`~.plot_gate_map` visualization function and the functions built - on top of it, :func:`~.plot_error_map` and :func:`~.plot_circuit_layout`, - have a new keyword argument, ``qubit_coordinates``. This argument takes - a sequence of 2D coordinates to use for plotting each qubit in the backend - being visualized. If specified this sequence must have a length equal to - the number of qubits on the backend and it will be used instead of the - default behavior. - -.. releasenotes/notes/0.20/update-plot-gate-map-9ed6ad5490bafbbf.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :func:`~.plot_gate_map` visualization function and the functions built - on top of it, :func:`~.plot_error_map` and :func:`~.plot_circuit_layout`, - now are able to plot any backend not just those with the number of qubits - equal to one of the IBM backends. This relies on - the retworkx ``spring_layout()`` - `function `__ - to generate the layout for the visualization. If the default layout doesn't - work with a backend's particular coupling graph you can use the - ``qubit_coordinates`` function to set a custom layout. - -.. releasenotes/notes/0.20/update-plot-gate-map-9ed6ad5490bafbbf.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :func:`~.plot_gate_map` visualization function and the functions built - on top of it, :func:`~.plot_error_map` and :func:`~.plot_circuit_layout`, - are now able to function with a :class:`~.BackendV2` based backend. - Previously, these functions only worked with :class:`~.BaseBackend` or - :class:`~.BackendV1` based backends. - -.. releasenotes/notes/0.20/upgrade-alap-asap-passes-bcacc0f1053c9828.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a new transpiler pass, :class:`~.SetIOLatency`. This pass takes two - arguments ``clbit_write_latency`` and ``conditional_latency`` to define the - I/O latency for classical bits and classical conditions on a backend. This - pass will then define these values on the pass manager's property set to - enable subsequent scheduling and alignment passes to correct for these - latencies and provide a more presice scheduling output of a dynamic circuit. - -.. releasenotes/notes/0.20/upgrade-convert-scheduling-passes-to-analysis-04333b6fef524d21.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- A new transpiler pass :class:`.PadDelay` has been added. This pass fills - idle time on the qubit wires with :class:`~.circuit.Delay` instructions. - This pass is part of the new workflow for scheduling passes in the - transpiler and depends on a scheduling analysis pass (such as - :class:`~.ALAPScheduleAnalysis` or :class:`~ASAPScheduleAnalysis`) and - any alignment passes (such as :class:`~.ConstrainedReschedule`) to be - run prior to :class:`.PadDelay`. - -.. releasenotes/notes/0.20/vf2layout-target-51cc8f77fdfcde67.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~.VF2Layout` transpiler pass has a new keyword argument, - ``target`` which is used to provide a :class:`~.Target` object for - the pass. When specified, the :class:`~.Target` will be used by the - pass for all information about the target device. If it is specified, - the ``target`` option will take priority over the ``coupling_map`` and - ``properties`` arguments. - -.. releasenotes/notes/0.20/vqe-optimizer-callables-1aa14d78c855d383.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Allow callables as optimizers in :class:`~qiskit.algorithms.VQE` and - :class:`~qiskit.algorithms.QAOA`. Now, the optimizer can either be one of Qiskit's optimizers, - such as :class:`~qiskit.algorithms.optimizers.SPSA` or a callable with the following signature: - - .. code-block:: python - - from qiskit.algorithms.optimizers import OptimizerResult - - def my_optimizer(fun, x0, jac=None, bounds=None) -> OptimizerResult: - # Args: - # fun (callable): the function to minimize - # x0 (np.ndarray): the initial point for the optimization - # jac (callable, optional): the gradient of the objective function - # bounds (list, optional): a list of tuples specifying the parameter bounds - - result = OptimizerResult() - result.x = # optimal parameters - result.fun = # optimal function value - return result - - The above signature also allows to directly pass any SciPy minimizer, for instance as - - .. code-block:: python - - from functools import partial - from scipy.optimize import minimize - - optimizer = partial(minimize, method="L-BFGS-B") - - -.. _Release Notes_0.20.0_Known Issues: - -Known Issues ------------- - -.. releasenotes/notes/0.20/multithreaded-stochastic-swap-6c2f13d7bd566284.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- When running :func:`.parallel_map` (which is done internally by - performance sensitive functions such as :func:`.transpile` and - :func:`.assemble`) in a subprocess launched outside of - :func:`.parallel_map`, it is possible that the parallel dispatch performed - inside :func:`.parallel_map` will hang and never return. - This is due to upstream issues in CPython around the default - method to launch subprocesses on Linux and macOS with Python 3.7 (see - https://bugs.python.org/issue40379 for more details). If you - encounter this, you have two options: you can either remove the nested - parallel processes, as calling :func:`.parallel_map` from a main process - should work fine; or you can manually call the CPython standard library - ``multiprocessing`` module to perform similar parallel dispatch from a - subprocess, but use the ``"spawn"`` or ``"forkserver"`` launch methods to - avoid the potential to have things get stuck and never return. - - -.. _Release Notes_0.20.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.20/bit-slots-17d6649872da0440.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The classes :class:`~.circuit.Qubit`, :class:`.Clbit` and :class:`.AncillaQubit` now - have the ``__slots__`` attribute. This is to reduce their memory usage. As a - side effect, they can no longer have arbitrary data attached as attributes - to them. This is very unlikely to have any effect on downstream code other - than performance benefits. - -.. releasenotes/notes/0.20/bump-retworkx-0.11.0-97db170ae39cacf8.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The core dependency ``retworkx`` had its version requirement bumped to 0.11.0, up from 0.10.1. - This improves the performance of transpilation pass - :class:`~qiskit.transpiler.passes.ConsolidateBlocks`. - -.. releasenotes/notes/0.20/bump-symengine-8ca362f5b9fef199.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The minimum supported version of ``symengine`` is now 0.9.0. This was - necessary to improve compatibility with Python's ``pickle`` module which - is used internally as part of parallel dispatch with :func:`.parallel_map`. - -.. releasenotes/notes/0.20/bump-symengine-8ca362f5b9fef199.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The default value of ``QISKIT_PARALLEL`` when running with Python 3.9 on - Linux is now set to ``TRUE``. This means when running :func:`.parallel_map` - or functions that call it internally, such as :func:`.transpile` and - :func:`.assemble`, the function will be executed in multiple processes and - should have better run time performance. This change was made because the - issues with reliability of parallel dispatch appear to have been resolved - (see `#6188 `__ for - more details). If you still encounter issues because of this you can disable - multiprocessing and revert to the previous default behavior by setting the - ``QISKIT_PARALLEL`` environment variable to ``FALSE``, or setting the - ``parallel`` option to ``False`` in your user config file (also please file - an issue so we can track any issues related to multiprocessing). - -.. releasenotes/notes/0.20/cleanup-deprecated-circuitmeth-89edb244f572b754.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The previously deprecated ``MSGate`` gate class previously found in - :mod:`qiskit.circuit.library` has been removed. It was originally deprecated in the - 0.16.0 release. Instead the :class:`~qiskit.circuit.library.GMS` class should be used, as - this allows you to create an equivalent 2 qubit MS gate in addition to - an ``MSGate`` for any number of qubits. - -.. releasenotes/notes/0.20/cleanup-deprecated-circuitmeth-89edb244f572b754.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The previously deprecated ``mirror()`` method of the :class:`~qiskit.circuit.Instruction` - class has been removed. It was originally deprecated in 0.15.0 release. Instead you should - use :meth:`.Instruction.reverse_ops`. - -.. releasenotes/notes/0.20/cleanup-deprecated-circuitmeth-89edb244f572b754.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The previously deprecated ``num_ancilla_qubits()`` method of the - :class:`qiskit.circuit.library.PiecewiseLinearPauliRotations` and - :class:`qiskit.circuit.library.WeightedAdder` classes has been removed. It was originally - deprecated in the 0.16.0 release. Instead the - :meth:`.PiecewiseLinearPauliRotations.num_ancillas` and :meth:`.WeightedAdder.num_ancillas` - methods should be used. - -.. releasenotes/notes/0.20/cleanup-deprecated-circuitmeth-89edb244f572b754.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The previously deprecated ``reverse`` argument on the constructor for the - :class:`~qiskit.circuit.library.PolynomialPauliRotations` class has been removed. It - was originally deprecated in the 0.15.0 release. Instead you should use the - :meth:`.QuantumCircuit.reverse_bits` method to reverse the - :class:`~qiskit.circuit.library.PolynomialPauliRotations` circuit if needed. - -.. releasenotes/notes/0.20/cleanup-deprecated-circuitmeth-89edb244f572b754.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The previously deprecated ``angle`` argument on the constructors for the - :class:`~qiskit.circuit.library.C3SXGate` and :class:`~qiskit.circuit.library.C3XGate` - gate classes has been removed. It was originally deprecated in the 0.17.0 release. Instead - for fractional 3-controlled X gates you can use the :meth:`.C3XGate.power` method. - -.. releasenotes/notes/0.20/cleanup-deprecated-circuitmeth-89edb244f572b754.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Support for using ``np.ndarray`` objects as part of the :attr:`~qiskit.circuit.Gate.params` attribute - of a :class:`~qiskit.circuit.Gate` object has been removed. This has been deprecated - since Qiskit Terra 0.16.0 and now will no longer work. Instead one should create a new subclass - of :class:`~qiskit.circuit.Gate` and explicitly allow a ``np.ndarray`` input by overloading the - :meth:`~.Gate.validate_parameter` method. - -.. releasenotes/notes/0.20/csp-layout-extra-b62a5e53f136534a.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- A new extra ``csp-layout-pass`` has been added to the install target for - ``pip install qiskit-terra``, and is also included in the ``all`` extra. - This has no effect in Qiskit Terra 0.20, but starting from Qiskit Terra 0.21, - the dependencies needed only for the :class:`.CSPLayout` transpiler pass will - be downgraded from requirements to optionals, and installed by this extra. - You can prepare a package that depends on this pass by setting its - requirements (or ``pip install`` command) to target - ``qiskit-terra[csp-layout-pass]``. - -.. releasenotes/notes/0.20/drop-python3.6-support-45ecc9e1832934cd.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Support for running with Python 3.6 has been removed. To run Qiskit you need - a minimum Python version of 3.7. - -.. releasenotes/notes/0.20/fix-algorithms-7f1b969e5b2447f9.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~.AmplitudeEstimator` now inherits from the ``ABC`` class from - the Python standard library. This requires any subclass to implement the - :meth:`~.AmplitudeEstimator.estimate` method when previously it wasn't - required. This was done because the original intent of the class was to - always be a child class of ``ABC``, as the :meth:`~.AmplitudeEstimator.estimate` - is required for the operation of an :class:`~.AmplitudeEstimator` object. - However, if you were previously defining an :class:`~.AmplitudeEstimator` - subclass that didn't implement :meth:`~.AmplitudeEstimator.estimate` this - will now result in an error. - -.. releasenotes/notes/0.20/lazy-dependency-checkers-d1f3ce7a14383484.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The error raised by :class:`.HoareOptimizer` if the optional dependency - ``z3`` is not available has changed from :class:`.TranspilerError` to - :class:`.MissingOptionalLibraryError` (which is both a :class:`.QiskitError` - and an ``ImportError``). This was done to be consistent with the other - optional dependencies. - -.. releasenotes/notes/0.20/manylinux2014-e33268fda54e12b1.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- On Linux, the minimum library support has been raised from the - `manylinux2010 VM `__ to - `manylinux2014 `__. This mirrors - similar changes in Numpy and Scipy. There should be no meaningful effect - for most users, unless your system still contains a very old version of - ``glibc``. - -.. releasenotes/notes/0.20/marginal_counts_act_on_memory-0a9b58d0b95046dd.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :func:`~.marginal_counts` function when called with a :class:`~.Result` - object input, will now marginalize the ``memory`` field of experiment data - if it's set in the input :class:`~.Result`. Previously, the ``memory`` field - in the the input was not marginalized. This change was made because the previous - behavior would result in the ``counts`` field not matching the ``memory`` - field after :func:`~.marginal_counts` was called. If the previous behavior - is desired it can be restored by setting ``marginalize_memory=None`` as - an argument to :func:`~.marginal_counts` which will not marginalize the - ``memory`` field. - -.. releasenotes/notes/0.20/multithreaded-stochastic-swap-6c2f13d7bd566284.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`.StochasticSwap` transpiler pass may return different results with - the same seed value set. This is due to the internal rewrite of the transpiler - pass to improve runtime performance. However, this means that if you ran - :func:`~.transpile` with ``optimization_level`` 0, 1 (the default), or 2 with a - value set for ``seed_transpiler`` you may get an output with different swap - mapping present after upgrading to Qiskit Terra 0.20.0. - -.. releasenotes/notes/0.20/multithreaded-stochastic-swap-6c2f13d7bd566284.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- To build Qiskit Terra from source a `Rust `__ - compiler is now needed. This is due to the internal rewrite of the - :class:`.StochasticSwap` transpiler pass which greatly improves the runtime - performance of the transpiler. The rust compiler can easily be installed - using rustup, which can be found here: https://rustup.rs/ - -.. releasenotes/notes/0.20/paulievo-classname-c0f002d519c45e42.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :attr:`~.PauliEvolutionGate.name` attribute of the - :class:`~qiskit.circuit.library.PauliEvolutionGate` class has been changed - to always be ``"PauliEvolution"``. This change was made to be consistent - with other gates in Qiskit and enables other parts of Qiskit to quickly - identify when a particular operation in a circuit is a - :class:`~qiskit.circuit.library.PauliEvolutionGate`. For example, - it enables the unrolling to Pauli evolution gates. - - Previously, the name contained the operators which are evolved, which is - now available via the :attr:`.PauliEvolutionGate.label` attribute. - If a circuit with a :class:`~.PauliEvolutionGate` is drawn, the gate will - still show the same information, which gates are being evolved. - -.. releasenotes/notes/0.20/remove-deprecated-algo-methods-eb101adf17a2b920.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The previously deprecated methods: - - * ``qiskit.algorithms.VQE.get_optimal_cost`` - * ``qiskit.algorithms.VQE.get_optimal_circuit`` - * ``qiskit.algorithms.VQE.get_optimal_vector`` - * ``qiskit.algorithms.VQE.optimal_params`` - * ``qiskit.algorithms.HamiltonianPhaseEstimationResult.most_likely_phase`` - * ``qiskit.algorithms.PhaseEstimationResult.most_likely_phase`` - - which were originally deprecated in the Qiskit Terra 0.18.0 release have - been removed and will no longer work. - -.. releasenotes/notes/0.20/remove-deprecated-algo-methods-eb101adf17a2b920.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`qiskit.algorithms.VariationalAlgorithm` class is now defined - as an abstract base class (``ABC``) which will require classes that inherit - from it to define both a :attr:`.VariationalAlgorithm.initial_point` getter - and setter method. - -.. releasenotes/notes/0.20/remove-deprecated-pass-manager-dc1dddbd7dcd866f.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The ``pass_manager`` kwarg for the :func:`.transpile` function - has been removed. It was originally deprecated in the 0.13.0 release. - The preferred way to transpile a circuit with a custom - :class:`~qiskit.transpiler.PassManager` object is to use the - :meth:`~qiskit.transpiler.PassManager.run` method of the :class:`.PassManager` - object. - -.. releasenotes/notes/0.20/remove-parametrized-schedule-fc4b31a8180db9d9.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The previously deprecated ``ParametrizedSchedule`` class has been removed - and no longer exists. This class was deprecated as a part of the 0.17.0 - release. Instead of using this class you can directly parametrize - :py:class:`~qiskit.pulse.Schedule` or - :py:class:`~qiskit.pulse.ScheduleBlock` objects by specifying a - :py:class:`~qiskit.circuit.Parameter` object to the parametric pulse - argument. - -.. releasenotes/notes/0.20/remove_probability_distributions-d30bd77f0f2b9570.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The module ``qiskit.circuit.library.probability_distributions`` has been - removed and no longer exists as per the deprecation notice from qiskit-terra - 0.17.0 (released Apr 1, 2021). The affected classes are - ``UniformDistribution``, ``NormalDistribution``, and - ``LogNormalDistribution``. They are all moved to the - `qiskit-finance `__ - library, into its circuit library module: - ``qiskit_finance.circuit.library.probability_distributions``. - -.. releasenotes/notes/0.20/rename-fake-mumbai-v2-2a4b4ead7360eab5.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The previous :class:`qiskit.test.mock.fake_mumbai_v2.FakeMumbaiV2` class - has been renamed to ``FakeMumbaiFractionalCX`` to differentiate it from - the :class:`~.BackendV2` based fake backend for the IBM Mumbai device, - :class:`qiskit.test.mock.backends.FakeMumbaiV2`. If you were previously - relying on the :class:`~qiskit.test.mock.fake_mumbai_v2.FakeMumbaiV2` class - to get a fake backend that had fractional applications of :class:`~.CXGate` - defined in its target you need to use ``FakeMumbaiFractionalCX`` class - as the :class:`~qiskit.test.mock.backends.FakeMumbaiV2` will no longer - have those extra gate definitions in its :class:`~.Target`. - -.. releasenotes/notes/0.20/rework-circuit-argument-resolver-780091cd6f97f872.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The resolver used by :meth:`.QuantumCircuit.append` (and consequently all - methods that add an instruction onto a :class:`.QuantumCircuit`) to convert - bit specifiers has changed to make it faster and more reliable. Certain - constructs like:: - - import numpy as np - from qiskit import QuantumCircuit - - qc = QuantumCircuit(1, 1) - qc.measure(np.array([0]), np.array([0])) - - will now work where they previously would incorrectly raise an error, but - certain pathological inputs such as:: - - from sympy import E, I, pi - qc.x(E ** (I * pi)) - - will now raise errors where they may have occasionally (erroneously) - succeeded before. For almost all correct uses, there should be no - noticeable change except for a general speed-up. - -.. releasenotes/notes/0.20/rework-circuit-argument-resolver-780091cd6f97f872.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The semi-public internal method :meth:`.QuantumCircuit._append` no longer - checks the types of its inputs, and assumes that there are no invalid - duplicates in its argument lists. This function is used by certain internal - parts of Qiskit and other libraries to build up :class:`.QuantumCircuit` - instances as quickly as possible by skipping the error checking when the - data is already *known* to be correct. In general, users or functions - taking in user data should use the public :meth:`.QuantumCircuit.append` - method, which resolves integer bit specifiers, broadcasts its arguments and - checks the inputs for correctness. - -.. releasenotes/notes/0.20/rust-pauli-expval-f2aa06c5bab85768.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Cython is no longer a build dependency of Qiskit Terra and is no longer - required to be installed when building Qiskit Terra from source. - -.. releasenotes/notes/0.20/vf2layout-preset-passmanager-db46513a24e79aa9.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The preset passmanagers in :mod:`qiskit.transpiler.preset_passmanagers` - for all optimization levels 2 and 3 as generated by - :func:`~qiskit.transpiler.preset_passmanagers.level_2_pass_manager` and - :func:`~qiskit.transpiler.preset_passmanagers.level_3_pass_manager` have - been changed to run the :class:`~qiskit.transpiler.passes.VF2Layout` by - default prior to the layout pass. The - :class:`~qiskit.transpiler.passes.VF2Layout` pass will quickly check if - a perfect layout can be found and supersedes what was previously - done for optimization levels 2 and 3 which were using a combination of - :class:`~qiskit.transpiler.passes.TrivialLayout` and - :class:`~qiskit.transpiler.passes.CSPLayout` to try and find a perfect - layout. This will result in potentially different behavior when - :func:`~qiskit.compiler.transpile` is called by default as it removes a - default path for all optimization levels >=2 of using a trivial layout - (where ``circuit.qubits[0]`` is mapped to physical qubit 0, - ``circuit.qubits[1]`` is mapped to physical qubit 1, etc) assuming the - trivial layout is perfect. If your use case was dependent on the - trivial layout you can explictly request it when transpiling by specifying - ``layout_method="trivial"`` when calling :func:`~qiskit.compiler.transpile`. - -.. releasenotes/notes/0.20/vf2layout-preset-passmanager-db46513a24e79aa9.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The preset pass manager for optimization level 1 (when calling - :func:`~qiskit.compiler.transpile` with ``optimization_level=1`` or when - no ``optimization_level`` argument is set) as generated by - :func:`~qiskit.transpiler.preset_passmanagers.level_1_pass_manager` has - been changed so that :class:`~qiskit.transpiler.passes.VF2Layout` is - called by default to quickly check if a a perfect layout can be found - prior to the :class:`~qiskit.transpiler.passes.DenseLayout`. However, - unlike with optimization level 2 and 3 a trivial layout is still attempted - prior to running :class:`~qiskit.transpiler.passes.VF2Layout` and if - it's a perfect mapping the output from - :class:`~qiskit.transpiler.passes.VF2Layout` will be used. - - -.. _Release Notes_0.20.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.20/deprecate-max-credits-56a404050a655a04.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The ``max_credits`` argument to :func:`~.execute_function.execute`, and all - of the ``Qobj`` configurations (e.g. :class:`.QasmQobjConfig` and - :class:`.PulseQobjConfig`), is deprecated and will be removed in a future - release. The credit system has not been in use on IBM Quantum backends for - two years, and the option has no effect. No alternative is necessary. - For example, if you were calling :func:`~.execute_function.execute` as:: - - job = execute(qc, backend, shots=4321, max_credits=10) - - you can simply omit the ``max_credits`` argument:: - - job = execute(qc, backend, shots=4321) - -.. releasenotes/notes/0.20/deprecate_odd_suzuki-091178b1bdc8b172.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Using an odd integer for the ``order`` argument on the constructor of the - :class:`~.qiskit.synthesis.SuzukiTrotter` class is deprecated and will - no longer work in a future release. The product formulae used by the - :class:`~.qiskit.synthesis.SuzukiTrotter` are only defined when the order - is even as the Suzuki product formulae is symmetric. - -.. releasenotes/notes/0.20/fix-registerless-bits-reverse-display-ee5efba0eff645a8.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The ``qregs``, ``cregs``, ``layout``, and ``global_phase`` kwargs to - the :class:`.MatplotlibDrawer`, :class:`.TextDrawing`, and - :class:`.QCircuitImage` classes, and the ``calibrations`` kwarg to the - :class:`.MatplotlibDrawer` class, are now deprecated and will be removed - in a subsequent release. - - -.. _Release Notes_0.20.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.19/fix-circuit-conversion-loose-qubits-8d190426e4e892f1.yaml @ b'29c62c4bf5d01015283566c81b40a5d66c2b6e86' - -- Fixed an error in the circuit conversion functions - :func:`.circuit_to_gate` and :func:`.circuit_to_instruction` (and their - associated circuit methods :meth:`.QuantumCircuit.to_gate` and - :meth:`.QuantumCircuit.to_instruction`) when acting on a circuit with - registerless bits, or bits in more than one register. - -.. releasenotes/notes/0.19/fix-control-flow-builder-parameter-copy-b1f6efcc6bc283e7.yaml @ b'd38620a6f399e9108b8ab183c5c31b70c8afcacf' - -- Fixed an issue where calling :meth:`.QuantumCircuit.copy` on the "body" - circuits of a control-flow operation created with the builder interface - would raise an error. For example, this was previously an error, but will - now return successfully:: - - from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister - - qreg = QuantumRegister(4) - creg = ClassicalRegister(1) - circ = QuantumCircuit(qreg, creg) - - with circ.if_test((creg, 0)): - circ.h(0) - - if_else_instruction, _, _ = circ.data[0] - true_body = if_else_instruction.params[0] - true_body.copy() - -.. releasenotes/notes/0.20/add-cx-equivalence-to-cp-and-crz-448c76d5b33516c8.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Added a missing entry from the standard session equivalence library - between :class:`.CXGate` and :class:`.CPhaseGate` as well as between - :class:`~.CXGate` and :class:`~.CRZGate`. - -.. releasenotes/notes/0.20/add-sparsepauliop-equiv-7a8a1420117dba21.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue where running the ``==`` operator between two - :class:`~.SparsePauliOp` objects would raise an error when the two operators - had different numbers of coefficients. For example:: - - op = SparsePauliOp.from_list([("X", 1), ("Y", 1)]) - op2 = SparsePauliOp.from_list([("X", 1), ("Y", 1), ("Z", 0)]) - print(op == op2) - - This would previously raise a ``ValueError`` instead of returning ``False``. - -.. releasenotes/notes/0.20/add-v2-backend-support-in-transpiler-parse-inst-map-a617801850178d05.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed support in :func:`~qiskit.compiler.transpile` for passing a - :class:`~.InstructionScheduleMap` object to the underlying - :class:`~qiskit.transpiler.PassManager` based on the - :class:`~qiskit.transpiler.Target` for - :class:`~qiskit.providers.backend.BackendV2` based backends. Previously, - the :func:`~qiskit.compiler.transpile` function would not do this - processing and any transpiler passes which do not support working with - a :class:`~.Target` object yet would not have access to the default - pulse calibrations for the instructions from a - :class:`~qiskit.providers.backend.BackendV2` backend. - -.. releasenotes/notes/0.20/fix-algorithms-7f1b969e5b2447f9.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~.AmplitudeAmplifier` is now correctly available from the root - :mod:`qiskit.algorithms` module directly. Previously it was not included - in the re-exported classes off the root module and was only accessible - from ``qiskit.algorithms.amplitude_amplifiers``. - Fixed `#7751 `__. - -.. releasenotes/notes/0.20/fix-conditions-fold-mpl-1890dae334f7fbc4.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue with the ``mpl`` backend for the circuit drawer function - :func:`~.circuit_drawer` and the :meth:`.QuantumCircuit.draw` method - where gates with conditions would not display properly when a sufficient - number of gates caused the drawer to fold over to a second row. - Fixed: `#7752 `__. - -.. releasenotes/notes/0.20/fix-hhl_construct_circuit-nl-size-03cbfba9ed50a57a.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue where the :meth:`.HHL.construct_circuit` method under - certain conditions would not return a correct - :class:`~.QuantumCircuit`. Previously, the function had a rounding error in - calculating how many qubits were necessary to represent the eigenvalues - which would cause an incorrect circuit output. - -.. releasenotes/notes/0.20/fix-mitigator-endian-ead88499eb7e12ea.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an endianness bug in :meth:`.BaseReadoutMitigator.expectation_value` - when a string ``diagonal`` was passed. It will now correctly be interpreted - as little endian in the same manner as the rest of Qiskit Terra, instead of - big endian. - -.. releasenotes/notes/0.20/fix-partial_trace-no-systems-0dc2df3007942eb6.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue with the :func:`.quantum_info.partial_trace` when the - function was asked to trace out *no* subsystems, it will now correctly - return the :class:`.DensityMatrix` of the input state with all dimensions - remaining rather than throwing an error. - Fixed `#7613 `__ - -.. releasenotes/notes/0.20/fix-phase-gate-condition-text-display-3e1595ad508d225c.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue with the ``text`` backend for the circuit drawer function - :func:`~.circuit_drawer` and the :meth:`.QuantumCircuit.draw` method - when gates that use side text, such as the :class:`~.CPhaseGate` and - :class:`~.RZZGate` gate classes, with classical conditions set would not - display properly. - Fixed `#7532 `__. - -.. releasenotes/notes/0.20/fix-registerless-bits-reverse-display-ee5efba0eff645a8.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` - function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of - :class:`~qiskit.circuit.QuantumCircuit`. When using the ``reverse_bits`` - option with the ``mpl``, ``latex``, or ``text`` options, bits without - registers did not display in the correct order. - Fixed `#7303 `__. - -.. releasenotes/notes/0.20/fix_local_readout_mitigator_assignment_matrix-8bd4229a5159a7fe.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue in the :meth:`.LocalReadoutMitigator.assignment_matrix` - method where it would previously reject an input value for the - ``qubits`` argument that wasn't a trivial sequence of qubits in the form: - ``[0, 1, 2, ..., n-1]``. This has been corrected so that now any list of - qubit indices to be measured are accepted by the method. - -.. releasenotes/notes/0.20/fix_stabilizerstate_expval-2556c5ee916f5327.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue in the :meth:`.StabilizerState.expectation_value` - method's expectation value calculation, where the output expectation value - would be incorrect if the input :class:`~.Pauli` operator for the ``oper`` - argument had a non-trivial phase. - Fixed `#7441 `__. - -.. releasenotes/notes/0.20/opflow-igate-97df9a8b809116f1.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- An opflow expression containing the Pauli identity ``opflow.I`` no longer - produces an :class:`~qiskit.circuit.library.IGate` when converted to a circuit. - This change fixes a difference in expectation; the identity gate in the circuit indicates - a delay however in opflow we expect a mathematical identity -- meaning no operation at all. - -.. releasenotes/notes/0.20/opflow-igate-97df9a8b809116f1.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The :class:`~qiskit.circuit.library.PauliGate` no longer inserts an - :class:`~qiskit.circuit.library.IGate` for Paulis with the label ``"I"``. - -.. releasenotes/notes/0.20/paulisumop-may-equal-pauliop-af86de94020fba22.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- :class:`.PauliSumOp` equality tests now handle the case when - one of the compared items is a single :class:`.PauliOp`. - For example, ``0 * X + I == I`` now evaluates to True, whereas it was - False prior to this release. - -.. releasenotes/notes/0.20/prepare-0.20-79918ed0fc5b496e.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed an issue with the :class:`~.ALAPSchedule` and :class:`~.ASAPSchedule` - transpiler passes when working with instructions that had custom pulse - calibrations (i.e. pulse gates) set. Previously, the scheduling passes - would not use the duration from the custom pulse calibration for thse - instructions which would result in the an incorrect scheduling being - generated for the circuit. This has been fixed so that now the scheduling - passes will use the duration of the custom pulse calibration for any - instruction in the circuit which has a custom calibration. - -.. releasenotes/notes/0.20/prepare-0.20-79918ed0fc5b496e.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Fixed support for using :class:`~.ParameterExpression` instruction - paramaters in the :class:`~.RZXCalibrationBuilder` transpiler pass. - Previously, if an instruction parameter included a - bound :class:`~.ParameterExpression` the pass would not be able to - handle this correctly. - -.. releasenotes/notes/0.20/qasm-lexer-bugfix-1779525b3738902c.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- Stopped the parser in :meth:`.QuantumCircuit.from_qasm_str` and - :meth:`~.QuantumCircuit.from_qasm_file` from accepting OpenQASM programs - that identified themselves as being from a language version other than 2.0. - This parser is only for OpenQASM 2.0; support for imported circuits from - OpenQASM 3.0 will be added in an upcoming release. - -.. releasenotes/notes/0.20/qasm3-escape-reserved-keywords-60d463db36d96319.yaml @ b'a2d13f55aad6c670f71a4613516b8891e02ece63' - -- The OpenQASM 3 exporter, :class:`.qasm3.Exporter`, will now escape register and - parameter names that clash with reserved OpenQASM 3 keywords by generating - a new unique name. Registers and parameters with the same name will no - longer have naming clashes in the code output from the OpenQASM 3 exporter. - Fixed `#7742 `__. - -Aer 0.10.3 -========== - -No change - -Ignis 0.7.0 -=========== - -No change - -IBM Q Provider 0.18.3 -===================== - -No change - -############# -Qiskit 0.34.2 -############# - -.. _Release Notes_0.19.2: - -Terra 0.19.2 -============ - -.. _Release Notes_0.19.2_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.19/prepare-0.19.2-bfcec925e228a2ad.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -Qiskit Terra 0.19.2 is predominantly a bugfix release, but also now comes -with wheels built for Python 3.10 on all major platforms. - - -.. _Release Notes_0.19.2_New Features: - -New Features ------------- - -.. releasenotes/notes/0.19/py310-support-869d47583c976eef.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Added support for running with Python 3.10. This includes publishing - precompiled binaries to PyPI for Python 3.10 on supported platforms. - - -.. _Release Notes_0.19.2_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.19/py310-support-869d47583c976eef.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Starting from Python 3.10, Qiskit Terra will have reduced support for 32-bit platforms. - These are Linux i686 and 32-bit Windows. These platforms with Python 3.10 - are now at Tier 3 instead of Tier 2 support (per the tiers defined in: - https://qiskit.org/documentation/getting_started.html#platform-support) - This is because the upstream dependencies Numpy and Scipy have dropped - support for them. Qiskit will still publish precompiled binaries for these - platforms, but we're unable to test the packages prior to publishing, and - you will need a C/C++ compiler so that ``pip`` can build their dependencies - from source. If you're using one of these platforms, we recommended that - you use Python 3.7, 3.8, or 3.9. - - -.. _Release Notes_0.19.2_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.19/cvar-paulisumop-fe48698236b77f9b.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed a bug where the :class:`.CVaRMeasurement` attempted to convert a - :class:`.PauliSumOp` to a dense matrix to check whether it were diagonal. - For large operators (> 16 qubits) this computation was extremely expensive and raised - an error if not explicitly enabled using ``qiskit.utils.algorithm_globals.massive = True``. - The check is now efficient even for large numbers of qubits. - -.. releasenotes/notes/0.19/dag-drawer-should-check-filename-existence-4a83418a893717f6.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- :meth:`.DAGCircuit.draw` and the associated function :func:`.dag_drawer` - will now show a more useful error message when the provided filename is not - valid. - -.. releasenotes/notes/0.19/fix-adding-ancilla-register-without-checking-abe367dab5a63dbb.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- :meth:`.QuantumCircuit.add_register` will no longer cause duplicate - :class:`.AncillaQubit` references in a circuit when given an - :class:`.AncillaRegister` whose bits are already present. - -.. releasenotes/notes/0.19/fix-circuit_to_instruction_single-bit-condition-db75291ce921001a.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed conversion of :class:`.QuantumCircuit`\ s with classical conditions on - single, registerless :class:`.Clbit` \s to :class:`~.circuit.Instruction`\ s when - using the :func:`.circuit_to_instruction` function or the - :meth:`.QuantumCircuit.to_instruction` method. For example, the following - will now work:: - - from qiskit.circuit import QuantumCircuit, Qubit, Clbit - - qc = QuantumCircuit([Qubit(), Clbit()]) - qc.h(0).c_if(qc.clbits[0], 0) - qc.to_instruction() - -.. releasenotes/notes/0.19/fix-duplicated-bits-9e72181c9247f934.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Registers will now correctly reject duplicate bits. Fixed `#7446 - `__. - -.. releasenotes/notes/0.19/fix-fake-openpulse2q-15f9c880de52e98f.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- The ``FakeOpenPulse2Q`` mock backend now has T2 times and readout errors - stored for its qubits. These are arbitrary values, approximately consistent - with real backends at the time of its creation. - -.. releasenotes/notes/0.19/fix-lietrotter-2q-61d5cd66e0bf7359.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fix the qubit order of 2-qubit evolutions in the - :class:`.PauliEvolutionGate`, if used with a product formula synthesis. - For instance, before, the evolution of ``IIZ + IZI + IZZ`` - - .. code-block:: python - - from qiskit.circuit.library import PauliEvolutionGate - from qiskit.opflow import I, Z - operator = (I ^ I ^ Z) + (I ^ Z ^ I) + (I ^ Z ^ Z) - print(PauliEvolutionGate(operator).definition.decompose()) - - produced - - .. code-block:: - - ┌───────┐ - q_0: ┤ Rz(2) ├──────── - ├───────┤ - q_1: ┤ Rz(2) ├─■────── - └───────┘ │ZZ(2) - q_2: ──────────■────── - - - whereas now it correctly yields - - .. code-block:: - - ┌───────┐ - q_0: ┤ Rz(2) ├─■────── - ├───────┤ │ZZ(2) - q_1: ┤ Rz(2) ├─■────── - └───────┘ - q_2: ───────────────── - -.. releasenotes/notes/0.19/fix-multi-underscore-display-37b900195ca3d2c5.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed a problem in the ``latex`` and ``mpl`` circuit drawers when register names - with multiple underscores in the name did not display correctly. - -.. releasenotes/notes/0.19/fix-negative-fraction-display-735efdba3b825cba.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Negative numbers in array outputs from the drawers will now appear as - decimal numbers instead of fractions with huge numerators and - denominators. Like positive numbers, they will still be fractions if the - ratio is between small numbers. - -.. releasenotes/notes/0.19/fix-non-global-operation-name-ideal-sim-3dcbc97e29c707c7.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed an issue with the :meth:`.Target.get_non_global_operation_names` - method when running on a target incorrectly raising an exception on targets - with ideal global operations. Previously, if this method was called on a - target that contained any ideal globally defined operations, where the - instruction properties are set to ``None``, this method would raise an - exception instead of treating that instruction as global. - -.. releasenotes/notes/0.19/fix-non-global-operation-name-ideal-sim-3dcbc97e29c707c7.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed an issue with the :func:`~qiskit.compiler.transpile` function where - it could fail when being passed a :class:`.Target` object directly with the - ``target`` kwarg. - -.. releasenotes/notes/0.19/fix-non-global-operation-name-ideal-sim-3dcbc97e29c707c7.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed an issue with the :func:`~qiskit.compiler.transpile` function where - it could fail when the ``backend`` argument was a :class:`.BackendV2` - or a :class:`.Target` via the ``target`` kwarg that contained ideal - globally defined operations. - -.. releasenotes/notes/0.19/fix-path2d-mpl3.4-b1af3a23b408d30a.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed an issue where plotting Bloch spheres could cause an ``AttributeError`` - to be raised in Jupyter or when trying to crop figures down to size with - Matplotlib 3.3 or 3.4 (but not 3.5). For example, the following code would - previously crash with a message:: - - AttributeError: 'Arrow3D' object has no attribute '_path2d' - - but will now succeed with all current supported versions of Matplotlib:: - - from qiskit.visualization import plot_bloch_vector - plot_bloch_vector([0, 1, 0]).savefig("tmp.png", bbox_inches='tight') - -.. releasenotes/notes/0.19/fix-pauli-sum-op-permute-a9b742f3a2fad934.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed a bug in :meth:`.PauliSumOp.permute` where the object on which the - method is called was permuted in-place, instead of returning a permuted - copy. This bug only occured for permutations that left the number of qubits - in the operator unchanged. - -.. releasenotes/notes/0.19/fix-paulievo-inverse-b53a6ecd0ff9a313.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed the :meth:`.PauliEvolutionGate.inverse` method, which previously - computed the inverse by inverting the evolution time. This was only the - correct inverse if the operator was evolved exactly. In particular, this - led to the inverse of Trotterization-based time evolutions being incorrect. - -.. releasenotes/notes/0.19/fix-qi-transpiled-8df449529bf6d9a2.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- The :meth:`.QuantumInstance.execute` method will no longer mutate its input - if it is given a list of circuits. - -.. releasenotes/notes/0.19/fix-qpy-empty-definition-a3a24a0409377a76.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed QPY serialisation of custom instructions which had an explicit no-op - definition. Previously these would be written and subsequently read the - same way as if they were opaque gates (with no given definition). They will - now correctly round-trip an empty definition. For example, the following - will now be correct:: - - import io - from qiskit.circuit import Instruction, QuantumCircuit, qpy_serialization - - # This instruction is explicitly defined as a one-qubit gate with no - # operations. - empty = QuantumCircuit(1, name="empty").to_instruction() - # This instruction will perform some operations that are only known - # by the hardware backend. - opaque = Instruction("opaque", 1, 0, []) - - circuit = QuantumCircuit(2) - circuit.append(empty, [0], []) - circuit.append(opaque, [1], []) - - qpy_file = io.BytesIO() - qpy_serialization.dump(circuit, qpy_file) - qpy_file.seek(0) - new_circuit = qpy_serialization.load(qpy_file)[0] - - # Previously both instructions in `new_circuit` would now be opaque, but - # there is now a correct distinction. - circuit == new_circuit - -.. releasenotes/notes/0.19/fix-quantum-instance-backend-v2-a4e2678fe3ce39d1.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Added a missing :attr:`.BackendV2.provider` attribute to implementations - of the :class:`.BackendV2` abstract class. Previously, :class:`.BackendV2` - backends could be initialized with a provider but that was not accessible - to users. - -.. releasenotes/notes/0.19/fix-quantum-instance-backend-v2-a4e2678fe3ce39d1.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed support for the :class:`.QuantumInstance` class when running with - a :class:`.BackendV2` backend. Previously, attempting to use a - :class:`.QuantumInstance` with a :class:`.BackendV2` would have resulted in - an error. - -.. releasenotes/notes/0.19/fix-vqe-paramorder-if-ansatz-resized-14634a7efff7c74f.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed a bug in :class:`~qiskit.algorithms.VQE` where the parameters of the ansatz were - still explicitly ASCII-sorted by their name if the ansatz was resized. This led to a - mismatched order of the optimized values in the ``optimal_point`` attribute of the result - object. - - In particular, this bug occurred if no ansatz was set by the user and the VQE chose - a default with 11 or more free parameters. - -.. releasenotes/notes/0.19/qasm-lexer-bugfix-1779525b3738902c.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Stopped the parser in :meth:`.QuantumCircuit.from_qasm_str` and - :meth:`~.QuantumCircuit.from_qasm_file` from accepting OpenQASM programs - that identified themselves as being from a language version other than 2.0. - This parser is only for OpenQASM 2.0; support for imported circuits from - OpenQASM 3.0 will be added in an upcoming release. - -.. releasenotes/notes/0.19/qpy-controlflow-97608dbfee5f3e7e.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed QPY serialization of :class:`.QuantumCircuit` objects that contained - control flow instructions. Previously if you attempted to serialize a - circuit containing :class:`.IfElseOp`, :class:`.WhileLoopOp`, or - :class:`.ForLoopOp` the serialization would fail. - Fixed `#7583 `__. - -.. releasenotes/notes/0.19/qpy-controlflow-97608dbfee5f3e7e.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- Fixed QPY serialization of :class:`.QuantumCircuit` containing subsets of - bits from a :class:`.QuantumRegister` or :class:`.ClassicalRegister`. - Previously if you tried to serialize a circuit like this it would - incorrectly treat these bits as standalone :class:`~.circuit.Qubit` or - :class:`.Clbit` without having a register set. For example, if you try to - serialize a circuit like:: - - import io - from qiskit import QuantumCircuit, QuantumRegister - from qiskit.circuit.qpy_serialization import load, dump - - qr = QuantumRegister(2) - qc = QuantumCircuit([qr[0]]) - qc.x(0) - with open('file.qpy', 'wb') as fd: - dump(qc, fd) - - when that circuit is loaded now the registers will be correctly populated - fully even though the circuit only contains a subset of the bits from the - register. - -.. releasenotes/notes/0.19/warn-on-too-large-qft-a2dd60d4a374751a.yaml @ b'6069e5cc01632353972068218c1acfa60f01a119' - -- :class:`.QFT` will now warn if it is instantiated or built with settings - that will cause it to lose precision, rather than raising an - ``OverflowError``. This can happen if the number of qubits is very large - (slightly over 1000) without the approximation degree being similarly large. - The circuit will now build successfully, but some angles might be - indistinguishable from zero, due to limitations in double-precision - floating-point numbers. - -.. _Release Notes_Aer_0.10.3: - -Aer 0.10.3 -========== - -.. _Release Notes_Aer_0.10.3_Prelude: - -Prelude -------- - -.. releasenotes/notes/release-0.10.3-22c93ddf4d160c5f.yaml @ b'326efca12cfcd7341f49ec4e5cf5a5aa769f6c94' - -Qiskit Aer 0.10.3 is mainly a bugfix release, fixing several bugs that -have been discovered since the 0.10.2 release. Howver, this release also -introduces support for running with Python 3.10 including precompiled -binary wheels on all major platforms. This release also includes precompiled -binary wheels for arm64 on macOS. - - -.. _Release Notes_Aer_0.10.3_New Features: - -New Features ------------- - -.. releasenotes/notes/add-py310-b6b1e7a0ed3a8e9d.yaml @ b'e9233eab38ca16d17fce56a0d4dbf7af09b829a8' - -- Added support for running with Python 3.10. This includes publishing - precompiled binaries to PyPI for Python 3.10 on supported platforms. - -.. releasenotes/notes/arm64-macos-wheels-3778e83a8d036168.yaml @ b'c5f63f387fd0837d62195c550334b95b618b1a88' - -- Added support for M1 macOS systems. Precompiled binaries for supported - Python versions >=3.8 on arm64 macOS will now be published on PyPI for this - and future releases. - -.. _Release Notes_Aer_0.10.3_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/add-py310-b6b1e7a0ed3a8e9d.yaml @ b'e9233eab38ca16d17fce56a0d4dbf7af09b829a8' - -- Qiskit Aer no longer fully supports 32 bit platforms on Python >= 3.10. - These are Linux i686 and 32-bit Windows. These platforms with Python 3.10 - are now at Tier 3 instead of Tier 2 support (per the tiers defined in: - https://qiskit.org/documentation/getting_started.html#platform-support) - This is because the upstream dependencies Numpy and Scipy have dropped - support for them. Qiskit will still publish precompiled binaries for these - platforms, but we're unable to test the packages prior to publishing, and - you will need a C/C++ compiler so that ``pip`` can build their dependencies - from source. If you're using one of these platforms, we recommended that - you use Python 3.7, 3.8, or 3.9. - -.. _Release Notes_Aer_0.10.3_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/delay-pass-units-a31341568057fdb3.yaml @ b'0119f3b1f55e2da1444fc89109b93f85833efbf3' - -- Fixes a bug in :class:`.RelaxationNoisePass` where instruction durations - were always assumed to be in *dt* time units, regardless of the actual - unit of the isntruction. Now unit conversion is correctly handled for - all instruction duration units. - - See `#1453 `__ - for details. - -.. releasenotes/notes/fix-local-noise-pass-f94546869a169103.yaml @ b'71e7e53fe9c690d3f9ce194d785610ea8a3c9d8f' - -- Fixes an issue with :class:`.LocalNoisePass` for noise functions that - return a :class:`.QuantumCircuit` for the noise op. These were appended - to the DAG as an opaque circuit instruction that must be unrolled to be - simulated. This fix composes them so that the cirucit instructions are - added to the new DAG and can be simulated without additional unrolling - if all circuit instructions are supported by the simulator. - - See `#1447 `__ - for details. - -.. releasenotes/notes/fix_parallel_diag_fusion-a7f914b3a9f491f7.yaml @ b'bc306537a47cd0116744900538bd543a3520bddd' - -- Multi-threaded transpilations to generate diagonal gates will now work correctly if - the number of gates of a circuit exceeds ``fusion_parallelization_threshold``. - Previously, different threads would occasionally fuse the same element into multiple blocks, - causing incorrect results. - -.. releasenotes/notes/resolve_parameters_before_truncation-ec7074f1f0f831e2.yaml @ b'b086dc6505ad1c116f25c58cc4e29b6ec652bc8b' - -- Fixes a bug with truncation of circuits in parameterized Qobjs. - Previously parameters of parameterized QObj could be wrongly resolved - if unused qubits of their circuits were truncated, because indices of - the parameters were not updated after the instructions on unmeasured qubits - were removed. - - See `#1427 `__ - for details. - -Ignis 0.7.0 -=========== - -No change - -IBM Q Provider 0.18.3 -===================== - -No change - -############# -Qiskit 0.34.1 -############# - -Terra 0.19.1 -============ - -No change - -.. _Release Notes_0.10.2_Aer: - -Aer 0.10.2 -=========== - -.. _Release Notes_0.10.2_Aer_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/fix-for-loop-no-parameter-aa5b04b1da0e956b.yaml @ b'c4a97d4e02baebd22497e3a3f6e83bdcf35fbb6a' - -- Fixed simulation of ``for`` loops where the loop parameter was not used in - the body of the loop. For example, previously this code would fail, but - will now succeed: - - .. code-block:: python - - import qiskit - from qiskit.providers.aer import AerSimulator - - qc = qiskit.QuantumCircuit(2) - with qc.for_loop(range(4)) as i: - qc.h(0) - qc.cx(0, 1) - - AerSimulator(method="statevector").run(qc) - -.. releasenotes/notes/fix_qerror_to_dict-13a7683ac4adddd4.yaml @ b'cb17b3fd547eb54b7b48f1c3e959ec2c3dabab6a' - -- Fixes a bug in :meth:`.QuantumError.to_dict` where N-qubit circuit - instructions where the assembled instruction always applied to - qubits ``[0, ..., N-1]`` rather than the instruction qubits. This - bug also affected device and fake backend noise models. - - See `Issue 1415 `__ - for details. - -Ignis 0.7.0 -=========== - -No change - -IBM Q Provider 0.18.3 -===================== - -No change - -############# -Qiskit 0.34.0 -############# - -Qiskit 0.34.0 includes a point release of Qiskit Aer: version 0.10.1, which -patches performance regressions in version 0.10.0 that were discovered -immediately post-release. See below for the release notes for both Qiskit Aer -0.10.0 and 0.10.1. - -Terra 0.19.1 -============ - -No change - -.. _Release Notes_Aer_0.10.1: - -Aer 0.10.1 -========== - -.. _Release Notes_Aer_0.10.1_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.10/0-10-1-release-6338690271374e16.yaml @ b'0ca6d1a3681110122c2f0c069806422248afef17' - -The Qiskit Aer 0.10.1 patch fixes performance regressions introduced in Qiskit Aer 0.10.0. - - -.. _Release Notes_Aer_0.10.1_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.10/0-10-1-release-6338690271374e16.yaml @ b'0ca6d1a3681110122c2f0c069806422248afef17' - -- Fix performance regression in noisy simulations due to large increase in - serialization overhead for loading noise models from Python into C++ - resulting from unintended nested Python multiprocessing calls. - See `issue 1407 `__ - for details. - -.. _Release Notes_Aer_0.10.0: - -Aer 0.10.0 -========== - -.. _Release Notes_Aer_0.10.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -The Qiskit Aer 0.10 release includes several performance and noise model -improvements. Some highlights are: - -* Improved performance for parallel shot GPU and HPC simulations -* Support for simulation of circuits containing QASM 3.0 control-flow instructions -* Support for relaxation noise on scheduled circuits in backend noise models -* Support of user-created transpiler passes for defining custom gate errors and - noise models, and inserting them into circuits. - - -.. _Release Notes_Aer_0.10.0_New Features: - -New Features ------------- - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added support of QASM 3.0 control-flow instructions introduced in Qiskit-Terra - 0.19.0. Supported instructions are :class:`~qiskit.circuit.ForLoopOp`, - :class:`~qiskit.circuit.WhileLoopOp`, :class:`~qiskit.circuit.ContinueLoopOp`, - :class:`~qiskit.circuit.BreakLoopOp`, :class:`~qiskit.circuit.IfElseOp`. - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added a batched-shot simulation optimization for GPU simulations. This - optional feature will use available memory on 1 or more GPUs to run multiple - simulation shots in parallel for greatly improved performance on - multi-shot simulations with noise models and/or intermediate measurements. - - This option is enabled by default when using ``device="GPU"`` and a - simulation ``method`` of either ``"statevector"`` or ``"density_matrix"`` - with the :class:`~qiskit.providers.aer.AerSimulator`. It can be disabled by - setting ``batched_shots_gpu=False`` in the simulator options. - - This optimization is most beneficial for small to medium numbers of qubits - where there is sufficient GPU memory to run multiple simulations in - parallel. The maximum number of active circuit qubits for enabling this - optimization can be configured using the ``batch_shots_gpu_max_qubits`` - simulator option. The default value of this option is 16. - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added the new ``max_shot_size`` option to a custom executor for - running multiple shots of a noisy circuit in parallel. - - For example configuring ``max_shot_size`` with a custom executor:: - - backend = AerSimulator( - max_shot_size=1, max_job_size=1, executor=custom_executor) - job = backend.run(circuits) - - will split the shots of a noisy circuit into multiple circuits. - After all individual shots have finished executing, the job results - are automatically combined into a single :class:`~qiskit.result.Result` - object that is returned by ``job.result()``. - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added the ``mps_swap_direction`` simulator option that allows the user to determine - the direction of internal swaps, when they are inserted for a - 2-qubit gate. Possible values are ``"mps_swap_right"`` and ``"mps_swap_left"``. - The direction of the swaps may affect performance, depending on the circuit. - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Implemented a new measurement sampling optimization for the - ``"matrix_product_state"`` simulation method of the - :class:`~qiskit.providers.aer.AerSimulator`. Currently this algorithm - is used only when all qubits are measured and when the simulator - ``mps_sample_measure_algorithm`` simulator option is set to ``"mps_probabilities"``. - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Improved the performance of the measure instruction for the ``"matrix_product_state"`` - simulation method of the :class:`~qiskit.providers.aer.AerSimulator`. - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added a :class:`~qiskit.providers.aer.library.SaveClifford` instruction for - saving the state of the stabilizer simulation method as a - :class:`~qiskit.quantum_info.Clifford` object. - - Note that this instruction is essentially equivalent to the - :class:`~qiskit.providers.aer.library.SaveStabilizer` instruction, however - that instruction will return the saved state as a - :class:`~qiskit.quantum_info.StabilizerState` object instead of a - :class:`~qiskit.quantum_info.Clifford` object. - -.. releasenotes/notes/0.10/add-noise-passes-1cb52b57a6d2294d.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added two transpiler passes for inserting instruction-dependent quantum - errors into circuits: - - * :class:`qiskit.providers.aer.noise.LocalNoisePass` - * :class:`qiskit.providers.aer.noise.RelaxationNoisePass` - - The :class:`~qiskit.providers.aer.noise.LocalNoisePass` pass can - be used to implement custom parameterized noise models by defining a - noise generating function of the form - - .. code-block:: python - - def fn( - inst: Instruction, - qubits: Optional[List[int]] = None, - ) -> InstructionLike - - which returns a noise instruction (eg. a :class:`.QuantumError` or other instruction) - that can depend on any properties or parameters of the instruction and - qubit arguements. - - This function can be applied to all instructions in a circuit, or a - specified subset (See the - :class:`~qiskit.providers.aer.noise.LocalNoisePass` documentation - for additional details.) - - The :class:`~qiskit.providers.aer.noise.RelaxationNoisePass` - is a special case of the - :class:`~qiskit.providers.aer.noise.LocalNoisePass` using a - predefined noise function that returns a tensor product of - :func:`~qiskit.providers.aer.noise.thermal_relaxation_error` on each - qubit in an instruction, dependent on the instruction's duration and - the supplied relaxation time constant parameters of the pass. - -.. releasenotes/notes/0.10/add-noise-passes-1cb52b57a6d2294d.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- The basic device noise model implemented by - :meth:`.NoiseModel.from_backend` and - :meth:`.AerSimulator.from_backend` has been - upgraded to allow adding duration-dependent relaxation errors on - circuit delay gates using the - :class:`~qiskit.providers.aer.noise.RelaxationNoisePass`. - - To enable this noise when running noisy simulations you must first - schedule your circuit to insert scheduled delay instructions as - follows: - - .. code-block:: python - - backend = AerSimulator.from_backend(ibmq_backend) - scheduled_circuit = qiskit.transpile( - circuit, backend=backend, scheduling_method='asap') - result = backend.run(scheduled_circuit).result() - - If the circuit is transpiled without being scheduled (and also - contains no delay instructions) the noisy simulation will not include - the effect of delay relaxation errors. In this case the simulation - will be equivalent to the previous qiskit-aer 0.9 simulation where - relaxation noise is only added to gate instructions based on their - duration as obtained from the backend properties. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- The constructor of :class:`~qiskit.providers.aer.noise.QuantumError` now - accepts several new types of input as ``noise_ops`` argument, for example: - - .. code-block:: python - - import numpy as np - - from qiskit import QuantumCircuit - from qiskit.circuit.library import IGate, XGate, Reset - from qiskit.quantum_info import Kraus - from qiskit.providers.aer.noise import QuantumError - - # Quantum channels - kraus = Kraus([ - np.array([[1, 0], [0, np.sqrt(1 - 0.9)]], dtype=complex), - np.array([[0, 0], [0, np.sqrt(0.9)]], dtype=complex) - ]) - print(QuantumError(kraus)) - - # Construction from a QuantumCircuit - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - error = QuantumError(qc) - - # Construction from a tuple of (Instruction, List[int]), where the list of - # integers represents the qubits. - error = QuantumError((Reset(), [0])) - - # Construction from an iterable of objects in the same form as above, but - # where each also has an associated probability. - error = QuantumError([ - ((IGate(), [0]), 0.9), - ((XGate(), [0]), 0.1), - ]) - - # A short-hand for the iterable form above, where the qubits are implicit, - # and each instruction is over all qubits. - error = QuantumError([(IGate(), 0.9), (XGate(), 0.1)]) - - Note that the original JSON-based input format is deperecated. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added a utility function :func:`qiskit.providers.aer.utils.transform_noise_model` - for constructing a noise model by applying a supplied function to all - :class:`~qiskit.providers.aer.noise.QuantumError`\ s in the noise model. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added two utility functions - :func:`qiskit.providers.aer.utils.transpile_quantum_error` and - :func:`qiskit.providers.aer.utils.transpile_noise_model` for transpiling - the circuits contained in :class:`~qiskit.providers.aer.noise.QuantumError`, - and all errors in a :class:`~qiskit.providers.aer.noise.NoiseModel`. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Added the ability to add :class:`~qiskit.providers.aer.noise.QuantumError` - objects directly to a :class:`.QuantumCircuit` without converting - to a :class:`~qiskit.quantum_info.Kraus` instruction. - - Circuits containing quantum errors can now be run on the - :class:`~qiskit.providers.aer.AerSimulator` and - :class:`~qiskit.providers.aer.QasmSimulator` simulators as an alternative - to, or in addition to, building a - :class:`~qiskit.providers.aer.noise.NoiseModel` for defining noisy circuit - instructions. - - Example:: - - from qiskit import QuantumCircuit - from qiskit.providers.aer import AerSimulator - from qiskit.providers.aer.noise import pauli_error - - error_h = pauli_error([('I', 0.95), ('X', 0.05)]) - error_cx = pauli_error([('II', 0.9), ('XX', 0.1)]) - - qc = QuantumCircuit(3) - qc.h(0) - qc.append(error_h, [0]) - qc.cx(0, 1) - qc.append(error_cx, [0, 1]) - qc.cx(0, 2) - qc.append(error_cx, [0, 2]) - qc.measure_all() - - backend = AerSimulator(method='stabilizer') - result = backend.run(qc).result() - result.get_counts(0) - - Circuits containing quantum errors can also be evaluated using - the :mod:`~qiskit.quantum_info` quantum channel and - :class:`~qiskit.quantum_info.DensityMatrix` classes. - - -.. _Release Notes_Aer_0.10.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- The return type of several save instructions have been changed to be the - corresponding Qiskit Terra classes rather than raw NumPy arrays or - dictionaries. The types that have changed are - - * :func:`.save_statevector` now returns as a - :class:`~qiskit.quantum_info.Statevector` - * :func:`.save_density_matrix` now returns as a - :class:`~qiskit.quantum_info.DensityMatrix` - * :func:`.save_stabilizer` now returns as - :class:`~qiskit.quantum_info.StabilizerState` - * :func:`.save_unitary` now returns as - :class:`~qiskit.quantum_info.Operator` - * :func:`.save_superop` now returns as - :class:`~qiskit.quantum_info.SuperOp` - * :func:`.save_probabilities_dict` now returns as a - :class:`~qiskit.result.ProbDistribution` - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Changed the default value of ``standard_gates`` to ``None`` for all functions - in :mod:`qiskit.providers.aer.noise.errors.standard_errors` as - those functions are updated so that they use standard gates by default. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- When an unsupported argument is supplied to :func:`.approximate_quantum_error`, - it will now raise a :class:`.NoiseError` instead of a ``RuntimeError``. - - -.. _Release Notes_Aer_0.10.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.10/0-10-release-8c37dadcc1c82fcc.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Using NumPy ``ndarray`` methods and attributes on the return type of - :func:`.save_statevector`, :func:`.save_density_matrix`, - :func:`.save_unitary`, and :func:`.save_superop` has been deprecated, and - will stop working in a future release. - These instructions now return :mod:`qiskit.quantum_info` classes for their - return types. Partial backwards compatability with treating these objects as - NumPy arrays is implemented by forwarding methods to the internal array - during the deprecation period. - -.. releasenotes/notes/0.10/add-noise-passes-1cb52b57a6d2294d.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Passing in a :class:`.BackendProperties` object for the ``backend`` argument of - :meth:`.NoiseModel.from_backend` has been deprecated, as it is incompatible - with duration dependent delay noises, and will be removed in a future release. - Pass in a Qiskit Terra :class:`.BackendV1` object instead. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Deprecated the ``number_of_qubits`` option of the :class:`.QuantumError` - constructor in favor of automatic determination of the dimension. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Deprecated the ``standard_gates`` option of the :class:`.QuantumError` - constructor in favor of externalizing such basis-change functionality. - In many cases, you can transform any error into an error defined - only with specific gates using :func:`.approximate_quantum_error`. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Deprecated the ``standard_gates`` option of all functions in - :mod:`qiskit.providers.aer.noise.errors.standard_errors` - in favor of returning errors in the form of a mixture of standard gates - as much as possible by default. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Deprecated all functions in :mod:`~qiskit.providers.aer.noise.errors.errorutils` - because they are helper functions meant to be used only for implementing - functions in :mod:`qiskit.providers.aer.noise.errors.standard_errors` and - they should have been provided as private functions. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Deprecated the ``standard_gates`` option of :meth:`.NoiseModel.from_backend` - in favor of externalizing such basis-change functionality. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Deprecated :meth:`.NoiseModel.from_dict` to make the noise model - independent of Qobj (JSON) format. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Deprecated all public variables, functions and classes in - :mod:`qiskit.providers.aer.noise.utils.noise_transformation` except for - :func:`.approximate_quantum_error` and :func:`.approximate_noise_model`, - because they are helper functions meant to be used only for implementing the - ``approximate_*`` functions and they should have been provided as private functions. - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Deprecated :func:`.remap_noise_model` since the C++ code now automatically - truncates and remaps noise models if it truncates circuits. - - -.. _Release Notes_Aer_0.10.0_Other Notes: - -Other Notes ------------ - -.. releasenotes/notes/0.10/refactor-noise-bab93a76677ba822.yaml @ b'61b028b7ccd1d6e96c8de48a10648c0bc3c07ff9' - -- Changes in the implementation of the function :func:`.approximate_quantum_error` - may change the resulting approximate error compared to Qiskit Aer 0.9. - -Ignis 0.7.0 -=========== - -No change - -.. _Release Notes_0.18.3_IBMQ: - -IBM Q Provider 0.18.3 -===================== - -.. _Release Notes_0.18.3_IBMQ_Bug Fixes: - -Bug Fixes ---------- - -- Fix delivered in `#1100 `__ for - an issue with JSON encoding and decoding when using - ``ParameterExpression``\ s in conjunction with Qiskit Terra 0.19.1 and - above. Previously, the ``Parameter`` instances reconstructed from the JSON - output would have different unique identifiers, causing them to seem unequal - to the input. They will now have the correct backing identities. - -############# -Qiskit 0.33.1 -############# - -.. _Release Notes_0.19.1_Terra: - -Terra 0.19.1 -============ - -.. _Release Notes_0.19.1_Terra_Prelude: - -Prelude -------- - -.. releasenotes/notes/prepare-0.19.1-37d14fd5cf05a576.yaml @ b'ee0d76052411230848ab2830c5741c14c2450439' - -Qiskit Terra 0.19.1 is a bugfix release, solving some issues in 0.19.0 -concerning circuits constructed by the control-flow builder interface, -conditional gates and QPY serialisation of newer Terra objects. - - -.. _Release Notes_0.19.1_Terra_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/reinstate-deprecate-loose-measure-reset-11591e35d350aaeb.yaml @ b'a9b6093551e0a6e6000fa2230c8182c7e0080dc5' - -- The loose functions ``qiskit.circuit.measure.measure()`` and - ``qiskit.circuit.reset.reset()`` are deprecated, and will be removed in a - future release. Instead, you should access these as methods on - :class:`.QuantumCircuit`:: - - from qiskit import QuantumCircuit - circuit = QuantumCircuit(1, 1) - - # Replace this deprecated form ... - from qiskit.circuit.measure import measure - measure(circuit, 0, 0) - - # ... with either of the next two lines: - circuit.measure(0, 0) - QuantumCircuit.measure(circuit, 0, 0) - - -.. _Release Notes_0.19.1_Terra_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.19/fix-circuit-conversion-loose-qubits-8d190426e4e892f1.yaml @ b'ee0d76052411230848ab2830c5741c14c2450439' - -- Fixed an error in the circuit conversion functions - :func:`.circuit_to_gate` and :func:`.circuit_to_instruction` (and their - associated circuit methods :meth:`.QuantumCircuit.to_gate` and - :meth:`.QuantumCircuit.to_instruction`) when acting on a circuit with - registerless bits, or bits in more than one register. Previously, the - number of bits necessary for the created gate or instruction would be - calculated incorrectly, often causing an exception during the conversion. - -.. releasenotes/notes/0.19/fix-control-flow-builder-parameter-copy-b1f6efcc6bc283e7.yaml @ b'7df86762371a5fb69c56470e414ed3679de2384b' - -- Fixed an issue where calling :meth:`.QuantumCircuit.copy` on the "body" - circuits of a control-flow operation created with the builder interface - would raise an error. For example, this was previously an error, but will - now return successfully:: - - from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister - - qreg = QuantumRegister(4) - creg = ClassicalRegister(1) - circ = QuantumCircuit(qreg, creg) - - with circ.if_test((creg, 0)): - circ.h(0) - - if_else_instruction, _, _ = circ.data[0] - true_body = if_else_instruction.params[0] - true_body.copy() - -.. releasenotes/notes/fix-circuit-builder-registers-21deba9a43356fb5.yaml @ b'188e9ecfdce2a1bb2262aeb9cbf5e8c94450064b' - -- The control-flow builder interface now supports using :class:`.ClassicalRegister`\ s - as conditions in nested control-flow scopes. Previously, doing this would - not raise an error immediately, but the internal circuit blocks would not - have the correct registers defined, and so later logic that worked with the - inner blocks would fail. - - For example, previously the drawers would fail when trying to draw an inner - block conditioned on a classical register, whereas now it will succeed, such - as in this example:: - - from qiskit import QuantumCircuit - from qiskit.circuit import QuantumRegister, ClassicalRegister - - qreg = QuantumRegister(4) - creg = ClassicalRegister(1) - circ = QuantumCircuit(qreg, creg) - - with circ.for_loop(range(10)) as a: - circ.ry(a, 0) - with circ.if_test((creg, 1)): - circ.break_loop() - - print(circ.draw(cregbundle=False)) - print(circ.data[0][0].blocks[0].draw(cregbundle=False)) - -.. releasenotes/notes/fix-paramater-vector-qpy-52b16ccefecf8b2e.yaml @ b'76a54747df03c359744f1934dcc7f948715faf80' - -- Fixed :mod:`~qiskit.circuit.qpy_serialization` support for - serializing :class:`~qiskit.circuit.QuantumCircuit` objects that are - using :class:`.ParameterVector` or :class:`.ParameterVectorElement` as - parameters. Previously, a :class:`.ParameterVectorElement` parameter was - just treated as a :class:`.Parameter` for QPY serialization which meant - the :class:`.ParameterVector` context was lost in QPY and the output - order of :attr:`~qiskit.circuit.QuantumCircuit.parameters` could be - incorrect. - - To fix this issue a new QPY format version, :ref:`qpy_version_3`, was required. - This new format version includes a representation of the - :class:`~qiskit.circuit.ParameterVectorElement` class which is - described in the :mod:`~qiskit.circuit.qpy_serialization` documentation at - :ref:`qpy_param_vector`. - -.. releasenotes/notes/fix-pauli-evolution-gate-bf85592f0f8f0ba7.yaml @ b'73024df2f62b0f8c9fd2e439a7bbeba2d8b0aaa9' - -- Fixed the :mod:`~qiskit.circuit.qpy_serialization` support for serializing - a :class:`~qiskit.circuit.library.PauliEvolutionGate` object. Previously, - the :class:`~qiskit.circuit.library.PauliEvolutionGate` was treated as - a custom gate for serialization and would be deserialized as a - :class:`~qiskit.circuit.Gate` object that had the same definition and - name as the original :class:`~qiskit.circuit.library.PauliEvolutionGate`. - However, this would lose the original state from the - :class:`~qiskit.circuit.library.PauliEvolutionGate`. This has been fixed - so that starting in this release a - :class:`~qiskit.circuit.library.PauliEvolutionGate` in the circuit will - be preserved 1:1 across QPY serialization now. The only limitation with - this is that it does not support custom - :class:`~qiskit.synthesis.EvolutionSynthesis` classes. Only the classes - available from :mod:`qiskit.synthesis` can be used with a - :class:`~qiskit.circuit.library.PauliEvolutionGate` for qpy serialization. - - To fix this issue a new QPY format version, :ref:`qpy_version_3`, was required. - This new format version includes a representation of the - :class:`~qiskit.circuit.library.PauliEvolutionGate` class which is - described in the :mod:`~qiskit.circuit.qpy_serialization` documentation at - :ref:`pauli_evo_qpy`. - -.. releasenotes/notes/reinstate-deprecate-loose-measure-reset-11591e35d350aaeb.yaml @ b'a9b6093551e0a6e6000fa2230c8182c7e0080dc5' - -- Two loose functions ``qiskit.circuit.measure.measure()`` and - ``qiskit.circuit.reset.reset()`` were accidentally removed without a - deprecation period. They have been reinstated, but are marked as deprecated - in favour of the methods :meth:`.QuantumCircuit.measure` and - :meth:`.QuantumCircuit.reset`, respectively, and will be removed in a future - release. - - -.. _Release Notes_0.19.1_Terra_Other Notes: - -Other Notes ------------ - -.. releasenotes/notes/fix-circuit-builder-registers-21deba9a43356fb5.yaml @ b'188e9ecfdce2a1bb2262aeb9cbf5e8c94450064b' - -- The new control-flow builder interface uses various context managers and - helper objects to do its work. These should not be considered part of the - public API, and are liable to be changed and removed without warning. The - *usage* of the builder interface has stability guarantees, in the sense that - the behaviour described by :meth:`.QuantumCircuit.for_loop`, - :meth:`~.QuantumCircuit.while_loop` and :meth:`~.QuantumCircuit.if_test` for - the builder interface are subject to the standard deprecation policies, but - the actual objects used to effect this are not. You should not rely on the - objects (such as ``IfContext`` or ``ControlFlowBuilderBlock``) existing in - their current locations, or having any methods or attributes attached to - them. - - This was not previously clear in the 0.19.0 release. All such objects now - have a warning in their documentation strings making this explicit. It is - likely in the future that their locations and backing implementations will - become quite different. - -Aer 0.9.1 -========= - -No change - -Ignis 0.7.0 -=========== - -No change - -.. _Release Notes_0.18.2_IBMQ: - -IBM Q Provider 0.18.2 -===================== - -.. _Release Notes_0.18.2_IBMQ_Bug Fixes: - -Bug Fixes ---------- - -- Fix delivered in `#1065 `__ for the - issue where job kept crashing when ``Parameter`` was passed in circuit metadata. - -- Fix delivered in `#1094 `__ for - the issue wherein :class:`qiskit.providers.ibmq.runtime.RuntimeEncoder` - does an extra `decompose()` if the circuit being serialized is a ``BlueprintCircuit``. - -############# -Qiskit 0.33.0 -############# - -This release officially marks the end of support for the Qiskit Aqua project -in Qiskit. It was originally deprecated in the 0.25.0 release and as was documented -in that release the ``qiskit-aqua`` package has been removed from the Qiskit -metapackage, which means ``pip install qiskit`` will no -longer include ``qiskit-aqua``. However, because of limitations in python -packaging we cannot automatically remove a pre-existing install of ``qiskit-aqua`` -when upgrading a previous version of Qiskit to this release (or a future release) -with ``pip install -U qiskit``. If you are upgrading from a previous version it's -recommended that you manually uninstall Qiskit Aqua with -``pip uninstall qiskit-aqua`` or install in a fresh python environment. - -The application modules that were provided by ``qiskit-aqua`` have been split into -several new packages: -``qiskit-optimization``, ``qiskit-nature``, ``qiskit-machine-learning``, and -``qiskit-finance``. These packages can be installed by themselves (via the -standard pip install command, e.g. ``pip install qiskit-nature``) or with the -rest of the Qiskit metapackage as optional extras (e.g. -``pip install 'qiskit[finance,optimization]'`` or ``pip install 'qiskit[all]'``). -The core algorithms and the operator flow now exist as part of Qiskit Terra at -``qiskit.algorithms`` and ``qiskit.opflow``. Depending on your existing -usage of Aqua you should either use the application packages or the new modules -in Qiskit Terra. For more details on how to migrate from Qiskit Aqua you can -refer to the -`Aqua Migration Guide `__. - -This release also officially deprecates the Qiskit Ignis project. Accordingly, in a -future release the ``qiskit-ignis`` package will be removed from the Qiskit -metapackage, which means in that future release ``pip install qiskit`` will no -longer include ``qiskit-ignis``. Qiskit Ignis has been supersceded by the -`Qiskit Experiments `__ project and active -development has ceased. While deprecated, critical bug fixes and compatibility fixes will -continue to be made to provide users a sufficient opportunity to migrate off of Ignis. After the -deprecation period (which will be no shorter than 3 months from this release) the project will be -retired and archived. You can refer to the -`migration guide `__ for details on how to -switch from Qiskit Ignis to Qiskit Experiments. - -.. _Release Notes_0.19.0: - -Terra 0.19.0 -============ - -.. _Release Notes_0.19.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.19/0.19-prelude-65c295aa9497ed48.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -The Qiskit Terra 0.19 release highlights are: - -* A new version of the abstract Qiskit/hardware interface, in the form of - :class:`.BackendV2`, which comes with a new data structure - :class:`~.transpiler.Target` to allow backends to better model their - constraints for the :ref:`transpiler `. - -* An :ref:`extensible plugin interface ` to the - :class:`~.passes.UnitarySynthesis` transpiler pass, allowing users or - other packages to extend Qiskit Terra's - synthesis routines with new methods. - -* Control-flow instructions, for representing ``for`` and ``while`` loops - and ``if``/``else`` statements in :class:`.QuantumCircuit`. The - simulators in Qiskit Aer will soon be able to work with these new - instructions, allowing you to write more dynamic quantum programs. - -* Preliminary support for the evolving `OpenQASM 3 specification`_. You can - use the new :mod:`qiskit.qasm3` module to serialize your - :class:`.QuantumCircuit`\ s into OpenQASM 3, including the new control-flow - constructs. - -.. _OpenQASM 3 specification: https://openqasm.com/ - -This release marks the end of support for Python 3.6 in Qiskit. This -release of Qiskit Terra, and any subsequent bugfix releases in the 0.19.x -series, will be the last to work with Python 3.6. Starting from the next -minor release (0.20.0) of Qiskit Terra, the minimum required Python version -will be 3.7. - -As always, there are many more features and fixes in this release as well, -which you can read about below. - - -.. _Release Notes_0.19.0_New Features: - -New Features ------------- - -.. releasenotes/notes/0.19/QuantumCircuit.decompose-takes-which-gate-to-decompose-d857da5d0c41fb66.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- :meth:`.QuantumCircuit.decompose` and its corresponding transpiler pass - :class:`~qiskit.transpiler.passes.Decompose` now optionally accept a - parameter containing a collection of gate names. If this parameter is given, - then only gates with matching names will be decomposed. This supports - Unix-shell-style wildcard matches. For example:: - - qc.decompose(["h", "r[xz]"]) - - will decompose any ``h``, ``rx`` or ``rz`` gates, but leave (for example) ``x`` gates untouched. - -.. releasenotes/notes/0.19/SPSA-termination-callback-a1ec14892f553982.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the ``termination_checker`` argument to the :class:`~qiskit.algorithms.optimizers.SPSA` optimizer. - This allows the user to implement a custom termination criterion. - - .. code-block:: python - - import numpy as np - from qiskit.algorithms.optimizers import SPSA - - def objective(x): - return np.linalg.norm(x) + .04*np.random.rand(1) - - class TerminationChecker: - - def __init__(self, N : int): - """ - Callback to terminate optimization when the average decrease over - the last N data points is smaller than the specified tolerance. - """ - self.N = N - self.values = [] - - def __call__(self, nfev, parameters, value, stepsize, accepted) -> bool: - """ - Returns: - True if the optimization loop should be terminated. - """ - self.values.append(value) - - if len(self.values) > self.N: - last_values = self.values[-self.N:] - pp = np.polyfit(range(self.N), last_values, 1) - slope = pp[0] / self.N - - if slope > 0: - return True - return False - - maxiter = 400 - spsa = SPSA(maxiter=maxiter, termination_checker=TerminationChecker(10)) - parameters, value, niter = spsa.optimize(2, objective, initial_point=np.array([0.5, 0.5])) - -.. releasenotes/notes/0.19/add-backend-v2-ce84c976fb13b038.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new version of the :class:`~qiskit.providers.Backend` interface, - :class:`~qiskit.providers.BackendV2`. This new version is a large change - from the previous version, :class:`~qiskit.providers.BackendV1` and - changes both the user access pattern for properties of the backend (like - number of qubits, etc) and how the backend represents its constraints - to the transpiler. The execution of circuits (via the - :meth:`~qiskit.providers.BackendV2.run` method) remains unchanged. With - a :class:`~qiskit.providers.BackendV2` backend instead of having a separate - :meth:`~qiskit.providers.BackendV1.configuration`, - :meth:`~qiskit.providers.BackendV1.properties`, and - :meth:`~qiskit.providers.BackendV1.defaults` methods that construct - :class:`~qiskit.providers.models.BackendConfiguration`, - :class:`~qiskit.providers.models.BackendProperties`, and - :class:`~qiskit.providers.models.PulseDefaults` objects respectively, - like in the :class:`~qiskit.providers.BackendV1` interface, the attributes - contained in those output objects are accessible directly as attributes of - the :class:`~qiskit.providers.BackendV2` object. For example, to get the - number of qubits for a backend with :class:`~qiskit.providers.BackendV1` - you would do:: - - num_qubits = backend.configuration().n_qubits - - while with :class:`~qiskit.providers.BackendV2` it is:: - - num_qubits = backend.num_qubits - - The other change around this is that the number of attributes exposed in - the abstract :class:`~qiskit.providers.BackendV2` class is designed to be - a hardware/vendor agnostic set of the required or optional fields that the - rest of Qiskit can use today with any backend. Subclasses of the abstract - :class:`~qiskit.providers.BackendV2` class can add support for additional - attributes and methods beyond those defined in - :class:`~qiskit.providers.BackendV2`, but these will not be supported - universally throughout Qiskit. - - The other critical change that is primarily important for provider authors is - how a :class:`~qiskit.providers.BackendV2` exposes the properties of - a particular backend to the transpiler. With - :class:`~qiskit.providers.BackendV2` this is done via a - :class:`~qiskit.transpiler.Target` object. The - :class:`~qiskit.transpiler.Target`, which is exposed via the - :attr:`~qiskit.providers.BackendV2.target` attribute, is used to represent - the set of constraints for running circuits on a particular backend. It - contains the subset of information previously exposed by the - :class:`~qiskit.providers.models.BackendConfiguration`, - :class:`~qiskit.providers.models.BackendProperties`, and - :class:`~qiskit.providers.models.PulseDefaults` classes which the transpiler - can actively use. When migrating a provider to use - :class:`~qiskit.providers.BackendV2` (or when creating a new provider - package) the construction of backend objects will primarily be around - creating a :class:`~qiskit.transpiler.Target` object for the backend. - -.. releasenotes/notes/0.19/add-backend-v2-ce84c976fb13b038.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new :class:`~qiskit.transpiler.Target` class to the - :mod:`~qiskit.transpiler` module. The :class:`~qiskit.transpiler.Target` - class is designed to represent the constraints of backend to the compiler. - The :class:`~qiskit.transpiler.Target` class is intended to be used - with a :class:`~qiskit.providers.BackendV2` backend and is how backends - will model their constraints for the transpiler moving forward. It combines - the previously distinct fields used for controlling the - :func:`~qiskit.compiler.transpile` target device (e.g. ``basis_gates``, - ``coupling_map``, ``instruction_durations``, etc) into a single data - structure. It also adds additional functionality on top of what was - available previously such as representing heterogeneous gate sets, - multi-qubit gate connectivity, and tuned variants of the same gates. - Currently the transpiler doesn't factor in all these constraints, but - over time it will grow to leverage the extra functionality. - -.. releasenotes/notes/0.19/add-backend-v2-ce84c976fb13b038.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :class:`~qiskit.providers.Options` class now has optional support for - specifying validators. This enables :class:`~qiskit.providers.Backend` - authors to optionally specify basic validation on the user supplied values - for fields in the :class:`~qiskit.providers.Options` object. For example, - if you had an :class:`~qiskit.providers.Options` object defined with:: - - from qiskit.providers.Options - options = Options(shots=1024) - - you can set a validator on shots for it to be between 1 and 4096 with:: - - options.set_validator('shots', (1, 4096)) - - With the validator set any call to the - :meth:`~qiskit.providers.Options.update_options` method will check that - if ``shots`` is being updated the proposed new value is within the valid - range. - -.. releasenotes/notes/0.19/add-contains_instruction-pass-dcad5f1978ee1e24.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new transpiler analysis pass, - :class:`~qiskit.transpiler.passes.ContainsInstruction`, to the - :mod:`qiskit.transpiler.passes` module. This pass is used to determine - if a circuit contains a specific instruction. It takes in a single - parameter at initialization, the name of the instruction to check for - and set a boolean in the property set whether the circuit contains that - instruction or not. For example:: - - from qiskit.transpiler.passes import ContainsInstruction - from qiskit.circuit import QuantumCircuit - - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.cx(0, 1) - circuit.measure_all() - - property_set = {} - # Contains Hadamard - contains_h = ContainsInstruction("h") - contains_h(circuit, property_set) - assert property_set["contains_h"] == True - # Not contains SX - contains_sx = ContainsInstruction("sx") - contains_sx(circuit, property_set) - assert property_set["contains_sx"] == False - -.. releasenotes/notes/0.19/add-detach-prefix-088e96b88ba29927.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a utility function :func:`qiskit.utils.detach_prefix` that is a - counterpart of :func:`~qiskit.utils.apply_prefix`. The new function returns - a tuple of scaled value and prefix from a given float value. For example, a - value ``1.3e8`` will be converted into ``(130, "M")`` that can be used to - display a value in the user friendly format, such as ``130 MHz``. - -.. releasenotes/notes/0.19/add-gate-error-objective-00a96f75055d1526.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The values ``"gate_error"`` and ``"balanced"`` are now available for the - ``objective`` option in the construction of the - :class:`~qiskit.transpiler.passes.BIPMapping` object, and ``"balanced"`` is - now the default. - - The ``"gate_error"`` objective requires passing a - :obj:`.BackendProperties` instance in the ``backend_prop`` - kwarg, which contains the 2q-gate gate errors used in the computation of the - objectives. The ``"balanced"`` objective will use the - :obj:`.BackendProperties` instance if it is given, but otherwise will assume - a CX error rate as given in the new parameter ``default_cx_error_rate``. - The relative weights of the gate-error and depth components of the balanced - objective can be controlled with the new ``depth_obj_weight`` parameter. - -.. releasenotes/notes/0.19/add-getters-and-setters-for-vqe-edc753591b368980.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Every attribute of the :class:`~qiskit.algorithms.VQE` class that is set at - the initialization is now accessible with getters and setters. Further, the - default values of the VQE attributes - :attr:`~qiskit.algorithms.minimimum_eigen_solvers.VQE.ansatz` and - :attr:`~qiskit.algorithms.minimimum_eigen_solvers.VQE.optimizer` can be - reset by assigning ``None`` to them:: - - vqe = VQE(my_ansatz, my_optimizer) - vqe.ansatz = None # reset to default: RealAmplitudes ansatz - vqe.optimizer = None # reset to default: SLSQP optimizer - -.. releasenotes/notes/0.19/add-group-qubit-wise-commuting-pauli-list-7b96834068a36928.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new method :meth:`.PauliList.group_qubit_wise_commuting` that - partitions a :obj:`.PauliList` into sets of mutually qubit-wise commuting - :obj:`.Pauli` operators. For example:: - - from qiskit.quantum_info import PauliList, Pauli - pauli_list = PauliList([Pauli("IY"), Pauli("XX"), Pauli("YY"), Pauli("YX")]) - pauli_list.group_qubit_wise_commuting() - -.. releasenotes/notes/0.19/add-hexagonal-lattice-couplingmap-d3b65b146b6cd1d1.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new coupling-map constructor method - :meth:`.CouplingMap.from_hexagonal_lattice` for constructing a hexagonal - lattice coupling map. For example, to construct a 2x2 hexagonal - lattice coupling map: - - .. code-block:: python - - from qiskit.transpiler import CouplingMap - cmap = CouplingMap.from_hexagonal_lattice(2, 2) - cmap.draw() - -.. releasenotes/notes/0.19/add-new-fake-backends-3376682dc5c63557.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- New fake backend classes are available under ``qiskit.test.mock``. These - include mocked versions of ``ibmq_brooklyn``, ``ibmq_manila``, - ``ibmq_jakarta``, and ``ibmq_lagos``. As with the other fake backends, these - include snapshots of calibration data (i.e. ``backend.defaults()``) and - error data (i.e. ``backend.properties()``) taken from the real system, and - can be used for local testing, compilation and simulation. - -.. releasenotes/notes/0.19/add-opflow-is-hermitian-method-6a461549e3c6b32c.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the :meth:`.OperatorBase.is_hermitian` method to check whether the - operator is Hermitian or not. :class:`~qiskit.algorithms.NumPyEigensolver` - and :class:`~qiskit.algorithms.NumPyMinimumEigensolver` use ``eigh`` or - ``eigsh`` to solve the eigenvalue problem when the operator is Hermitian. - -.. releasenotes/notes/0.19/add-passmanager-config-from-backend-af5dd7d99ec053ef.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new constructor method :meth:`.PassManagerConfig.from_backend`. It - constructs a :class:`~qiskit.transpiler.PassManagerConfig` object with user - options and the configuration of a backend. With this feature, a preset - passmanager can be built easier. For example:: - - from qiskit.transpiler.passmanager_config import PassManagerConfig - from qiskit.transpiler.preset_passmanagers import level_1_pass_manager - from qiskit.test.mock import FakeMelbourne - - pass_manager = level_1_pass_manager( - PassManagerConfig.from_backend(FakeMelbourne(), seed_transpiler=42) - ) - -.. releasenotes/notes/0.19/add-pulse-gate-pass-dc347177ed541bcc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- A new transpiler pass, :class:`.PulseGates`, was added, which automatically - extracts user-provided calibrations from the instruction schedule map and - attaches the gate schedule to the given (transpiled) quantum circuit as a - pulse gate. - - The :class:`.PulseGates` transpiler pass is applied to all optimization - levels from 0 to 3. No gate implementation is updated unless the end-user - explicitly overrides the ``backend.defaults().instruction_schedule_map``. - This pass saves users from individually calling - :meth:`.QuantumCircuit.add_calibration` for every circuit run on the - hardware. - - To supplement this new pass, a schedule was added to - :class:`~qiskit.pulse.InstructionScheduleMap` and is implicitly updated with - a metadata field ``"publisher"``. Backend-calibrated gate schedules have a - special publisher kind to avoid overriding circuits with calibrations of - already known schedules. Usually, end-users don't need to take care of this - metadata as it is applied automatically. You can call - :meth:`.InstructionScheduleMap.has_custom_gate` to check if the map has - custom gate calibration. - - See the below code example to learn how to apply custom gate implementation - for all circuits under execution. - - .. code-block:: python - - from qiskit.test.mock import FakeGuadalupe - from qiskit import pulse, circuit, transpile - - backend = FakeGuadalupe() - - with pulse.build(backend, name="x") as x_q0: - pulse.play(pulse.Constant(160, 0.1), pulse.drive_channel(0)) - - backend.defaults().instruction_schedule_map.add("x", (0,), x_q0) - - circs = [] - for _ in range(100): - circ = circuit.QuantumCircuit(1) - circ.sx(0) - circ.rz(1.57, 0) - circ.x(0) - circ.measure_active() - circs.append(circ) - - circs = transpile(circs, backend) - circs[0].calibrations # This returns calibration only for x gate - - Note that the instruction schedule map is a mutable object. - If you override one of the entries and use that backend for other experiments, - you may accidentally update the gate definition. - - .. code-block:: python - - backend = FakeGuadalupe() - - instmap = backend.defaults().instruction_schedule_map - instmap.add("x", (0, ), my_x_gate_schedule) - - qc = QuantumCircuit(1, 1) - qc.x(0) - qc.measure(0, 0) - - qc = transpile(qc, backend) # This backend uses custom X gate - - If you want to update the gate definitions of a specific experiment, - you need to first deepcopy the instruction schedule map - and directly pass it to the transpiler. - -.. releasenotes/notes/0.19/add-qubit-subset-to-bip-mapper-e1c6234d04484d58.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Introduced a new option ``qubit_subset`` to the constructor of - :class:`.BIPMapping`. - The option enables us to specify physical qubits to be used - (in ``coupling_map`` of the device) during the mapping in one line: - - .. code-block:: python - - mapped_circ = BIPMapping( - coupling_map=CouplingMap([[0, 1], [1, 2], [1, 3], [3, 4]]), - qubit_subset=[1, 3, 4] - )(circ) - - Previously, to do the same thing, we had to supply a reduced ``coupling_map`` - which contains only the qubits to be used, embed the resulting circuit onto - the original ``coupling_map`` and update the ``QuantumCircuit._layout`` accordingly: - - .. code-block:: python - - reduced_coupling = coupling_map.reduce(qubit_to_use) - mapped = BIPMapping(reduced_coupling)(circ) - # skip the definition of fill_with_ancilla() - # recover circuit on original coupling map - layout = Layout({q: qubit_to_use[i] for i, q in enumerate(mapped.qubits)}) - for reg in mapped.qregs: - layout.add_register(reg) - property_set = {"layout": fill_with_ancilla(layout)} - recovered = ApplyLayout()(mapped, property_set) - # recover layout - overall_layout = Layout({v: qubit_to_use[q] for v, q in mapped._layout.get_virtual_bits().items()}) - for reg in mapped.qregs: - overall_layout.add_register(reg) - recovered._layout = fill_with_ancilla(overall_layout) - -.. releasenotes/notes/0.19/add-sparsepauliop-fast-path-228065a05fca4387.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the ``ignore_pauli_phase`` and ``copy`` arguments to the constructor - of :obj:`~qiskit.quantum_info.SparsePauliOp`. ``ignore_pauli_phase`` - prevents the ``phase`` attribute of an input - :class:`~qiskit.quantum_info.PauliList` from being read, which is more - performant if the :obj:`.PauliList` is already known to have all phases as - zero in the internal ZX convention. ``copy`` allows users to avoid the copy - of the input data when they explicitly set ``copy=False``. - -.. releasenotes/notes/0.19/add-sparsepauliop-fast-path-228065a05fca4387.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Improved performance of the following :class:`~qiskit.quantum_info.SparsePauliOp` operations: - - * :meth:`~qiskit.quantum_info.SparsePauliOp.simplify` (see `#7122 `__) - * :meth:`~qiskit.quantum_info.SparsePauliOp.compose` - (see `#7126 `__) - * :meth:`~qiskit.quantum_info.SparsePauliOp._add` - (see `#7138 `__) - * :meth:`~qiskit.quantum_info.SparsePauliOp.from_list` and :meth:`~qiskit.quantum_info.PauliList.__init__` - (see other discussion in `#7138 `__). - -.. releasenotes/notes/0.19/add-sparsepauliop-sum-d55fc817c9fded82.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the :meth:`.SparsePauliOp.sum` method to add together many - :class:`.SparsePauliOp`\ s. This method has significantly better - performance than adding the instances together in a loop. For example, the - previous way to add several :class:`.SparsePauliOp`\ s together would be to - do:: - - from qiskit.quantum_info import SparsePauliOp, random_pauli_list - sparse_ops = [SparsePauliOp(random_pauli_list(10, 10)) for _ in [None]*1000] - - total = sparse_ops[0] - for op in sparse_ops[1:]: - total += op - - This can now be done far more efficiently (in both speed and typing!) as:: - - SparsePauliOp.sum(sparse_ops) - -.. releasenotes/notes/0.19/add-support-to-disable-amplitude-limit-in-parametric-pulses-ef88b77db8c1b06c.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added an argument ``limit_amplitude`` to the constructor of - ``ParametricPulse``, which is the base class of :obj:`.Gaussian`, - :obj:`.GaussianSquare`, :obj:`.Drag` and :obj:`.Constant`, to allowing - disabling the amplitude limit of 1 on a pulse-by-pulse basis. With - ``limit_amplitude=False``, individual pulses may have an amplitude exceeding - unity without raising a :class:`.PulseError`. See `#6544 - `__ for more - detail. - -.. releasenotes/notes/0.19/added-multiformat-support-b5d3c7c7c1536951.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Using :meth:`.QuantumCircuit.draw` or :func:`.circuit_drawer` with the - ``latex`` drawer will now generate a file in an image format inferred from the - filename extension, for example:: - - import qiskit - - circuit = qiskit.QuantumCircuit(2) - circuit.h(0) - circuit.cx(0, 1) - circuit.draw('latex', filename='./file.jpg') - - This will save the circuit drawing in the JPEG format. Previously, the - image always be in PNG format. Refer to `#6448 - `__ for more details. - - Now, if it encounters a filename extension which is not supported, for example:: - - circuit.draw('latex', filename='./file.spooky') - - it will raise a ``ValueError`` to change the filename extension to a supported image format. - -.. releasenotes/notes/0.19/added-snapshot-tests-for-backend-mapping-functions-5961300e09f05be0.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the parameter ``filename`` to - :func:`~qiskit.visualization.plot_gate_map` and - :func:`~qiskit.visualization.plot_coupling_map`, which allows saving the - resulting images to a file. - -.. releasenotes/notes/0.19/approxiamte-quantum-compiler-3c74652d4c5e9fa6.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Introduced an approximate quantum compiler and a corresponding unitary - synthesis plugin implementation. The main AQC class is - :class:`~qiskit.transpiler.synthesis.aqc.AQC` for a standalone version that - compiles a unitary matrix into an approximate circuit. The plugin may be - invoked by :func:`~.compiler.transpile` when the - ``unitary_synthesis_method`` argument is set to ``'aqc'``. See - :mod:`qiskit.transpiler.synthesis.aqc` for full details. - -.. releasenotes/notes/0.19/circuit-size-depth-filter-function-2177a8a71588f915.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a ``filter_function`` argument to - :meth:`.QuantumCircuit.depth` and - :meth:`.QuantumCircuit.size` in order to - analyze circuit operations according to some criteria. - - For example, to get the number of two-qubit gates, you can do:: - - circuit.size(lambda x: x[0].num_qubits == 2) - - Or to get the depth of T gates acting on the zeroth qubit:: - - circuit.depth(lambda x: x[0].name == 't' and circuit.qubits[0] in x[1]) - -.. releasenotes/notes/0.19/collect-block-pass-b15031aa9749d735.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new transpiler pass, - :class:`~qiskit.transpiler.passes.CollectMultiQBlocks`, to the - :mod:`qiskit.transpiler.passes` module. This pass is used to collect - sequences of uninterrupted gates acting on groups of qubits. It provides - a similar function to the existing - :class:`~qiskit.transpiler.passes.Collect2qBlocks` pass, but while that - pass is designed and optimized to find 2 qubit blocks this new pass will - work to find blocks of any size. - -.. releasenotes/notes/0.19/control-flow-builder-interface-63910843f8bea5e0.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- There is a builder interface for the new control-flow operations on - :obj:`.QuantumCircuit`, such as the new :obj:`.ForLoopOp`, :obj:`.IfElseOp`, - and :obj:`.WhileLoopOp`. The interface uses the same circuit methods, - *i.e.* :meth:`.QuantumCircuit.for_loop`, :meth:`.QuantumCircuit.if_test` and - :meth:`.QuantumCircuit.while_loop`, which are overloaded so that if the - ``body`` parameter is not given, they return a context manager. Entering - one of these context managers pushes a scope into the circuit, and captures - all gate calls (and other scopes) and the resources these use, and builds up - the relevant operation at the end. For example, you can now do:: - - qc = QuantumCircuit(2, 2) - with qc.for_loop(range(5)) as i: - qc.rx(i * math.pi / 4, 0) - - This will produce a :obj:`.ForLoopOp` on ``qc``, which knows that qubit 0 is - the only resource used within the loop body. These context managers can be - nested, and will correctly determine their widths. You can use - :meth:`.QuantumCircuit.break_loop` and :meth:`.QuantumCircuit.continue_loop` - within a context, and it will expand to be the correct width for its - containing loop, even if it is nested in further - :meth:`.QuantumCircuit.if_test` blocks. - - The :meth:`~.QuantumCircuit.if_test` context manager provides a chained - manager which, if desired, can be used to create an ``else`` block, such as - by:: - - qreg = QuantumRegister(2) - creg = ClassicalRegister(2) - qc = QuantumCircuit(qreg, creg) - qc.h(0) - qc.cx(0, 1) - qc.measure(0, 0) - with qc.if_test((creg, 0)) as else_: - qc.x(1) - with else_: - qc.z(1) - - The manager will ensure that the ``if`` and ``else`` bodies are defined over - the same set of resources. - -.. releasenotes/notes/0.19/cx-cancellation-pass-generalization-538fb7cfe49b3fd5.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Introduced a new transpiler pass :obj:`.InverseCancellation` that generalizes the :obj:`.CXCancellation` - pass to cancel any self-inverse gates or gate-inverse pairs. It can be used by - initializing :obj:`.InverseCancellation` and passing a gate to cancel, for example:: - - from qiskit.transpiler.passes import InverseCancellation - from qiskit import QuantumCircuit - from qiskit.circuit.library import HGate - from qiskit.transpiler import PassManager - - qc = QuantumCircuit(2, 2) - qc.h(0) - qc.h(0) - pass_ = InverseCancellation([HGate()]) - pm = PassManager(pass_) - new_circ = pm.run(qc) - -.. releasenotes/notes/0.19/deprecate-backend-rzx-cal-build-8eda1526725d7e7d.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The constructor of :class:`~qiskit.transpiler.passes.RZXCalibrationBuilder` - has two new kwargs ``instruction_schedule_map`` and ``qubit_channel_mapping`` - which take a :class:`~qiskit.pulse.InstructionScheduleMap` and list of - channel name lists for each qubit respectively. These new arguments are used - to directly specify the information needed from a backend target. They should - be used instead of passing a :class:`~qiskit.providers.BaseBackend` or - :class:`~qiskit.providers.BackendV1` object directly to the pass with the - ``backend`` argument. - -.. releasenotes/notes/0.19/draw-statevector-in-ket-notation-0726959d1f6ea3ce.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :obj:`.Statevector`\ s of states comprised only of qubits can now be - drawn in LaTeX in ket notation. In ket notation the entries of the - statevector are processed such that exact factors like fractions or square - roots of two are drawn as such. The particular convention can be chosen by - passing the ``convention`` keyword argument as either ``"ket"`` or - ``"vector"`` as appropriate:: - - import math - from qiskit.quantum_info import Statevector - - sv = Statevector([math.sqrt(0.5), 0, 0, -math.sqrt(0.5)]) - sv.draw("latex", convention="ket") - sv.draw("latex", convention="vector") - -.. releasenotes/notes/0.19/echo-rzx-weyl-decomposition-ef72345a58bea9e0.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new transpiler pass :class:`.EchoRZXWeylDecomposition` that allows - users to decompose an arbitrary two-qubit gate in terms of echoed RZX-gates - by leveraging Cartan's decomposition. In combination with other transpiler - passes, this can be used to transpile arbitrary circuits to RZX-gate-based - and pulse-efficient circuits that implement the same unitary. - -.. releasenotes/notes/0.19/ensure-qnspsa-batching-e48f7ec72412c071.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :class:`~qiskit.algorithms.optimizers.SPSA` and - :class:`~qiskit.algorithms.optimizers.QNSPSA` optimizer classes are now - capable of batching as many circuit evaluations as possible for both the - iterations and the initial calibrations. This can be leveraged by setting - the ``max_evals_grouped`` kwarg on the constructor for - :class:`~qiskit.algorithms.VQE` when using either - :class:`~qiskit.algorithms.optimizers.SPSA` or - :class:`~qiskit.algorithms.optimizers.QNSPSA` as the ``optimizer`` parameter. - For example:: - - from qiskit.circuit.library import TwoLocal - from qiskit.algorithms import VQE - from qiskit.algorithms.optimizers import QNSPSA - from qiskit.test.mock import FakeMontreal - - backend = FakeMontreal() - ansatz = TwoLocal(2, rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - qnspsa = QNSPSA(fidelity, maxiter=5) - vqe = VQE( - ansatz=ansatz, - optimizer=qnspsa, - max_evals_grouped=100, - quantum_instance=backend, - ) - -.. releasenotes/notes/0.19/feature-rzx-decomposition-c3b5a36b88303c1f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- This release introduces a decomposition method for two-qubit gates which - targets user-defined sets of RZX gates. Transpiler users can enable - decomposition for {``RZX(pi/2)``, ``RZX(pi/4)``, and ``RZX(pi/6)``} specifically by including - ``'rzx'`` in their ``basis_gates`` list when calling - :func:`~qiskit.compiler.transpile`. Quantum information package users can - find the method itself under the :obj:`.XXDecomposer` class. - -.. releasenotes/notes/0.19/feature_optimize_1q_commutation-28530970f58fb21e.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a transpiler pass :obj:`.Optimize1qGatesSimpleCommutation`, which optimizes - a circuit according to a strategy of commuting single-qubit gates around to - discover resynthesis opportunities. - -.. releasenotes/notes/0.19/fix-infinite-job-submissions-d6f6a583535ca798.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a ``max_job_tries`` parameter to :obj:`~qiskit.utils.QuantumInstance`, - to limit the number of times a job will attempt to be executed on a backend. - Previously the submission and fetching of results would be attempted - infinitely, even if the job was cancelled or errored on the backend. The - default is now 50, and the previous behaviour can be achieved by setting - ``max_job_tries=-1``. Fixes `#6872 - `__ and `#6821 - `__. - -.. releasenotes/notes/0.19/fix-latex-drawer-bit-cond-d629c04a08e81d6d.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``latex`` output method for the :func:`~qiskit.visualization.circuit_drawer` - function and the :meth:`.QuantumCircuit.draw` method can now - draw circuits that contain gates with single bit condition. This was added for - compatibility of latex drawer with the new feature of supporting classical - conditioning of gates on single classical bits. - -.. releasenotes/notes/0.19/fix-mpl-drawer-bit-condition-90c4dac2defdd8c6.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``"mpl"`` output method for the :func:`~qiskit.visualization.circuit_drawer` - function and the :meth:`.QuantumCircuit.draw` method can now - draw circuits that contain gates with single bit condition. This was added for - compatibility of the ``"mpl"`` drawer with the new feature of supporting classical - conditioning of gates on single classical bits. - -.. releasenotes/notes/0.19/fix-text-drawer-bit-cond-a3b02f0b0b6e3ec2.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``text`` output method for the :func:`~qiskit.visualization.circuit_drawer` - function and the :meth:`.QuantumCircuit.draw` method can now - draw circuits that contain gates with single bit condition. This was added for - compatibility of text drawer with the new feature of supporting classical - conditioning of gates on single classical bits. - -.. releasenotes/notes/0.19/gates-in-basis-pass-337f6637e61919db.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- A new analysis transpiler pass, - :class:`~qiskit.transpiler.passes.GatesInBasis`, was added to - :mod:`qiskit.transpiler.passes`. This pass is used to check if the - :class:`~qiskit.dagcircuit.DAGCircuit` being transpiled has all the gates - in the configured basis set or not. It will set the attribute - ``"all_gates_in_basis"`` in the property set to ``True`` if all the gates - in the :class:`~qiskit.dagcircuit.DAGCircuit` are in the configured basis - set or ``False`` if they are not. For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.transpiler.passes import GatesInBasis - - # Instatiate Pass - basis_gates = ["cx", "h"] - basis_check_pass = GatesInBasis(basis_gates) - # Build circuit - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.cx(0, 1) - circuit.measure_all() - # Run pass on circuit - property_set = {} - basis_check_pass(circuit, property_set=property_set) - assert property_set["all_gates_in_basis"] - -.. releasenotes/notes/0.19/heavy-hex-heavy-square-coupling-map-29f459b93cd18518.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added two new constructor methods, - :meth:`~qiskit.transpiler.CouplingMap.from_heavy_hex` and - :meth:`~qiskit.transpiler.CouplingMap.from_heavy_square`, to the - :class:`~qiskit.transpiler.CouplingMap` class. These constructor methods - are used to create a :class:`~qiskit.transpiler.CouplingMap` that are - a heavy hex or heavy square graph as described in |Chamberland2020|_. - - For example: - - .. code-block:: python - - from qiskit.transpiler import CouplingMap - - cmap = CouplingMap.from_heavy_hex(5) - cmap.draw() - - - .. code-block:: python - - from qiskit.transpiler import CouplingMap - - cmap = CouplingMap.from_heavy_square(5) - cmap.draw() - - .. |Chamberland2020| replace:: Chamberland *et al.*, 2020 - .. _Chamberland2020: https://journals.aps.org/prx/abstract/10.1103/PhysRevX.10.011022 - -.. releasenotes/notes/0.19/hhl-negative-eigenvalues-ef11d231181e8043.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :obj:`.HHL` algorithm can now find solutions when its matrix has negative eigenvalues. - To enable this, the algorithm now adds an extra qubit to represent the sign of the value, - and the helper algorithm :obj:`.ExactReciprocal` was updated to process this - new information. See `#6971 - `__ for more details. - -.. releasenotes/notes/0.19/ignis-mitigators-70492690cbcf99ca.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added two new classes, :class:`~qiskit.utils.mitigation.CompleteMeasFitter` - and :class:`~qiskit.utils.mitigation.TensoredMeasFitter` to the - :mod:`qiskit.utils.mitigation` module. These classes are for use only as - values for the ``measurement_error_mitigation_cls`` kwarg of the - :class:`~qiskit.utils.QuantumInstance` class. The instantiation and usage - of these classes (or anything else in :mod:`qiskit.utils.mitigation`) - outside of the ``measurement_error_mitigation_cls`` kwarg should be treated as an - internal private API and not relied upon. - -.. releasenotes/notes/0.19/listops-coeffs-1e04a34b46b2fd23.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :obj:`.ListOp` class in :mod:`qiskit.opflow` now has a - :attr:`~.ListOp.coeffs` attribute, which returns a list of the coefficients - of the operator list, with the overall coefficient (:obj:`.ListOp.coeff`) - distributed multiplicatively into the list. Note that :obj:`.ListOp` - objects may be nested (contained in ``oplist`` of a :obj:`.ListOp` object), - and in these cases an exception is raised if the `coeffs` method is called. - The :obj:`.ListOp.coeffs` method conveniently duck-types against the - ``coeffs`` property method of the non-nesting :obj:`.PauliSumOp` class. - -.. releasenotes/notes/0.19/make-statevector-subscriptable-and-add-inner-product_method-a0337393d9a5b666.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :class:`~qiskit.quantum_info.Statevector` class is now subscriptable. - User can now retrieve the nth coefficient in a - :class:`~qiskit.quantum_info.Statevector` by index as ``statevec[n]``. - -.. releasenotes/notes/0.19/make-statevector-subscriptable-and-add-inner-product_method-a0337393d9a5b666.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the :obj:`.Statevector.inner` method to calculate inner products of - :class:`.Statevector` instances. For example:: - - statevec_inner_other = statevec.inner(other) - - will return the inner product of ``statevec`` with ``other``. While - ``statevec`` must be a :class:`.Statevector`, ``other`` can be anything - that can be constructed into a :class:`.Statevector`, such as a Numpy array. - -.. releasenotes/notes/0.19/measure_all-add_bits-8525317935197b90.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new parameter, ``add_bits``, to :meth:`.QuantumCircuit.measure_all`. - By default it is set to ``True`` to maintain the previous behaviour of adding a new :obj:`.ClassicalRegister` of the same size as the number of qubits to store the measurements. - If set to ``False``, the measurements will be stored in the already existing classical bits. - For example, if you created a circuit with existing classical bits like:: - - from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister - - qr = QuantumRegister(2) - cr = ClassicalRegister(2, "meas") - circuit = QuantumCircuit(qr, cr) - - calling ``circuit.measure_all(add_bits=False)`` will use the existing - classical register ``cr`` as the output target of the - :class:`~qiskit.circuit.Measurement` objects added to the circuit. - -.. releasenotes/notes/0.19/more-forgiving-numeric-conversions-in-ParameterExpression-6cd7316c32c67c55.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- :obj:`~qiskit.circuit.ParameterExpression` now delegates its numeric - conversions to the underlying symbolic library, even if there are - potentially unbound parameters. This allows conversions of expressions such - as:: - - >>> from qiskit.circuit import Parameter - >>> x = Parameter('x') - >>> float(x - x + 2.3) - 2.3 - - where the underlying expression has a fixed value, but the parameter ``x`` - is not yet bound. - -.. releasenotes/notes/0.19/optimizer-minimize-5a5a1e9d67db441a.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added an :meth:`.Optimizer.minimize` method to all optimizers: - :class:`~qiskit.algorithms.optimizers.Optimizer` and derived classes. - This method mimics the signature of SciPy's ``minimize()`` function and - returns an :class:`~qiskit.algorithms.optimizers.OptimizerResult`. - - For example - - .. code-block:: python - - import numpy as np - from qiskit.algorithms.optimizers import COBYLA - - def loss(x): - return -(x[0] - 1) ** 2 - (x[1] + 1) ** 3 - - initial_point = np.array([0, 0]) - optimizer = COBYLA() - result = optimizer.minimize(loss, initial_point) - - optimal_parameters = result.x - minimum_value = result.fun - num_function_evals = result.nfev - -.. releasenotes/notes/0.19/pauli-evolution-gate-ad767a3e43714fa7.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a :class:`~qiskit.circuit.library.PauliEvolutionGate` to the circuit - library (:mod:`qiskit.circuit.library`) which defines a gate performing time - evolution of (sums or sums-of-sums of) :obj:`.Pauli`\ s. The synthesis of - this gate is performed by :class:`~qiskit.synthesis.EvolutionSynthesis` and - is decoupled from the gate itself. Currently available synthesis methods - are: - - * :class:`~qiskit.synthesis.LieTrotter`: first order Trotterization - * :class:`~qiskit.synthesis.SuzukiTrotter`: higher order Trotterization - * :class:`~qiskit.synthesis.MatrixExponential`: exact, matrix-based evolution - - For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import PauliEvolutionGate - from qiskit.quantum_info import SparsePauliOp - from qiskit.synthesis import SuzukiTrotter - - operator = SparsePauliOp.from_list([ - ("XIZ", 0.5), ("ZZX", 0.5), ("IYY", -1) - ]) - time = 0.12 # evolution time - synth = SuzukiTrotter(order=4, reps=2) - - evo = PauliEvolutionGate(operator, time=time, synthesis=synth) - - circuit = QuantumCircuit(3) - circuit.append(evo, range(3)) - -.. releasenotes/notes/0.19/plot_coupling_map-new-function-deb973b1bf0ad92f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- A new function :func:`~qiskit.visualization.plot_coupling_map()` has been introduced, which - extends the functionality of the existing function - :func:`~qiskit.visualization.plot_gate_map()`, by accepting three parameters: ``num_qubit``, - ``qubit_coordinates``, and ``coupling_map`` (instead of ``backend``), to allow an arbitrary - qubit coupling map to be plotted. - -.. releasenotes/notes/0.19/qasm3_dumps-7475de655e1acb24.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Qiskit Terra now has initial support for serializing - :class:`.QuantumCircuit`\ s to `OpenQASM 3 `__: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister - from qiskit import qasm3 - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - - print(qasm3.dumps(qc)) - - This initial release has limited support for named registers, basic built-in - instructions (such as measure, barrier and reset), user-defined gates, - user-defined instructions (as subroutines), and the new control-flow constructs - also introduced in this release: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister - from qiskit import qasm3 - import math - - composite_circ_qreg = QuantumRegister(2) - composite_circ = QuantumCircuit(composite_circ_qreg, name="composite_circ") - composite_circ.h(0) - composite_circ.x(1) - composite_circ.cx(0, 1) - composite_circ_gate = composite_circ.to_gate() - - qr = QuantumRegister(2, "qr") - cr = ClassicalRegister(2, "cr") - qc = QuantumCircuit(qr, cr) - with qc.for_loop(range(4)) as i: - qc.rx(i * math.pi / 4, 0) - qc.cx(0, 1) - qc.barrier() - qc.append(composite_circ_gate, [0, 1]) - qc.measure([0, 1], [0, 1]) - - print(qasm3.dumps(qc)) - -.. releasenotes/notes/0.19/qdrift-as-product-formula-044a37a106a45a47.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :class:`~qiskit.opflow.evolutions.QDrift` class was reformulated as a - synthesis method for :obj:`.PauliEvolutionGate`, deriving from - :obj:`~qiskit.opflow.evolutions.TrotterizationBase`. - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import PauliEvolutionGate - from qiskit.synthesis import QDrift - from qiskit.opflow import X, Y, Z - - qdrift = QDrift(reps=2) - operator = (X ^ 3) + (Y ^ 3) + (Z ^ 3) - time = 2.345 # evolution time - - evolution_gate = PauliEvolutionGate(operator, time, synthesis=qdrift) - - circuit = QuantumCircuit(3) - circuit.append(evolution_gate, range(3)) - -.. releasenotes/notes/0.19/qpy-v2-f1c380b40936cccf.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- QPY serialization is now capable of representing - :attr:`~qiskit.circuit.QuantumCircuit.global_phase` attributes of a - :class:`~qiskit.circuit.QuantumCircuit` object that are an ``int``, - :class:`~qiskit.circuit.Parameter` object, or - :class:`~qiskit.circuit.ParameterExpression` object. Previous versions of - QPY would only accept a :attr:`~qiskit.circuit.QuantumCircuit.global_phase` - that was a ``float``. - - This requires the QPY format :ref:`qpy_version_2` which was introduced in - this release to represent the additional types. - -.. releasenotes/notes/0.19/quantumcircuit-consolidate-bit_indices-c4ee90e831f1aed2.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- A new :meth:`~qiskit.circuit.QuantumCircuit.find_bit` method has - been added to the :class:`~qiskit.circuit.QuantumCircuit` class, - which allows lookups of the index and registers of a provided - :class:`~qiskit.circuit.Bit` on the given circuit. The method - returns a two-element ``namedtuple`` containing 0) the index of the ``Bit`` - in either :attr:`~qiskit.circuit.QuantumCircuit.qubits` (for - a :class:`~qiskit.circuit.Qubit`) or - :attr:`~qiskit.circuit.QuantumCircuit.clbits` (for a - :class:`~qiskit.circuit.Clbit`) and 1) a list of length-2 tuples - containing each circuit :class:`~qiskit.circuit.Register` which - contains the ``Bit``, and the index in that ``Register`` at which the - ``Bit`` can be found. - - For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit - - reg1 = QuantumRegister(3, 'foo') - qubit = Qubit() - reg2 = QuantumRegister(2, 'bar') - - qc = QuantumCircuit(reg1, [qubit], reg2) - - print(qc.find_bit(reg1[2])) - print(qc.find_bit(qubit)) - - would generate: - - .. code-block:: python - - BitLocations(index=2, registers=[(QuantumRegister(3, 'foo'), 2)]) - BitLocations(index=3, registers=[]) - -.. releasenotes/notes/0.19/quantumcircuit-dynamic-instructions-a69db25665739004.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Three new :class:`~qiskit.circuit.Instruction` subclasses have been added - to support control flow operations in dynamic circuits: - :class:`~qiskit.circuit.WhileLoopOp`, - :class:`~qiskit.circuit.ForLoopOp`, - and :class:`~qiskit.circuit.IfElseOp`. Additionally, two - subclasses, :class:`~qiskit.circuit.BreakLoopOp`, - and :class:`~qiskit.circuit.ContinueLoopOp`, have been added to - support breaking from and continuing to the next iteration of a loop - context, respectively. - - These can be created as stand-alone :class:`~qiskit.circuit.Instruction`\ s, - or appended to an existing :class:`~qiskit.circuit.QuantumCircuit` instance - via their respective methods, - :meth:`.QuantumCircuit.while_loop`, - :meth:`~qiskit.circuit.QuantumCircuit.for_loop`, - :meth:`~qiskit.circuit.QuantumCircuit.if_test`, - :meth:`~qiskit.circuit.QuantumCircuit.if_else`, - :meth:`~qiskit.circuit.QuantumCircuit.break_loop`, - and :meth:`~qiskit.circuit.QuantumCircuit.continue_loop`. - -.. releasenotes/notes/0.19/readout-mitigation-classes-2ef175e232d791ae.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the :class:`~qiskit.result.BaseReadoutMitigator` abstract base class - for implementing classical measurement error mitigators. These objects - are intended for mitigation measurement errors in - :class:`~qiskit.result.Counts` objects returned from execution of circuits - on backends with measurement errors. - - Readout mitigator classes have two main methods: - - * :meth:`~.BaseReadoutMitigator.expectation_value` which computes an - mitigated expectation value and standard error of a diagonal operator from - a noisy :class:`~qiskit.result.Counts` object. - - * :meth:`~.BaseReadoutMitigator.quasi_probabilities` that computes an error - mitigated :class:`~qiskit.result.QuasiDistribution`, including standard - error, from a noisy counts object. - - Note that currently the :mod:`qiskit.algorithms` module and the - :class:`~qiskit.utils.QuantumInstance` class still use the legacy mitigators - migrated from Qiskit Ignis in :mod:`qiskit.utils.mitigation`. It is planned - to upgrade the module to use the new mitigator classes and deprecate the legacy - mitgation code in a future release. - -.. releasenotes/notes/0.19/readout-mitigation-classes-2ef175e232d791ae.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the :class:`~qiskit.result.LocalReadoutMitigator` class for - performing measurement readout error mitigation of local measurement - errors. Local measuerment errors are those that are described by a - tensor-product of single-qubit measurement errors. - - This class can be initialized with a list of :math:`N` single-qubit of - measurement error assignment matrices or from a backend using the readout - error information in the backend properties. - - Mitigation is implemented using local assignment-matrix inversion which has - complexity of :math:`O(2^N)` for :math:`N`-qubit mitigation of - :class:`~qiskit.result.QuasiDistribution` and expectation values. - -.. releasenotes/notes/0.19/readout-mitigation-classes-2ef175e232d791ae.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added the :class:`~qiskit.result.CorrelatedReadoutMitigator` class for - performing measurement readout error mitigation of correlated measurement - errors. This class can be initialized with a single :math:`2^N \times 2^N` - measurement error assignment matrix that descirbes the error probabilities. - Mitigation is implemented via inversion of assigment matrix which has - mitigation complexity of :math:`O(4^N)` of - :class:`~qiskit.result.QuasiDistribution` and expectation values. - -.. releasenotes/notes/0.19/readout-mitigation-classes-2ef175e232d791ae.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a :attr:`.QuasiDistribution.stddev_upper_bound` - attribute and a kwarg to the constructor of the :class:`.QuasiDistribution` - class, which is used for storing standard errors in quasi-probability - estimates. This is used by :class:`~qiskit.result.BaseReadoutMitigator` - classes to store the standard error in mitigated quasi probabilities. - -.. releasenotes/notes/0.19/readout-mitigation-classes-2ef175e232d791ae.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a :meth:`~qiskit.result.Counts.shots` method to - :class:`qiskit.result.Counts` to return the sum of all outcomes in - the counts. - -.. releasenotes/notes/0.19/refactor-grover-isgoodstate-check-54fdb61f899e5158.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- When running the :class:`~qiskit.algorithms.Grover` algorithm class if the - optimal power is known and only a single circuit is run, the - :attr:`.AmplificationProblem.is_good_state` callback function is no longer - required to be set and the Grover search will return the most likely - bitstring. Generally, if the optimal power of the Grover operator is not - known, the :class:`~qiskit.algorithms.Grover` algorithm checks different - powers (i.e. iterations) and applies the - :attr:`~qiskit.algorithms.AmplificationProblem.is_good_state` function to - check whether a good bitstring has been measured. For example, you are now - able to run something like:: - - from qiskit.algorithms import Grover, AmplificationProblem - from qiskit.providers.aer import AerSimulator - from qiskit.quantum_info import Statevector - - # Fixed Grover power: 2. - grover = Grover(iterations=2, quantum_instance=AerSimulator()) - - # The ``is_good_state`` argument not required here since Grover search - # will be run only once, with a power of 2. - problem = AmplificationProblem(Statevector.from_label("111")) - - # Run Grover search and print the best measurement - result = grover.amplify(problem) - print(result.top_measurement) # should print 111 - -.. releasenotes/notes/0.19/remove-final-measure-rewrite-37d26dbba7385ebc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added method :meth:`~qiskit.dagcircuit.DAGCircuit.remove_cregs` - to class :class:`~qiskit.dagcircuit.DAGCircuit` to support classical - register removal. - -.. releasenotes/notes/0.19/remove-final-measure-rewrite-37d26dbba7385ebc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added method :meth:`~qiskit.dagcircuit.DAGCircuit.remove_clbits` - to class :class:`~qiskit.dagcircuit.DAGCircuit` to support the removal - of idle classical bits. Any classical registers referencing a removed bit - are also removed. - -.. releasenotes/notes/0.19/replace-block-with-op-e0d387f6d860d586.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new method, - :meth:`~qiskit.dagcircuit.DAGCircuit.replace_block_with_op`, to the - :class:`~qiskit.dagcircuit.DAGCircuit` class. This method is used to replace - a block of nodes in the DAG with a single operation. The canonical example - is for the :class:`~qiskit.transpiler.passes.ConsolidateBlocks` pass which - replaces blocks of nodes with equivalent - :class:`~qiskit.extensions.UnitaryGate` nodes. - -.. releasenotes/notes/0.19/replace-block-with-op-e0d387f6d860d586.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new analysis transpiler pass, - :class:`~qiskit.transpiler.passes.Collect1qRuns`, to the - :mod:`qiskit.transpiler.passes` module. This pass is used to find sequences - of uninterrupted gates acting on a single qubit. It is similar to the - :class:`~qiskit.transpiler.passes.Collect2qBlocks` and - :class:`~qiskit.transpiler.passes.CollectMultiQBlocks` but optimized for - single qubit runs instead of multiple qubit blocks. - -.. releasenotes/notes/0.19/retworkx-substitute_node_with_dag-speedup-d7d1f0d33716131d.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Various transpilation internals now use new features in `retworkx - `__ 0.10 when operating on the internal - circuit representation. This can often result in speedups in calls to - :obj:`~.compiler.transpile` of around 10-40%, with greater effects at higher - optimization levels. See `#6302 - `__ for more details. - -.. releasenotes/notes/0.19/squ-gate-name-785b7896300a92ef.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :class:`~qiskit.transpiler.passes.UnitarySynthesis` transpiler pass in - :mod:`qiskit.transpiler.passes` has a new kwarg in the constructor, - ``min_qubits``. When specified this can be set to an ``int`` value which - is the minimum size :class:`~qiskit.extensions.UnitaryGate` object to - run the unitary synthesis on. If a :class:`~qiskit.extensions.UnitaryGate` - in a :class:`~qiskit.circuit.QuantumCircuit` uses fewer qubits it will - be skipped by that instance of the pass. - -.. releasenotes/notes/0.19/support-dict-for-aux-operators-c3c9ad380c208afd.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :obj:`~qiskit.algorithms.eigen_solvers.Eigensolver` and - :obj:`~qiskit.algorithms.minimimum_eigen_solvers.MinimumEigensolver` interfaces now support the type - ``Dict[str, Optional[OperatorBase]]`` for the ``aux_operators`` parameter in their respective - :meth:`~qiskit.algorithms.eigen_solvers.Eigensolver.compute_eigenvalues` and - :meth:`~qiskit.algorithms.minimimum_eigen_solvers.MinimumEigensolver.compute_minimum_eigenvalue` methods. - In this case, the auxiliary eigenvalues are also stored in a dictionary under the same keys - provided by the ``aux_operators`` dictionary. Keys that correspond to an operator that does not commute - with the main operator are dropped. - -.. releasenotes/notes/0.19/target-in-transpiler-c0a97bd33ad9417d.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :class:`~qiskit.transpiler.passes.BasisTranslator`, - :class:`~qiskit.transpiler.passes.GateDirection`, and - :class:`~qiskit.transpiler.passes.CheckGateDirection` transpiler passes have - a new ``target`` kwarg in their constructors, which can be used to set - a :class:`~qiskit.transpiler.Target` object as the target for the pass. If - it is set it will be used instead of the ``target_basis`` (in the case of - the :class:`~qiskit.transpiler.passes.BasisTranslator` pass) or - ``coupling_map`` (in the case of the - :class:`~qiskit.transpiler.passes.GateDirection` and - :class:`~qiskit.transpiler.passes.CheckGateDirection` passes) arguments. - -.. releasenotes/notes/0.19/two-step-transpile-f20d709a7a0c42dd.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Allow two transpiler stages in the :class:`~qiskit.utils.QuantumInstance`, one for - parameterized circuits and a second one for bound circuits (i.e. no free parameters) only. - If a quantum instance with passes for unbound and bound circuits is passed into a - :class:`.CircuitSampler`, the sampler will attempt to apply the unbound pass - once on the parameterized circuit, cache it, and only apply the bound pass for all future - evaluations. - - This enables variational algorithms like the :class:`~qiskit.algorithms.VQE` to run a - custom pass manager for parameterized circuits once and, additionally, another the transpiler - again with a different custom pass manager on the bound circuits in each iteration. Being able - to run different pass managers is important because not all passes support parameterized - circuits (for example :class:`~qiskit.transpiler.passes.Optimize1qGatesDecomposition` only - works with bound circuit parameters). - - For example, this feature allows using the pulse-efficient CX decomposition in the VQE, as - - .. code-block:: python - - from qiskit.algorithms import VQE - from qiskit.opflow import Z - from qiskit.circuit.library.standard_gates.equivalence_library import StandardEquivalenceLibrary as std_eqlib - from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap - from qiskit.transpiler.preset_passmanagers import level_1_pass_manager - from qiskit.transpiler.passes import ( - Collect2qBlocks, ConsolidateBlocks, Optimize1qGatesDecomposition, - RZXCalibrationBuilderNoEcho, UnrollCustomDefinitions, BasisTranslator - ) - from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import EchoRZXWeylDecomposition - from qiskit.test.mock import FakeBelem - from qiskit.utils import QuantumInstance - - # Replace by a real backend! If not ensure qiskit-aer is installed to simulate the backend - backend = FakeBelem() - - # Build the pass manager for the parameterized circuit - rzx_basis = ['rzx', 'rz', 'x', 'sx'] - coupling_map = CouplingMap(backend.configuration().coupling_map) - config = PassManagerConfig(basis_gates=rzx_basis, coupling_map=coupling_map) - pre = level_1_pass_manager(config) - - # Build a pass manager for the CX decomposition (works only on bound circuits) - post = PassManager([ - # Consolidate consecutive two-qubit operations. - Collect2qBlocks(), - ConsolidateBlocks(basis_gates=['rz', 'sx', 'x', 'rxx']), - - # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates. - EchoRZXWeylDecomposition(backend), - - # Attach scaled CR pulse schedules to the RZX gates. - RZXCalibrationBuilderNoEcho(backend), - - # Simplify single-qubit gates. - UnrollCustomDefinitions(std_eqlib, rzx_basis), - BasisTranslator(std_eqlib, rzx_basis), - Optimize1qGatesDecomposition(rzx_basis), - ]) - - quantum_instance = QuantumInstance(backend, pass_manager=pre, bound_pass_manager=post) - - vqe = VQE(quantum_instance=quantum_instance) - result = vqe.compute_minimum_eigenvalue(Z ^ Z) - -.. releasenotes/notes/0.19/unitary-synthesis-plugin-a5ec21a1906149fa.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Introduced a new unitary synthesis plugin interface which is used to enable - using alternative synthesis techniques included in external packages - seamlessly with the :class:`~qiskit.transpiler.passes.UnitarySynthesis` - transpiler pass. Users can select a plugin to use when calling - :func:`~qiskit.compiler.transpile` by setting the - ``unitary_synthesis_method`` kwarg to the plugin's name. A full list of - installed plugins can be found using the - :func:`qiskit.transpiler.passes.synthesis.plugin.unitary_synthesis_plugin_names` - function. For example, if you installed a package that includes a synthesis - plugin named ``special_synth`` you could use it with:: - - from qiskit import transpile - - transpile(qc, unitary_synthesis_method='special_synth', optimization_level=3) - - This will replace all uses of the :class:`~qiskit.transpiler.passes.UnitarySynthesis` - with the method included in the external package that exports the ``special_synth`` - plugin. - - The plugin interface is built around setuptools - `entry points `__ - which enable packages external to Qiskit to advertise they include a - synthesis plugin. For details on writing a new plugin refer to the - :mod:`qiskit.transpiler.passes.synthesis.plugin` module documentation. - -.. releasenotes/notes/0.19/vf2layout-4cea88087c355769.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Added a new transpiler pass, :class:`~qiskit.transpiler.passes.VF2Layout`. - This pass models the layout allocation problem as a subgraph isomorphism - problem and uses the `VF2 algorithm`_ implementation in `rustworkx - `__ - to find a perfect layout (a layout which would not require additional - routing) if one exists. The functionality exposed by this new pass is very - similar to exisiting :class:`~qiskit.transpiler.passes.CSPLayout` but - :class:`~qiskit.transpiler.passes.VF2Layout` is significantly faster. - - .. _VF2 algorithm: https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.101.5342&rep=rep1&type=pdf - -.. _Release Notes_0.19.0_Known Issues: - -Known Issues ------------- - -.. releasenotes/notes/0.19/draw-statevector-in-ket-notation-0726959d1f6ea3ce.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``"ket"`` convention in the ``"latex"`` drawer of :meth:`.Statevector.draw` - is only valid for states comprising purely of qubits. If you are using states - with some spaces of dimension greater than two, you should either pass - ``convention="vector"``, or use a different drawer. - -.. releasenotes/notes/0.19/qasm3-limitations-ebfdedab3f4ab6e1.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The OpenQASM 3 export capabilities are in a beta state, and some features of - Qiskit Terra's :obj:`.QuantumCircuit` are not yet supported. In particular, you - may see errors if you try to export custom subroutines with classical - parameters, and there is no provision yet for exporting pulse-calibrated - operations into `OpenPulse `__. - -.. releasenotes/notes/0.19/target-in-transpiler-c0a97bd33ad9417d.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- When running the :class:`~qiskit.transpiler.passes.BasisTranslator` in - isolation with the ``target`` argument set to a - :class:`~qiskit.transpiler.Target` object, where some single-qubit gates - can only apply to non-overlapping sets of qubits, the output circuit might - incorrectly include operations on a qubit that are not allowed by the - :class:`~qiskit.transpiler.Target`. For example, if you ran:: - - from qiskit.circuit import QuantumCircuit, Parameter - from qiskit.circuit.library import UGate, RZGate, XGate, SXGate, CXGate - from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - - from qiskit.transpiler import PassManager, Target, InstructionProperties - from qiskit.transpiler.passes import BasisTranslator - - gmap = Target() - - # U gate in qubit 0. - theta = Parameter('theta') - phi = Parameter('phi') - lam = Parameter('lambda') - u_props = { - (0,): InstructionProperties(duration=5.23e-8, error=0.00038115), - } - gmap.add_instruction(UGate(theta, phi, lam), u_props) - - # Rz gate in qubit 1. - phi = Parameter("phi") - rz_props = { - (1,): InstructionProperties(duration=0.0, error=0), - } - gmap.add_instruction(RZGate(phi), rz_props) - - # X gate in qubit 1. - x_props = { - (1,): InstructionProperties( - duration=3.5555555555555554e-08, error=0.00020056469709026198 - ), - } - gmap.add_instruction(XGate(), x_props) - - # SX gate in qubit 1. - sx_props = { - (1,): InstructionProperties( - duration=3.5555555555555554e-08, error=0.00020056469709026198 - ), - } - gmap.add_instruction(SXGate(), sx_props) - - cx_props = { - (0, 1): InstructionProperties(duration=5.23e-7, error=0.00098115), - (1, 0): InstructionProperties(duration=4.52e-7, error=0.00132115), - } - gmap.add_instruction(CXGate(), cx_props) - - bt_pass = BasisTranslator(sel, target_basis=None, target=gmap) - - qc = QuantumCircuit(2) - qc.iswap(0, 1) - output = bt_pass(qc) - - ``output`` will have :class:`.RZGate` and :class:`.SXGate` on qubit 0, even - though this is forbidden. To correct this you can normally run the basis - translator a second time (i.e. ``output = bt_pass(output)`` in the above - example) to correct this. This should not affect the output of running the - :func:`~qiskit.compiler.transpile` function and is only an issue if you run - the pass by itself. - - -.. _Release Notes_0.19.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.19/7274-6f57628a7995a461.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Starting with this version, ``from qiskit import *`` will not import submodules, but - only a selected list of objects. This might break existing code using - ``from qiskit import *`` and referring to objects that are not part of the - current namespace. As a reminder, ``import *`` is considered bad practice - and it should not be used in production code. Qiskit sets ``__all__`` in - ``qiskit/__init__.py`` as a way to mitigate the effects of said bad - practice. If your code raises ``name '' is not defined``, add - ``from qiskit import `` and try again. - -.. releasenotes/notes/0.19/add-contains_instruction-pass-dcad5f1978ee1e24.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The preset pass managers for optimization levels 0, 1, 2, and 3 which are - generated by - :func:`~qiskit.transpiler.preset_passmanagers.level_0_pass_manager`, - :func:`~qiskit.transpiler.preset_passmanagers.level_1_pass_manager`, - :func:`~qiskit.transpiler.preset_passmanagers.level_2_pass_manager`, and - :func:`~qiskit.transpiler.preset_passmanagers.level_3_pass_manager` - respectively will no longer unconditionally run the - :class:`~qiskit.transpiler.passes.TimeUnitConversion`. Previously, the - preset pass managers would always run this pass regardless of the inputs - to the transpiler and the circuit. Now this pass will only be run if - a ``scheduling_method`` parameter is set or the circuit contains a - :class:`~qiskit.circuit.Delay` instruction and the - ``instruction_durations`` parameter is set. This change was made in - the interest of runtime performance as in some cases running - :func:`~qiskit.compiler.transpile` on circuits with a large number of gates - and no delays, timing, or scheduling being used the - :class:`~qiskit.transpiler.passes.TimeUnitConversion` could be the largest - bottleneck in the transpilation. - -.. releasenotes/notes/0.19/add-gate-error-objective-00a96f75055d1526.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The default method for :obj:`.BIPMapping` is now ``balanced`` rather than - ``depth``. This new objective generally achieves a better result, as it - factors in both the circuit depth and the gate error. - -.. releasenotes/notes/0.19/add-getters-and-setters-for-vqe-edc753591b368980.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``sort_parameters_by_name`` of the :class:`~qiskit.algorithms.VQE` class - has been removed, following its deprecation in Qiskit Terra 0.18. There is - no alternative provided, as the new ordering of parameters is the more - natural sort order. - -.. releasenotes/notes/0.19/added-multiformat-support-b5d3c7c7c1536951.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The circuit drawers :meth:`.QuantumCircuit.draw` and - :func:`.circuit_drawer` with the ``latex`` option will now save their images - in a format determined the file extension (if a file name is provided). - Previously, they would always save in PNG format. They now raise - ``ValueError`` if the image format is not known. This was done to make it - easier to save the image in different formats. - -.. releasenotes/notes/0.19/bump-retworkx-0.10.1-1fcf4fc746bd754a.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The core dependency ``retworkx`` had its version requirement bumped to 0.10.1, up from 0.9. - This enables several performance improvements across different transpilation passes. - -.. releasenotes/notes/0.19/dag_node_to_op_in_out_node-af2cf9c3e7686285.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The previously deprecated ``condition`` kwarg, which was deprecated as part - of the 0.15.0 release, has been removed from - :meth:`.DAGCircuit.apply_operation_back` and - :meth:`.DAGCircuit.apply_operation_front`. Instead set the ``condition`` - attribute on the :class:`~qiskit.circuit.Instruction` instances being added - to the :class:`~qiskit.dagcircuit.DAGCircuit` using :meth:`.Instruction.c_if`. - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``DAGCircuit.extend_back()`` method has been removed. It was originally - deprecated in the 0.13.0 release. Instead you can use the - :meth:`.DAGCircuit.compose` method which is more general - and provides the same functionality. - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``DAGCircuit.compose_back()`` method has been removed. It was originally - deprecated in the 0.13.0 release. Instead you can use the - :meth:`.DAGCircuit.compose` method which is more general - and provides the same functionality. - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``edge_map`` kwarg of the :class:`~qiskit.dagcircuit.DAGCircuit` method - :meth:`~qiskit.dagcircuit.DAGCircuit.compose` has been removed. It was - originally deprecated in the 0.14.0 release. The method takes a ``qubits`` - and ``clbits`` kwargs to specify the positional order of bits to compose - onto instead of using a dictionary mapping that ``edge_map`` previously - provided. - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``DAGCircuit.twoQ_gates()`` method has been removed. It was originally - deprecated in the 0.13.0 release. Instead, - :meth:`.DAGCircuit.two_qubit_ops` should be used. - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``DAGCircuit.threeQ_or_more_gates()`` method has been removed. It was - originally deprecated in the 0.13.0 release. Instead, - :meth:`.DAGCircuit.multi_qubit_ops` method should be used. - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Named access for the first positional argument for the constructor of - the :class:`.SingleQubitUnitary` class with ``u`` has been removed. - It was originally deprecated in the 0.14.0 release. Instead, the first - positional argument can be set using the name ``unitary_matrix`` - (or just set it positionally instead of by name). - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Named access for the first positional argument for the - :class:`~qiskit.circuit.QuantumCircuit` method - :class:`~qiskit.circuit.QuantumCircuit.squ` with ``u`` has been removed. - It was originally deprecated in the 0.14.0 release. Instead the first - positional argument can be set using the name ``unitary_matrix`` - (or just set it positionally instead of by name). - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The unused ``proc`` and ``nested_scope`` kwargs for the ``qasm()`` method - of the QASM node classes in the ``qiskit.qasm.node`` module have been - removed. They were originally deprecated in the 0.15.0 release. - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The unused ``proc`` and ``nested_scope`` kwargs for the ``latex()`` method - of the QASM node classes in the ``qiskit.qasm.node`` module have been - removed. They were originally deprecated in the 0.15.0 release. - -.. releasenotes/notes/0.19/deprecation-cleanup-3d3e203e2d6e6f31.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The unused ``proc`` and ``nested_scope`` kwargs for the ``real()`` method - of the QASM node classes in the ``qiskit.qasm.node`` module have been - removed. They were originally deprecated in the 0.15.0 release. - -.. releasenotes/notes/0.19/draw-statevector-in-ket-notation-0726959d1f6ea3ce.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The output of :meth:`.Statevector.draw` when using ``"latex"`` output is - now the new ``"ket"`` convention if plotting a state comprised purely of qubits. - This was changed to make reading the output clearer, especially in - educational contexts, because it shows the ket labels, and only displays the - nonzero elements. - -.. releasenotes/notes/0.19/execute-fix-108e835dc4f4593d.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- When running :func:`~qiskit.execute_function.execute` with a - :class:`~qiskit.providers.BackendV1` backend the default values for the - kwargs ``shots``, ``max_credits``, ``meas_level``, ``meas_return`` and - ``memory_slot_size`` will now be whatever the set default is on the - target backend's :attr:`~qiskit.providers.BackendV1.options` attribute. - Previously these defaults were set to match the default values when - calling :func:`~qiskit.execute_function.execute` with a legacy - :class:`~qiskit.providers.BaseBackend` backend. For example:: - - from qiskit.test.mock import FakeMumbai - from qiskit import QuantumCircuit, execute - - circuit = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - - backend = FakeMumbai() - backend.set_options(shots=4096) - execute(qc, backend) - - will now run with ``4096`` shots. While in previous releases it would run - with ``1024``. - -.. releasenotes/notes/0.19/mpl-bump-33a1240266e66508.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The minimum supported version of Matplotlib has been raised from 2.1.0 to - 3.3.0. You will now need to have Matplotlib 3.3.0 installed if you're using - Matplotlib-based visualization functions such as the ``'mpl'`` backend for - the :func:`~qiskit.visualization.circuit_drawer` function or the - :func:`~qiskit.visualization.plot_bloch_vector` function. This was done for - two reasons, the first is because recent versions of Matplotlib have - deprecated the use of APIs around 3D visualizations that were compatible - with older releases and second installing older versions of Matplotlib - was becoming increasingly difficult as matplotlib's upstream dependencies - have caused incompatiblities that made testing moving forward more - difficult. - -.. releasenotes/notes/0.19/random-shift-38532a7321cd8313.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The internal use of the random number generator in - :func:`~qiskit.circuit.random.random_circuit` was adjusted, which will - change the output from previous versions, even with a fixed seed. This was - done to greatly improve the runtime scaling with the number of qubits being - used. If you were depending on an identical output from a previous version - it is recommended that you use - :func:`.qpy_serialization.dump` to save the random - circuit generated with a previous version and instead of re-generating it - with the new release, and instead just use - :func:`.qpy_serialization.load` to load that saved circuit. - -.. releasenotes/notes/0.19/remove-deprecated-ops-2fbd27abaee98c68.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The use of ``*`` (``__mul__``) for the - :meth:`~qiskit.quantum_info.Operator.dot` method and ``@`` (``__matmul__``) - for the :meth:`~qiskit.quantum_info.Operator.compose` method of - ``BaseOperator`` (which is the parent of all the operator classes in - :mod:`qiskit.quantum_info` including classes like - :class:`~qiskit.quantum_info.Operator` and - :class:`~qiskit.quantum_info.Pauli`) is no longer supported. The use of - these operators were previously deprecated in 0.17.0 release. Instead you - should use the :meth:`~qiskit.quantum_info.Operator.dot` and - :meth:`~qiskit.quantum_info.Operator.compose` methods directly, or the ``&`` - operator (``__and__``) can be used for - :meth:`~qiskit.quantum_info.Operator.compose`. For example, if you were - previously using the operator like:: - - from qiskit.quantum_info import random_hermitian - - op_a = random_hermitian(4) - op_b = random_hermitian(4) - - new_op = op_a @ op_b - - this should be changed to be:: - - from qiskit.quantum_info import random_hermitian - - op_a = random_hermitian(4) - op_b = random_hermitian(4) - new_op = op_a.compose(op_b) - - or:: - - new_op = op_a & op_b - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Various methods of assigning parameters to operands of pulse program - instructions have been removed, having been deprecated in Qiskit Terra 0.17. - These include: - - * the ``assign()`` method of :obj:`.pulse.Instruction`. - * the ``assign()`` method of ``Channel``, which is the base of - :obj:`.AcquireChannel`, :obj:`.SnapshotChannel`, :obj:`.MemorySlot` and - :obj:`.RegisterSlot`. - * the ``assign()`` and ``assign_parameters()`` methods of - ``ParametricPulse``, which is the base of :obj:`.pulse.Gaussian`, - :obj:`.pulse.GaussianSquare`, :obj:`.pulse.Drag` and :obj:`.pulse.Constant`. - - These parameters should be assigned from the pulse program - (:class:`.pulse.Schedule` and :class:`.pulse.ScheduleBlock`) rather than - operands of the pulse program instruction. - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``flatten()`` method of :class:`.pulse.Instruction` and - :class:`qiskit.pulse.Schedule` has been removed and no longer exists as per - the deprecation notice from Qiskit Terra 0.17. This transformation is - defined as a standalone function in - :func:`qiskit.pulse.transforms.canonicalization.flatten`. - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- ``qiskit.pulse.interfaces.ScheduleComponent`` has been removed and no longer - exists as per the deprecation notice from Qiskit Terra 0.15. No alternative - class will be provided. - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Legacy pulse drawer arguments have been removed from - :meth:`.pulse.Waveform.draw`, :meth:`.Schedule.draw` and - :meth:`.ScheduleBlock.draw` and no longer exist as per the deprecation - notice from Qiskit Terra 0.16. Now these draw methods support only V2 pulse - drawer arguments. See method documentations for details. - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``qiskit.pulse.reschedule`` module has been removed and this import path - no longer exist as per the deprecation notice from Qiskit Terra 0.14. Use - :mod:`qiskit.pulse.transforms` instead. - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- A protected method ``Schedule._children()`` has been removed and replaced by - a protected instance variable as per the deprecation notice from Qiskit - Terra 0.17. This is now provided as a public attribute - :obj:`.Schedule.children`. - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Timeslot relevant methods and properties have been removed and no longer - exist in :class:`~.pulse.ScheduleBlock` as per the deprecation notice from - Qiskit Terra 0.17. Since this representation doesn't have notion of - instruction time ``t0``, the timeslot information will be available after it - is transformed to a :obj:`~.pulse.Schedule`. Corresponding attributes have - been provided after this conversion, but they are no longer supported. The - following attributes are removed: - - * ``timeslots`` - * ``start_time`` - * ``stop_time`` - * ``ch_start_time`` - * ``ch_stop_time`` - * ``shift`` - * ``insert`` - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Alignment pulse schedule transforms have been removed and no longer exist as - per the deprecation notice from Qiskit Terra 0.17. These transforms are - integrated and implemented in the ``AlignmentKind`` context of the schedule - block. The following explicit transform functions are removed: - - * ``qiskit.pulse.transforms.align_equispaced`` - * ``qiskit.pulse.transforms.align_func`` - * ``qiskit.pulse.transforms.align_left`` - * ``qiskit.pulse.transforms.align_right`` - * ``qiskit.pulse.transforms.align_sequential`` - -.. releasenotes/notes/0.19/remove-deprecated-pulse-code-57ec531224e45b5f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Redundant pulse builder commands have been removed and no longer exist as - per the deprecation notice from Qiskit Terra 0.17. - ``pulse.builder.call_schedule`` and ``pulse.builder.call_circuit`` have been - integrated into :func:`.pulse.builder.call`. - -.. releasenotes/notes/0.19/remove-manual-warning-filters-028646b73bb86860.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- An internal filter override that caused all Qiskit deprecation warnings to - be displayed has been removed. This means that the behaviour will now - revert to the standard Python behaviour for deprecations; you should only - see a ``DeprecationWarning`` if it was triggered by code in the main script - file, interpreter session or Jupyter notebook. The user will no longer be - blamed with a warning if internal Qiskit functions call deprecated - behaviour. If you write libraries, you should occasionally run with the - default warning filters disabled, or have tests which always run with them - disabled. See the `Python documentation on warnings`_, and in particular the - `section on testing for deprecations`_ for more information on how to do this. - - .. _Python documentation on warnings: https://docs.python.org/3/library/warnings.html - .. _section on testing for deprecations: https://docs.python.org/3/library/warnings.html#updating-code-for-new-versions-of-dependencies - -.. releasenotes/notes/0.19/remove-manual-warning-filters-028646b73bb86860.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Certain warnings used to be only issued once, even if triggered from - multiple places. This behaviour has been removed, so it is possible that if - you call deprecated functions, you may see more warnings than you did - before. You should change any deprecated function calls to the suggested - versions, because the deprecated forms will be removed in future Qiskit - releases. - -.. releasenotes/notes/0.19/remove-schemas-ca9f3f2e0f08bca8.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The deprecated ``qiskit.schemas`` module and the ``qiskit.validation`` - module which build jsonschema validator from the schemas have been removed. - This was deprecated in the 0.17.0 release and has been replaced with a - `dedicated repository for the IBM Quantum API payload schemas - `__. - - If you were relying on the schema files previously packaged in - ``qiskit.schemas`` or the validators built on them you should use that - repository and create validators from the schema files it contains. - -.. releasenotes/notes/0.19/remove-schemas-ca9f3f2e0f08bca8.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The functions ``qiskit.qobj.validate_qobj_against_schema`` and - ``qiskit.qobj.common.validator`` along with the ``validate`` kwarg of - the methods :meth:`.QasmQobj.to_dict`, - :meth:`.PulseQobj.to_dict`, and :meth:`.Qobj.to_dict` - have been removed. These were deprecated in the 0.17.0 release. If you were - using these function you will have to manually build jsonschema validation - functions for ``Qobj`` objects using the jsonschema files from - `the dedicated repository for the IBM Quantum API payload schemas `__. - -.. releasenotes/notes/0.19/remove-schemas-ca9f3f2e0f08bca8.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``fastjsonschema`` and ``jsonschema`` packages are no longer in the requirements - list for qiskit-terra. The internal use of jsonschema has been removed and - they are no longer required to use qiskit-terra. - -.. releasenotes/notes/0.19/remove-schemas-ca9f3f2e0f08bca8.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The exception raised by the :func:`~.compiler.assemble` function when - invalid parameters are passed in for constructing a - :class:`~qiskit.qobj.PulseQobj` have changed from a ``SchemaValidationError`` - to a :class:`.QiskitError`. This was necessary because - the ``SchemaValidationError`` class was removed along with the rest of - the deprecated ``qiskit.schemas`` and ``qiskit.validation``. This also - makes it more consistent with other error conditions from - :func:`~qiskit.compiler.assemble` which were already raising a - :class:`.QiskitError`. - -.. releasenotes/notes/0.19/sabre-opt-lvl3-default-550924470d683112.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The default routing pass and layout pass for transpiler optimization level 3 has - changed to use :class:`~qiskit.transpiler.passes.SabreSwap` and - :class:`~qiskit.transpiler.passes.SabreLayout` respectively. This - was done to improve the quality of the output result, as using the sabre - passes produces better results than using - :class:`~qiskit.transpiler.passes.StochasticSwap` and - :class:`~qiskit.transpiler.passes.DenseLayout`, which were used as the - defaults in prior releases. This change will improve the quality of the - results when running :func:`~qiskit.compiler.transpile` or - :func:`~qiskit.execute_function.execute` functions with the - ``optimization_level`` kwarg set to ``3``. While this is generally an - improvement, if you need to retain the previous behavior for any reason - you can do this by explicitly setting the ``routing_method="stochastic"`` - and ``layout_method="dense"`` when calling - :func:`~qiskit.compiler.transpile` with ``optimization_level=3``. - -.. releasenotes/notes/0.19/sparse-pauli-internal-8226b4f57a61b982.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The return type of :func:`~qiskit.quantum_info.pauli_basis` will change from - :class:`~qiskit.quantum_info.PauliTable` to - :class:`~qiskit.quantum_info.PauliList` in a future release of Qiskit Terra. - To immediately swap to the new behaviour, pass the keyword argument - ``pauli_list=True``. - -.. releasenotes/notes/0.19/squ-gate-name-785b7896300a92ef.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :attr:`~qiskit.extensions.SingleQubitUnitary.name` attribute of the - :class:`~qiskit.extensions.SingleQubitUnitary` gate class has been changed - from ``unitary`` to ``squ``. This was necessary to avoid a conflict with - the :class:`~qiskit.extensions.UnitaryGate` class's name which was also - ``unitary`` since the 2 gates are not the same and don't have the same - implementation (and can't be used interchangeably). - -.. releasenotes/notes/0.19/symengine-bump-20dedd5204870e7a.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The minimum version of Symengine__ required for installing has been increased - to 0.8.0. This was necessary to fix some issues with the handling of - ``numpy.float16`` and ``numpy.float32`` values when running - :meth:`~qiskit.circuit.ParameterExpression.bind` to bind parameters in a - :class:`~qiskit.circuit.ParameterExpression`. - - .. __: https://pypi.org/project/symengine - -.. releasenotes/notes/0.19/unitary-synthesis-plugin-a5ec21a1906149fa.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- A new dependency `stevedore `__ has - been added to the requirements list. This is required by qiskit-terra as - it is used to build the unitary synthesis plugin interface. - - -.. _Release Notes_0.19.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.19/QuantumCircuit.decompose-takes-which-gate-to-decompose-d857da5d0c41fb66.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``gate`` attribute and initialization parameter of - :class:`qiskit.transpiler.passes.Decompose` is deprecated, and will be - removed in a future release. Instead of this single gate, you should pass a - list of gate names to the new parameter ``gates_to_decompose``. This was - done as the new form allows you to select more than one gate as a - decomposition target, which is more flexible, and does not need to re-run - the pass several times to decompose a set of gates. - -.. releasenotes/notes/0.19/add-pulse-gate-pass-dc347177ed541bcc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- There has been a significant transpiler pass reorganization regarding calibrations. - The import paths:: - - from qiskit.transpiler.passes.scheduling.calibration_creators import RZXCalibrationBuilder - from qiskit.transpiler.passes.scheduling.calibration_creators import RZXCalibrationBuilderNoEcho - - are deprecated, and will be removed in a future release. - The import path:: - - from qiskit.transpiler.passes.scheduling.rzx_templates import rzx_templates - - is also deprecated, and will be removed in a future release. - You should use the new import paths:: - - from qiskit.transpiler.passes import RZXCalibrationBuilder - from qiskit.transpiler.passes import RZXCalibrationBuilderNoEcho - from qiskit.transpiler.passes.calibration.rzx_templates import rzx_templates - -.. releasenotes/notes/0.19/dag_node_to_op_in_out_node-af2cf9c3e7686285.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :class:`~qiskit.dagcircuit.DAGNode` class is being deprecated as a - standalone class and will be used in the future only as the parent class for - :class:`~qiskit.dagcircuit.DAGOpNode`, - :class:`~qiskit.dagcircuit.DAGInNode`, and - :class:`~qiskit.dagcircuit.DAGOutNode`. As part of this deprecation, the - following kwargs and associated attributes in :obj:`.DAGNode` are also being - deprecated: ``type``, ``op``, and ``wire``. - -.. releasenotes/notes/0.19/deprecate-backend-rzx-cal-build-8eda1526725d7e7d.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- For the constructor of the - :class:`~qiskit.transpiler.passes.RZXCalibrationBuilder` passing a backend - either as the first positional argument or with the named ``backend`` kwarg - is deprecated and will no longer work in a future release. Instead - a :class:`~qiskit.pulse.InstructionScheduleMap` should be passed directly to - the ``instruction_schedule_map`` kwarg and a list of channel name lists for - each qubit should be passed directly to ``qubit_channel_mapping``. For example, - if you were calling the pass like:: - - from qiskit.transpiler.passes import RZXCalibrationBuilder - from qiskit.test.mock import FakeMumbai - - backend = FakeMumbai() - cal_pass = RZXCalibrationBuilder(backend) - - instead you should call it like:: - - from qiskit.transpiler.passes import RZXCalibrationBuilder - from qiskit.test.mock import FakeMumbai - - backend = FakeMumbai() - inst_map = backend.defaults().instruction_schedule_map - channel_map = self.backend.configuration().qubit_channel_mapping - cal_pass = RZXCalibrationBuilder( - instruction_schedule_map=inst_map, - qubit_channel_mapping=channel_map, - ) - - This change is necessary because as a general rule backend objects are not - pickle serializable and it would break when it was used with multiple - processes inside of :func:`~qiskit.compiler.transpile` when compiling - multiple circuits at once. - -.. releasenotes/notes/0.19/deprecate-mcmt-label-12865e041ce67658.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The ``label`` property of class - :class:`~qiskit.circuit.library.MCMT` and subclass - :class:`~qiskit.circuit.library.MCMTVChain` has been - deprecated and will be removed in a future release. Consequently, the - ``label`` kwarg on the constructor for both classes is also deprecated, - along with the ``label`` kwarg of method :meth:`.MCMT.control`. - Currently, the ``label`` property is used to name the controlled target - when it is comprised of more than one target qubit, however, this was - never intended to be user-specifiable, and can result in an incorrect - MCMT gate if the name of a well-known operation is used. - After deprecation, the ``label`` property will no longer be - user-specifiable. However, you can get the generated name of the controlled - target via - - :: - - MCMT.data[0][0].base_gate.name - -.. releasenotes/notes/0.19/deprecate-subgraph-coupling_map-93af284f5410e4b0.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :meth:`~qiskit.transpiler.CouplingMap.subgraph` method of the - :class:`~qiskit.transpiler.CouplingMap` class is deprecated and will - be removed in a future release. Instead the - :meth:`~qiskit.transpiler.CouplingMap.reduce` method should be used, which - does the same thing except it preserves the node list order for the output - :class:`~qiskit.transpiler.CouplingMap` (while - :meth:`~qiskit.transpiler.CouplingMap.subgraph` did not preserve list - order). - -.. releasenotes/notes/0.19/fix-instruction-c_if-3334bc8bcc38a327.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Creating an instance of :obj:`.InstructionSet` with the ``circuit_cregs`` - keyword argument is deprecated. In general, these classes never need to be - constructed by users (but are used internally), but should you need to, you - should pass a callable as the ``resource_requester`` keyword argument. For - example:: - - from qiskit.circuit import Clbit, ClassicalRegister, InstructionSet - from qiskit.circuit.exceptions import CircuitError - - def my_requester(bits, registers): - bits_set = set(bits) - bits_flat = tuple(bits) - registers_set = set(registers) - - def requester(specifier): - if isinstance(specifer, Clbit) and specifier in bits_set: - return specifier - if isinstance(specifer, ClassicalRegster) and specifier in register_set: - return specifier - if isinstance(specifier, int) and 0 <= specifier < len(bits_flat): - return bits_flat[specifier] - raise CircuitError(f"Unknown resource: {specifier}") - - return requester - - my_bits = [Clbit() for _ in [None]*5] - my_registers = [ClassicalRegister(n) for n in range(3)] - - InstructionSet(resource_requester=my_requester(my_bits, my_registers)) - -.. releasenotes/notes/0.19/ignis-mitigators-70492690cbcf99ca.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The use of the measurement mitigation classes - :class:`qiskit.ignis.mitigation.CompleteMeasFitter` and - :class:`qiskit.ignis.mitigation.TensoredMeasFitter` from ``qiskit-ignis`` - as values for the ``measurement_error_mitigation_cls`` kwarg of the - constructor for the :class:`~qiskit.utils.QuantumInstance` class is - deprecated and will be removed in a future release. Instead the equivalent - classes from :mod:`qiskit.utils.mitigation`, - :class:`~qiskit.utils.mitigation.CompleteMeasFitter` and - :class:`~qiskit.utils.mitigation.TensoredMeasFitter` should be used. This - was necessary as the ``qiskit-ignis`` project is now deprecated and will - no longer be supported in the near future. - It's worth noting that unlike the equivalent classes from ``qiskit-ignis`` - the versions from :mod:`qiskit.utils.mitigation` are supported only in - their use with :class:`~qiskit.utils.QuantumInstance` (i.e. as a class not - an instance with the ``measurement_error_mitigation_cls`` kwarg) and not - intended for standalone use. - -.. releasenotes/notes/0.19/optimizer-minimize-5a5a1e9d67db441a.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :meth:`.Optimizer.optimize` method for all the optimizers - (:class:`~qiskit.algorithms.optimizers.Optimizer` and derived classes) is - now deprecated and will be removed in a future release. Instead, the - :meth:`.Optimizer.minimize` method should be used which mimics the signature - of SciPy's ``minimize()`` function. - - To replace the current `optimize` call with `minimize` you can replace - - .. code-block:: python - - xopt, fopt, nfev = optimizer.optimize( - num_vars, - objective_function, - gradient_function, - variable_bounds, - initial_point, - ) - - with - - .. code-block:: python - - result = optimizer.minimize( - fun=objective_function, - x0=initial_point, - jac=gradient_function, - bounds=variable_bounds, - ) - xopt, fopt, nfev = result.x, result.fun, result.nfev - -.. releasenotes/notes/0.19/qiskit.util-louder-deprecation-135d9e9ead7ab396.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Importing the ``qiskit.util`` module will now issue a ``DeprecationWarning``. - Users should instead import all the same functionality from :obj:`qiskit.utils`. - The ``util`` module has been deprecated since Terra 0.17, but previously did not issue a warning. - It will be removed in Terra 0.20. - -.. releasenotes/notes/0.19/sparse-pauli-internal-8226b4f57a61b982.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The property :attr:`~qiskit.quantum_info.SparsePauliOp.table` is deprecated, - and will be removed in a future release. This is because - :class:`~qiskit.quantum_info.SparsePauliOp` has been updated to internally use - :class:`~qiskit.quantum_info.operators.PauliList` instead of - :class:`~qiskit.quantum_info.PauliTable`. This is in order to significantly - improve performance. You should now access the :obj:`.PauliList` data by - using the :attr:`.SparsePauliOp.paulis` attribute. - - -.. _Release Notes_0.19.0_Bug Fixes: - -Bug Fixes ---------- - -.. releasenotes/notes/0.19/7156-df1a60c608b93184.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed a bug where many layout methods would ignore 3-or-more qubit gates, - resulting in unexpected layout-allocation decisions. The transpiler pass - :class:`.Unroll3qOrMore` is now being executed before the layout pass in all - the preset pass managers when :func:`~.compiler.transpile` is called. Fixed `#7156 - `__. - -.. releasenotes/notes/0.19/add-circuit-calibrations-to-diassambler-2e68437a815cc729.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Disassembled circuits now inherit calibrations from assembled - :obj:`.QasmQobj` and experiments. Fixes `#5348 - `__. - -.. releasenotes/notes/0.19/add-getters-and-setters-for-vqe-edc753591b368980.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed setting the ``ansatz`` or ``optimizer`` attributes of a - :obj:`~qiskit.algorithms.VQE` instance to ``None`` resulting in a buggy - behavior. See `#7093 - `__ for details. - -.. releasenotes/notes/0.19/add-sparsepauliop-fast-path-228065a05fca4387.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed addition of :obj:`.PauliList`\ s with ``qargs``. The method used to raise a runtime error - if the operands had different numbers of qubits. - -.. releasenotes/notes/0.19/bugfix-6918-4b3cc4056df39e48.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue causing an error when trying to compute a gradient with the - :class:`~qiskit.opflow.gradients.CircuitGradient` class for a gate that was - not a supported gate. This bugfix transpiles a given gate to the set of - supported gates for a requested gradient method. Fixes `#6918 - `__. - -.. releasenotes/notes/0.19/calibration_results-ac2f9f75479e8d64.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Removed calibration results when using error mitigation with the - :meth:`~qiskit.utils.QuantumInstance.execute` method of - :class:`~qiskit.utils.QuantumInstance`. Fixes `#7129 - `__. - -.. releasenotes/notes/0.19/expr_free_symbols_deprecation-72e4db5c178efcff.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed a deprecation warning emitted when running - :meth:`.QuantumCircuit.draw` or :func:`.circuit_drawer` with Sympy 1.9 - installed, mentioning the Sympy function ``expr_free_symbols()``. - The circuit drawers previously made use of this method when finding - instances of symbolic constants. - -.. releasenotes/notes/0.19/fix-ax-figwidth-scaling-mpl-drawer-dc480ccf82dc1007.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue where the ``ax`` kwarg and the ``figwidth`` option in the - ``style`` kwarg for the ``mpl`` circuit drawer did not scale properly. - Users can now pass an ``ax`` from a Matplotlib subplot to the ``mpl`` - circuit drawer and the circuit will be drawn within the boundaries of - that subplot. Alternatively, users can set the ``figwidth`` in inches in - the ``style`` dict kwarg and the drawing will scale to the width in - inches that was set. - Fixed `#6367 `__. - -.. releasenotes/notes/0.19/fix-bit-failures-circuit-drawers-cc502c9cb7f90e2b.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` - function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of - :class:`~qiskit.circuit.QuantumCircuit`. When displaying a ``measure`` - instruction targeted on a classical bit instead of a register, using - the ``latex`` drawer option, the drawer would fail. - -.. releasenotes/notes/0.19/fix-bit-failures-circuit-drawers-cc502c9cb7f90e2b.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` - function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of - :class:`~qiskit.circuit.QuantumCircuit`. With any of the 3 drawer - options, ``mpl``, ``latex``, or ``text``, if a gate with a classical - condition was encountered that was conditioned on a classical bit - without a register, the drawer would fail. - -.. releasenotes/notes/0.19/fix-bit-failures-circuit-drawers-cc502c9cb7f90e2b.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` - function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of - :class:`~qiskit.circuit.QuantumCircuit`. With any of the 3 drawer - options, ``mpl``, ``latex``, or ``text``, if a gate with a classical - condition was conditioned on the same classical bit as a ``measure`` - and the bit that the measure targeted did not have a register, the - drawer would fail. - -.. releasenotes/notes/0.19/fix-c3sxgate-7138e004a2b05ca8.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- :obj:`~qiskit.circuit.library.C3SXGate` now has a correct decomposition and - matrix representation. Previously it was equivalent to - ``SdgXGate().control(3)``, rather than the intended ``SXGate().control(3)``. - -.. releasenotes/notes/0.19/fix-configurable-fake-backend-6a07ca5a6159baf5.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The member ``name`` of ``qiskit.test.mock.utils.ConfigurableFakeBackend`` - has been changed to ``backend_name``. This was done to avoid a conflict with - the :meth:`~qiskit.providers.BackendV1.name` method inherited from the - parent abstract :class:`~qiskit.providers.BackendV1` class. This makes - ``ConfigurableFakeBackend`` compatible with anything expecting a - :class:`~qiskit.providers.BackendV1` object. However, if you were using the - ``name`` attribute directly before you will now need to either call it as a - method or access the ``backend_name`` attribute instead. - -.. releasenotes/notes/0.19/fix-decompose-empty-gate-71455847dcaaea26.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue where calling :meth:`.QuantumCircuit.decompose()` on a - circuit containing an :class:`~qiskit.circuit.Instruction` whose - :attr:`~.Instruction.definition` attribute was empty would leave the - instruction in place, instead of decomposing it into zero operations. For - example, with a circuit:: - - from qiskit.circuit import QuantumCircuit - empty = QuantumCircuit(1, name="decompose me!") - circuit = QuantumCircuit(1) - circuit.append(empty.to_gate(), [0]) - - Previously, calling ``circuit.decompose()`` would not change the circuit. - Now, the decomposition will correct decompose ``empty`` into zero - instructions. - See `#6997 `__ for more. - -.. releasenotes/notes/0.19/fix-display-measure-condition-139ddbb8c7ae4071.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` - function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of - :class:`~qiskit.circuit.QuantumCircuit`. When displaying a ``measure`` - instruction containing a classical ``condition`` using the ``mpl`` or - ``latex`` options, the ``condition`` information would sometimes - overwrite the ``measure`` display. - -.. releasenotes/notes/0.19/fix-display-measure-condition-139ddbb8c7ae4071.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` - function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of - :class:`~qiskit.circuit.QuantumCircuit`. The ``mpl`` drawer used hex - notation to display the ``condition`` value, whereas the ``text`` and - ``latex`` drawers used decimal notation. Now all three drawers use - hex notation. - -.. releasenotes/notes/0.19/fix-hoare-optimizer-0ef5ac330fc80cc4.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed a bug in the Hoare optimizer transpilation pass where it could attempt - to remove a gate twice if it could be separately combined with both its - predecessor and its successor to form the identity. Refer to `#7271 - `__ for more details. - -.. releasenotes/notes/0.19/fix-instruction-c_if-3334bc8bcc38a327.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Making an instruction conditional with the standard - :meth:`.InstructionSet.c_if` method with integer indices is now consistent - with the numbering scheme used by the :obj:`.QuantumCircuit` the - instructions are part of. Previously, if there were two - :obj:`.ClassicalRegister`\ s with overlapping :obj:`.Clbit`\ s, the - numbering would be incorrect. See `#7246 - `__ for more detail. - -.. releasenotes/notes/0.19/fix-instruction-c_if-3334bc8bcc38a327.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Making an instruction conditional with the standard - :meth:`.InstructionSet.c_if` method will now succeed, even if there are no - :obj:`.ClassicalRegister`\ s in the circuit. - See `#7250 `__ for more detail. - -.. releasenotes/notes/0.19/fix-instruction-c_if-3334bc8bcc38a327.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Making an instruction conditional with the standard - :meth:`.InstructionSet.c_if` method when using a :obj:`.Clbit` that is - contained in a :obj:`.ClassicalRegister` of size one will now correctly - create a condition on the bit, not the register. - See `#7255 `__ for more detail. - -.. releasenotes/notes/0.19/fix-instruction-c_if-3334bc8bcc38a327.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Trying to make an instruction conditional with the standard - :meth:`.InstructionSet.c_if` method will now correctly raise an error if the - classical resource is not present in the circuit. - See `#7255 `__ for more detail. - -.. releasenotes/notes/0.19/fix-matplotlib-3.5-40f6d1a109ae06fe.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed a compatibility issue with Matplotlib 3.5, where the Bloch sphere - would fail to render if it had any vectors attached, such as by using - :obj:`~qiskit.visualization.plot_bloch_vector`. See `#7272 - `__ for more detail. - -.. releasenotes/notes/0.19/fix-nlocal-add_layer-c3cb0b5a49c2b04e.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with the :meth:`.NLocal.add_layer` method incorrectly - appending layers if the :obj:`.NLocal` object had already been built. - -.. releasenotes/notes/0.19/fix-pickle-support-instmap-9f90cbcb4078f988.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with pickling :class:`~.pulse.InstructionScheduleMap` object - when using Python 3.6. See `#6944 - `__ for details. - -.. releasenotes/notes/0.19/fix-pulse-parameter-formatter-c9ff103f1a7181e0.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Complex valued pulse parameter assignment with symengine has been fixed. For example, - - .. code-block:: python - - from qiskit import circuit, pulse - import numpy as np - - amp = circuit.Parameter("amp") - phase = circuit.Parameter("phase") - - with pulse.build() as sched: - pulse.play(pulse.Gaussian(160, amp * np.exp(1j * phase), 40), pulse.DriveChannel(0)) - sched.assign_parameters({amp: 0.1, phase: 1.57}, inplace=True) - - The assigned amplitude has been shown as - ``ParameterExpression(0.1*exp(1.57*I))`` after the use of ``symengine`` was - introduced in the 0.18.0 release. This is now correctly evaluated and shown - as ``7.96327e-05 + 0.0999999683j``. - -.. releasenotes/notes/0.19/fix-qaoa-construct-da37faf75f29fc35.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue where :meth:`.QAOA.construct_circuit` with different - operators with same number of qubits would generate the same circuit each - time. See `#7223 `__ - for more detail. - -.. releasenotes/notes/0.19/fix-qaoa-construct-da37faf75f29fc35.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue where :class:`~qiskit.circuit.library.QAOAAnsatz` had an - incorrect number of parameters if identities of - :class:`~qiskit.opflow.PauliSumOp` were given, e.g., - ``PauliSumOp.from_list([("III", 1)])``. See `#7225 - `__ for more detail. - -.. releasenotes/notes/0.19/fix-qasm-invalid-names-04a935a3a14e045c.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed a bug where the :meth:`.QuantumCircuit.qasm` method could - return OpenQASM 2 instructions with invalid identifiers. The same bug was fixed - for :obj:`~qiskit.extensions.UnitaryGate`. - -.. releasenotes/notes/0.19/fix-registerless-one-bit-displays-4deb8f7cecf3f602.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue where trying to display registerless bits would cause a - failure of the ``mpl`` and the ``latex`` circuit drawers. A leading ``_`` - has been removed from the display of registerless bits' numbers in the - ``text`` drawer. Fixed `#6732 - `__. - -.. releasenotes/notes/0.19/fix-registerless-one-bit-displays-4deb8f7cecf3f602.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- For one-bit registers, all of the circuit drawers now display only - the register name and no longer show the ``0`` subscript. - Fixed `#5784 `__. - -.. releasenotes/notes/0.19/fix-registerless-qasm-output-7a497dd8e9a0706b.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed naming collisions of implicit registers in :obj:`.QuantumCircuit.qasm` - when dealing with registerless qubits and clbits. Previously, registerless - qubits and clbits were put into corresponding ``qreg`` and ``creg`` both - called ``regless``, despite the collision. They will now have separate, - deterministically generated names, which will not clash with any - user-defined register names in the circuit. - -.. releasenotes/notes/0.19/fix-scheduling-circuits-with-clbits-operations-e5d8bfa90e9a3ae1.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue in scheduling of circuits with clbits operations, e.g. measurements, - conditional gates, updating - :class:`~qiskit.transpiler.passes.ASAPSchedule`, - :class:`~qiskit.transpiler.passes.ALAPSchedule`, and - :class:`~qiskit.transpiler.passes.AlignMeasures`. - The updated schedulers assume all clbits I/O operations take no time, - ``measure`` writes the measured value to a clbit at the end, and - ``c_if`` reads the conditional value in clbit(s) at the beginning. - Fixed `#7006 `__. - -.. releasenotes/notes/0.19/fix-transpile-empty-list-c93a41d4145a01c3.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Calling :obj:`~qiskit.compiler.transpile` on an empty list will now - correctly return an empty list without issuing a warning. Fixed `#7287 - `__. - -.. releasenotes/notes/0.19/fix_pwchebysev_constant_fx-93e8a3d2880f68ac.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue in :obj:`.PiecewiseChebyshev` when the function to be - approximated was constant. In these cases, you should now pass the constant - directly as the ``f_x`` argument, rather than using a function, such as:: - - from qiskit.circuit.library.arithmetic import PiecewiseChebyshev - - PiecewiseChebyshev(1.0, degree=3) - - See `#6707 `__ for more details. - -.. releasenotes/notes/0.19/hhl_qi_fix-d0ada86abaa2dba5.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- If an :class:`~qiskit.algorithms.HHL` algorithm instance was constructed - without a :obj:`.QuantumInstance` (the default), attempts to use the getter - and setter properties to read or set an instance later would fail. The - getters and setters now work as expected. - -.. releasenotes/notes/0.19/modify-copy-instruction-in-qasm-abd5c9767f2a7f38.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :meth:`.QuantumCircuit.qasm` method now edits the names of copies of the - instructions present in the circuit, not the original instructions that live - in ``circuit.data``. Refer to `#6952 - `__ for more details. - -.. releasenotes/notes/0.19/pauli-op-permute-fix-d244a1145093369d.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed a bug in :meth:`.PauliSumOp.permute` causing the error:: - - QiskitError: 'Pauli string label "" is not valid.' - - if the permutation had the same number of Pauli terms. Calling - ``permute([2, 1, 0])`` on ``X ^ Y ^ Z`` no longer raises an error, and now - returns ``Z ^ Y ^ X``. - -.. releasenotes/notes/0.19/qaoa-parameters-49e4524ed2d3e875.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed a bug where the parameter bounds for the mixer parameters in the - :class:`~qiskit.circuit.library.QAOAAnsatz` were not been set. - -.. releasenotes/notes/0.19/remove-final-measure-rewrite-37d26dbba7385ebc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed determination of final operations (barriers and measures) in pass - :class:`~qiskit.transpiler.passes.RemoveFinalMeasurements` and in method - :meth:`~qiskit.circuit.QuantumCircuit.remove_final_measurements` - of class :class:`~qiskit.circuit.QuantumCircuit` which previously considered - only nodes immediately preceding an output node. - -.. releasenotes/notes/0.19/remove-final-measure-rewrite-37d26dbba7385ebc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed determination of final operations in pass - :class:`~qiskit.transpiler.passes.RemoveFinalMeasurements` and in method - :meth:`~qiskit.circuit.QuantumCircuit.remove_final_measurements` of class - :class:`~qiskit.circuit.QuantumCircuit` which could wrongly consider a barrier - to be final, even if other circuit operations followed it. - -.. releasenotes/notes/0.19/remove-final-measure-rewrite-37d26dbba7385ebc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed multi-bit classical register removal in pass - :class:`~qiskit.transpiler.passes.RemoveFinalMeasurements` and in method - :meth:`~qiskit.circuit.QuantumCircuit.remove_final_measurements` of class - :class:`~qiskit.circuit.QuantumCircuit` where classical - registers were not removed even if other bits were idle, unless a final measure - was done into each and every bit. Now, classical registers that become idle as a - result of removing final measurements and barriers are always removed. Classical - bits are removed if they are referenced only by removed registers or are not - referenced at all and became idle due to the removal. This fix also adds proper - handling of registers with shared underlying bits. - -.. releasenotes/notes/0.19/remove-final-measure-rewrite-37d26dbba7385ebc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with :obj:`~qiskit.transpiler.passes.RemoveFinalMeasurements` - which could cause the resulting :obj:`.DAGCircuit` to become invalid. See - `#7196 `__ for more details. - -.. releasenotes/notes/0.19/remove-final-measure-rewrite-37d26dbba7385ebc.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with method :meth:`~qiskit.circuit.QuantumCircuit.remove_final_measurements` - of class :class:`~qiskit.circuit.QuantumCircuit` that caused :attr:`.QuantumCircuit.clbits` - to be incorrect after invocation. Refer to - `#7089 `__ for details. - -.. releasenotes/notes/0.19/taper_empty_operator_fix-53ce20e5d2b68fd6.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- When tapering an empty zero operator in :mod:`qiskit.opflow`, the code, on detecting it was zero, logged a - warning and returned the original operator. Such operators are commonly found in - the auxiliary operators, when using Qiskit Nature, and the above behavior caused - :obj:`~qiskit.algorithms.minimimum_eigen_solvers.VQE` - to throw an exception as tapered non-zero operators were a different number of qubits - from the tapered zero operators (since taper has returned the input operator unchanged). - The code will now correctly taper a zero operator such that the number of qubits is - reduced as expected and matches to tapered non-zero operators e.g ```0*"IIII"``` when we are - tapering by 3 qubits will become ``0*"I"``. - -.. releasenotes/notes/0.19/user-config-mpl-style-a9ae4eb7ce072fcd.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- Fixed an issue with the :meth:`~qiskit.circuit.QuantumCircuit.draw` method and - :func:`~qiskit.visualization.circuit_drawer` function, where a custom style set via the - user config file (i.e. ``settings.conf``) would ignore the set value of the - ``circuit_mpl_style`` field if the ``style`` kwarg on the function/method was not - set. - - -.. _Release Notes_0.19.0_Other Notes: - -Other Notes ------------ - -.. releasenotes/notes/0.19/full_prec_sympy-aeee8210091ef20f.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The string cast for :class:`qiskit.circuit.ParameterExpression` does not - have full precision anymore. This removes the trailing 0s when printing - parameters that are bound to floats. This has consequences for QASM - serialization and the circuit text drawer:: - - >>> from qiskit.circuit import Parameter - >>> x = Parameter('x') - >>> str(x.bind({x:0.5})) - '0.5' # instead of '0.500000000000000' - -.. releasenotes/notes/0.19/qaoa-parameters-49e4524ed2d3e875.yaml @ b'd5094eeca27f2c0f3c13f23f1e812cd41b6108f2' - -- The :class:`~qiskit.circuit.library.QAOAAnsatz` has been updated to use the parameter - symbol ``γ`` for the cost operator and ``β`` for the mixer operator, as is the standard - notation in QAOA literature. - -Aer 0.9.1 -========= - -No change - - -.. _Release Notes_Ignis_0.7.0: - -Ignis 0.7.0 -=========== - -.. _Release Notes_Ignis_0.7.0_Prelude: - -Prelude -------- - -.. releasenotes/notes/0.7/deprecate-ignis-def3e398d9d86ac5.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -This release deprecates the Qiskit Ignis project, it has been supersceded by the -`Qiskit Experiments `__ project and active -development has ceased. While deprecated, critical bug fixes and compatibility fixes will -continue to be made to provide users a sufficient opportunity to migrate off of Ignis. After the -deprecation period (which will be no shorter than 3 months from this release) the project will be -retired and archived. - -.. _Release Notes_Ignis_0.7.0_New Features: - -New Features ------------- - -.. releasenotes/notes/0.7/accreditation-rework-193c331d6f85dc57.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -- Updated the accreditation protocol to use fitting routine from - https://arxiv.org/abs/2103.06603. - :class:`~qiskit.ignis.verification.accreditation.AccreditationFitter` - now has methods FullAccreditation (previous protocol) and MeanAccreditation - (new protocol). In addtition data entry has been changed to either - use the result object AppendResult or a list of strings AppendStrings. - :func:`qiskit.ignis.verification.QOTPCorrectString` was also added. - -.. releasenotes/notes/0.7/analytical-syndrome-graph-1cbc0a900c987ad8.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -- Added the option for the fast analytical generation of syndrome graphs. - The :class:`.RepetitionCode` now has a new bool argument ``brute``, which - allows to still use the brute force method. - Helper class :class:`.RepetitionCodeSyndromeGenerator` added to - facilitate this. - -.. releasenotes/notes/0.7/optional-resets-and-delays-2cd301f1257b3962.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -- The :class:`~qiskit.ignis.verification.RepetitionCode` now has keyword - arguments ``resets`` and ``delay``. The former determines whether reset - gates are inserted after measurement. The latter allows a time (in dt) to - be specificed for a delay after each measurement (and reset, if applicable). - - The :meth:`~qiskit.ignis.verification.RepitionCode.syndrome_measurement` method of - :class:`~qiskit.ignis.verification.RepetitionCode` now has keyword - arguments ``final`` and ``delay``. The former determines whether to add reset gates according - to the global ``resets``, or to overwrite it with appropriate behavior for the - final round of syndrome measurements. The latter allows a time (in dt) to be specificed - for a delay after each measurement (and reset, if applicable). - -.. releasenotes/notes/0.7/xbasis-encoding-e9d008b027b5d7d9.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -- The :class:`.RepetitionCode` class now supports encoding with x basis - states. This can be used by setting the ``xbasis`` keyword argument when - constructing a :class:`.RepetitionCode` object. - - -.. _Release Notes_Ignis_0.7.0_Upgrade Notes: - -Upgrade Notes -------------- - -.. releasenotes/notes/0.7/optional-resets-and-delays-2cd301f1257b3962.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -- The keyword argument ``reset`` has been removed from the - the :meth:`~qiskit.ignis.verification.RepitionCode.syndrome_measurement` - method of :class:`~qiskit.ignis.verification.RepetitionCode`. This is - replaced by the global ``resets`` keyword argument for the class as well as - the keyword argument ``final`` for ``syndrome_measurement``. In cases where - one would previously add the final measurement round using ``reset=False`` - to avoid the final reset gates, one should now use ``final=True``. - -.. releasenotes/notes/0.7/remove-parametrized-schedule-dependency-71f43e478d9a4080.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -- Remove ``ParametrizedSchedule`` from - :py:func:`~qiskit.ignis.characterization.calibrations.ibmq_utils.update_u_gates`. - - ``ParametrizedSchedule`` was deprecated as a part of Qiskit-terra 0.17.0 and will be - removed in next release. The function now updates u gates with ``Schedule`` programs - involving unassigned ``Parameter`` objects. - - -.. _Release Notes_Ignis_0.7.0_Deprecation Notes: - -Deprecation Notes ------------------ - -.. releasenotes/notes/0.7/accreditation-rework-193c331d6f85dc57.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -- Deprecating methods in - :class:`~qiskit.ignis.verification.accreditation.AccreditationFitter` - namely bound_variation_distance and single_protocol_run - -.. releasenotes/notes/0.7/deprecate-ignis-def3e398d9d86ac5.yaml @ b'4c45654256ce8fecb60cb1d9d5ff481d6efd3428' - -- The Qiskit Ignis project as a whole has been deprecated and the project - will be retired and archived in the future. While deprecated only - compatibility fixes and fixes for critical bugs will be made to the proejct. - Instead of using Qiskit Ignis you should migrate to use - `Qiskit Experiments `__ - instead. You can refer to the migration guide: - - https://github.com/Qiskit/qiskit-ignis#migration-guide - - -############# -Qiskit 0.32.1 -############# - -Terra 0.18.3 -============ - -No change - -Aer 0.9.1 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.5 -========== - -No change - -.. _Release Notes_0.18.1_IBMQ: - -IBM Q Provider 0.18.1 -===================== - -.. _Release Notes_0.18.1_IBMQ_Bug Fixes: - -Bug Fixes ---------- - -- Fixes `#209 `__ where the websocket - connection kept timing out when streaming results for a runtime job, due to inactivity, - when the job is in a pending state for a long time. - -############# -Qiskit 0.32.0 -############# - -Terra 0.18.3 -============ - -No change - -Aer 0.9.1 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.5 -========== - -No change - -.. _Release Notes_0.18.0_IBMQ: - -IBM Q Provider 0.18.0 -===================== - -.. _Release Notes_0.18.0_IBMQ_New Features: - -New Features ------------- - -- You can now pass ``program_id`` parameter to - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.jobs` - method to filter jobs by Program ID. - -- You can view the last updated date of a runtime program using - :attr:`~qiskit.providers.ibmq.runtime.RuntimeProgram.update_date` property. - -- If you are the author of a runtime program, - you can now use :attr:`qiskit.providers.ibmq.runtime.RuntimeProgram.data` - property to retrieve the program data as a string. - -- You can now use the :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.update_program` - method to update the metadata for a Qiskit Runtime program. - Program metadata can be specified using the ``metadata`` parameter or - individual parameters, such as ``name`` and ``description``. If the - same metadata field is specified in both places, the individual parameter - takes precedence. - -- You can now use the :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.update_program` - method to update the data of an existing runtime program. - - -.. _Release Notes_0.18.0_IBMQ_Upgrade Notes: - -Upgrade Notes -------------- - -- Runtime programs will no longer have a ``version`` field. - -- By default, :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.pprint_programs()` - now only prints the summary of each runtime program instead of all of the details. - There is a new parameter ``detailed`` that can be set to ``True`` to print all details. - -- ``limit`` and ``skip`` parameters have been added to - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.programs` and - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.pprint_programs`. - ``limit`` can be used to set the number of runtime programs returned - and ``skip`` is the number of programs to skip when retrieving - programs. - -- The `data` parameter to :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.upload_program` - can now only be of type string. It can be either the program data, - or path to the file that contains program data. - -- :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.upload_program` now takes only two - parameters, ``data``, which is the program passed as a string or the path to the program - file and the ``metadata``, which is passed as a dictionary or path to the metadata JSON file. - In ``metadata`` the ``backend_requirements``, ``parameters``, ``return_values`` and - ``interim_results`` are now grouped under a specifications ``spec`` section. - ``parameters``, ``return_values`` and ``interim_results`` should now be specified as - JSON Schema. - -- :meth:`qiskit.providers.ibmq.AccountProvider.run_circuits` method now takes a `backend_name` - parameter, which is a string, instead of `backend`, which is a ``Backend`` object. - -- The default number of ``shots`` (represents the number of repetitions of each circuit, - for sampling) in :meth:`qiskit.providers.ibmq.IBMQBackend.run`, has been increased from - 1024 to 4000. - - -.. _Release Notes_0.18.0_IBMQ_Bug Fixes: - -Bug Fixes ---------- - -- Fixes the issue wherein a runtime job result cannot be retrieved multiple - times if the result contains a numpy array. - -############# -Qiskit 0.31.0 -############# - -Terra 0.18.3 -============ - -No change - -.. _Release Notes_0.9.1_Aer: - -Aer 0.9.1 -========= - -.. _Release Notes_0.9.1_Aer_Upgrade Notes: - -Upgrade Notes -------------- - -- ``optimize_ideal_threshold`` and ``optimize_noisy_threshold`` have been - removed from the lists of simulator defaults and the documentation. - These have had no effect since Aer 0.5.1, but these references to them - had remained accidentally. - -.. _Release Notes_0.9.1_Aer_Bug Fixes: - -Bug Fixes ---------- - -- Fixes `#1351 `__ - where running an empty :obj:`~qiskit.circuit.QuantumCircuit` with - a noise model set would cause the simulator to crash. - -- Fixes `#1347 `__ - where the behaviour of using the - :meth:`~qiskit.providers.aer.AerSimulator.set_options` and - :meth:`~qiskit.providers.aer.AerSimulator.set_option` methods of - simulator backends could lead to different behavior for some options. - -- Fixes an bug where using a Dask Client executor would cause an error at - job submission due to the executor Client not being pickleable. - -- Fixed an issue with the `matrix_product_state` simulation method where - the accumulation of small rounding errors during measurement of many - quits could sometimes cause a segmentation fault. - -- Fixes an unintended change between qiskit-aer 0.8.0 and 0.9.0 where when - running a list of circuits with an invalid circuit using the ``automatic`` - simulation method of the :class:`~qiskit.providers.aer.AerSimulator` or - :class:`~qiskit.providers.aer.QasmSimulator` would raise an exception - for an invalid input qobj rather than return partial results for the - circuits that were valid. - -- Fixes an issue with the standalone simulator where it would return a - `IBM Quantum API schema `__ - invalid response in the case of an error that prevented the simulation from running. - -- Fixes `#1346 `__ - which was a bug in the handling of the ``parameter_binds`` kwarg of - the backend :meth:`~qiskit.providers.aer.AerSimulator.run` method that - would result in an error if the parameterized circuit was transpiled to - a different set of basis gates than the original parameterizations. - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.5 -========== - -No change - -.. _Release Notes_0.17.0_IBMQ: - -IBM Q Provider 0.17.0 -===================== - -.. _Release Notes_0.17.0_IBMQ_New Features: - -New Features ------------- - -- A runtime program's visibility can now be specified on upload - using ``is_public`` parameter in - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.upload_program`. - -- You can now specify a parent experiment ID when creating an experiment - with :meth:`qiskit.providers.ibmq.experiment.IBMExperimentService.create_experiment`. - Experiments can also be filtered by their parent experiment ID in - :meth:`qiskit.providers.ibmq.experiment.IBMExperimentService.experiments`. - -- Runtime image can now be specified using the `image` parameter in - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.run`. - Note that not all accounts are authorized to select a different image. - - -.. _Release Notes_0.17.0_IBMQ_Upgrade Notes: - -Upgrade Notes -------------- - -- :class:`qiskit.providers.ibmq.runtime.RuntimeEncoder` and - :class:`qiskit.providers.ibmq.runtime.RuntimeDecoder` - are updated to support Python ``datetime``, which is not - JSON serializable by default. - - -.. _Release Notes_0.17.0_IBMQ_Bug Fixes: - -Bug Fixes ---------- - -- Fixes the issue where - :meth:`qiskit.providers.ibmq.managed.IBMQJobManager.retrieve_job_set` only - retrieves the first 10 jobs in a :class:`qiskit.providers.ibmq.managed.ManagedJobSet`. - -- :class:`qiskit.providers.ibmq.runtime.RuntimeDecoder` can now restore dictionary integer keys - in optimizer settings from a JSON string representation dumped by the - :class:`qiskit.providers.ibmq.runtime.RuntimeEncoder`. - -############# -Qiskit 0.30.1 -############# - -.. _Release Notes_0.18.3: - -Terra 0.18.3 -============ - -Prelude -------- - -This bugfix release fixes a few minor issues in 0.18, including a performance -regression in :obj:`~qiskit.compiler.assemble` when dealing with executing -:class:`~qiskit.circuit.QuantumCircuit` objects on pulse-enabled backends. - -.. _Release Notes_0.18.3_Bug Fixes: - -Bug Fixes ---------- - -- Fixed `#7004 `__ where - ``AttributeError`` was raised when executing - :obj:`~qiskit.pulse.ScheduleBlock` on a pulse backend. These blocks are now - correctly treated as pulse jobs, like :obj:`~qiskit.pulse.Schedule`. - -- Fixed an issue causing an error when binding a complex parameter value to an operator's - coefficient. Casts to ``float`` in :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - were generalized to casts to ``complex`` if necessary, but will remain ``float`` if - there is no imaginary component. - Fixes `#6976 `__. - -- Update the 1-qubit gate errors in - :obj:`~qiskit.visualization.plot_error_map` to use the `sx` gate instead of - the `u2` gate, consistent with IBMQ backends. - -Aer 0.9.0 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.5 -========== - -No change - -IBM Q Provider 0.16.0 -===================== - -No change - -############# -Qiskit 0.30.0 -############# - -Terra 0.18.2 -============ - -No change - -.. _Release Notes_Aer_0.9.0: - -Aer 0.9.0 -========= - -.. _Release Notes_Aer_0.9.0_Prelude: - -Prelude -------- - -The 0.9 release includes new backend options for parallel exeuction -of large numbers of circuits on a HPC cluster using a Dask distributed, -along with other general performance improvements and bug fixes. - - -.. _Release Notes_0.9.0_Aer_New Features: - -New Features ------------- - -- Added support for set_matrix_product_state. - -- Add qiskit library :class:`~qiskit.circuit.library.SXdgGate` - and :class:`~qiskit.circuit.library.CUGate` to the supported basis gates for - the Aer simulator backends. Note that the :class:`~qiskit.circuit.library.CUGate` - gate is only natively - supported for the ``statevector`` and ``unitary`` methods. For other simulation - methods it must be transpiled to the supported basis gates for that method. - -- Adds support for N-qubit Pauli gate ( - :class:`qiskit.circuit.library.generalized_gates.PauliGate`) to all - simulation methods of the - :class:`~qiskit.providers.aer.AerSimulator` and - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Adds the ability to set a custom executor and configure job splitting for - executing multiple circuits in parallel on a HPC clustor. A custom - executor can be set using the ``executor`` option, and job splitting is - configured by using the ``max_job_size`` option. - - For example configuring a backend and executing using - - .. code-block:: python - - backend = AerSimulator(max_job_size=1, executor=custom_executor) - job = backend.run(circuits) - - will split the exection into multiple jobs each containing a single - circuit. If job splitting is enabled the ``run`` method will return a - :class:`~qiskit.providers.aer.jobs.AerJobSet` object containing all the - individual :class:`~qiskit.providers.aer.jobs.AerJob` classes. After all - individual jobs finish running the job results are automatically combined - into a single Result object that is returned by ``job.result()``. - - Supported executors include those in the Python ``concurrent.futures`` - `module `__ - (eg. ``ThreadPoolExecutor``, ``ProcessPoolExecutor``), and - `Dask `__ distributed Client executors if the optional - dask library is installed. Using a Dask executor allows configuring parallel - execution of multiple circuits on HPC clusters. - -- Adds ability to record logging data for the ``matrix_product_state`` - simulation method to the experiment result metadata by setting the - backend option ``mps_log_data=True``. The saved data includes the - bond dimensions and the discarded value (the sum of the squares of - the Schmidt coeffients that were discarded by approximation) after - every relevant circuit instruction. - -- The :meth:`~qiskit.providers.aer.AerSimulator.run` method for the - :class:`~qiskit.providers.aer.AerSimulator`, - :class:`~qiskit.providers.aer.QasmSimulator`, - :class:`~qiskit.providers.aer.StatevectorSimulator`, and - :class:`~qiskit.providers.aer.UnitarySimulator` has a new kwarg, - ``parameter_binds`` which is used to provide a list of values to use for - any unbound parameters in the inbound circuit. For example:: - - from qiskit.circuit import QuantumCircuit, Parameter - from qiskit.providers.aer import AerSimulator - - shots = 1000 - backend = AerSimulator() - circuit = QuantumCircuit(2) - theta = Parameter('theta') - circuit.rx(theta, 0) - circuit.cx(0, 1) - circuit.measure_all() - parameter_binds = [{theta: [0, 3.14, 6.28]}] - backend.run(circuit, shots=shots, parameter_binds=parameter_binds).result() - - will run the input circuit 3 times with the values 0, 3.14, and 6.28 for - theta. When running with multiple parameters the length of the value lists - must all be the same. When running with multiple circuits, the length - of ``parameter_binds`` must match the number of input circuits (you can use - an empty dict, ``{}``, if there are no binds for a circuit). - -- The :class:`~qiskit.providers.aer.backends.PulseSimulator` can now take - :class:`~qiskit.circuit.QuantumCircuit` objects on the - :meth:`~qiskit.providers.aer.backends.PulseSimulator.run`. Previously, - it only would except :class:`~qiskit.pulse.Schedule` objects as input to - :meth:`~qiskit.providers.aer.backends.PulseSimulator.run`. When a circuit - or list of circuits is passed to the simulator it will call - :func:`~qiskit.compiler.schedule` to convert the circuits to a schedule - before executing the circuit. For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.compiler import transpile - from qiskit.test.mock import FakeVigo - from qiskit.providers.aer.backends import PulseSimulator - - backend = PulseSimulator.from_backend(FakeVigo()) - - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.cx(0, 1) - circuit.measure_all() - - transpiled_circuit = transpile(circuit, backend) - backend.run(circuit) - - -.. _Release Notes_Aer_0.9.0_Known Issues: - -Known Issues ------------- - -- The :class:`~qiskit.providers.aer.library.SaveExpectationValue` and - :class:`~qiskit.providers.aer.library.SaveExpectationValueVariance` have - been disabled for the `extended_stabilizer` method of the - :class:`~qiskit.providers.aer.QasmSimulator` and - :class:`~qiskit.providers.aer.AerSimulator` due to returning the - incorrect value for certain Pauli operator components. Refer to - `#1227 ` for more - information and examples. - - -.. _Release Notes_Aer_0.9.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The default basis for the :class:`~qiskit.providers.aer.noise.NoiseModel` - class has been changed from ``["id", "u3", "cx"]`` to - ``["id", "rz", "sx", "cx"]`` due to the deprecation of the ``u3`` circuit - method in qiskit-terra and change of qiskit-ibmq-provider backend basis - gates. To use the old basis gates you can initialize a noise model with - custom basis gates as ``NoiseModel(basis_gates=["id", "u3", "cx"])``. - -- Removed the ``backend_options`` kwarg from the ``run`` methnod of Aer backends - that was deprecated in qiskit-aer 0.7. All run options must now be passed as - separate kwargs. - -- Removed passing ``system_model`` as a positional arg for the ``run`` method of the - :class:`~qiskit.providers.aer.PulseSimulator`. - - -.. _Release Notes_Aer_0.9.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- Passing an assembled qobj directly to the - :meth:`~qiskit.providers.aer.AerSimulator.run` method of the Aer simulator - backends has been deprecated in favor of passing transpiled circuits - directly as ``backend.run(circuits, **run_options)``. - -- All snapshot instructions in :mod:`qiskit.providers.aer.extensions` have - been deprecated. For replacement use the save instructions from the - :mod:`qiskit.providers.aer.library` module. - -- Adding non-local quantum errors to a - :class:`~qiskit.providers.aer.noise.NoiseModel` has been deprecated due to - inconsistencies in how this noise is applied to the optimized circuit. - Non-local noise should be manually added to a scheduled circuit in Qiskit - using a custom transpiler pass before being run on the simulator. - -- Use of the ``method`` option of the - :class:`~qiskit.providers.aer.StatevectorSimulator`, and - :class:`~qiskit.providers.aer.UnitarySimulator` to run a GPU simulation - has been deprecated. To run a GPU simulation on a compatible system - use the option ``device='GPU'`` instead. - - -.. _Release Notes_Aer_0.9.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixes performance issue with how the ``basis_gates`` configuration - attribute was set. Previously there were unintended side-effects to the - backend class which could cause repeated simulation runtime to - incrementally increase. Refer to - `#1229 ` for more - information and examples. - -- Fixed bug in MPS::apply_kraus. After applying the kraus matrix to the relevant - qubits, we should propagate the changes to the neighboring qubits. - -- Fixes a bug where qiskit-terra assumes that qubits in a multiplexer gate - are first the targets and then the controls of the gate while qiskit-aer - assumes the opposite order. - -- Fixes a bug introduced in 0.8.0 where GPU simulations would allocate - unneeded host memory in addition to the GPU memory. - -- Fixes bug where the initialize instruction would disable measurement - sampling optimization for the statevector and matrix product state - simulation methods even when it was the first circuit instruction or - applied to all qubits and hence deterministic. - -- Fix issue #1196 by using the inner products with the computational basis - states to calculate the norm rather than the norm estimation algorithm. - -- Fixes a bug in the ``stabilizer`` simulator method of the - :class:`~qiskit.providers.aer.QasmSimulator` and - :class:`~qiskit.providers.aer.AerSimulator` where the expectation value - for the ``save_expectation_value`` and ``snapshot_expectation_value`` - could have the wrong sign for certain ``Y`` Pauli's. - -- Fixes bug where the if the required memory is smaller than the system memory the - multi-chunk simulation method was enabled and simulation was still started. - This case will now throw an insufficient memory exception. - -- Fixes issue where setting the ``shots`` option for a backend with - ``set_options(shots=k)`` was always running the default number of shots (1024) - rather than the specified value. - -- Fixes a bug in how the :class:`~qiskit.providers.aer.AerSimulator` handled the - option value for ``max_parallel_experiments=1``. Previously this was treated - the same as ``max_parallel_experiments=0``. - -- Fixes bug in the ``extended_stabilizer`` simulation method where it - incorrectly treated qelay gate and multi-qubit Pauli instructions as - unsupported. - -- Fixes typo in the :class:`~qiskit.providers.aer.AerSimulator` and - :class:`~qiskit.providers.aer.QasmSimulator` options for the - ``extended_stabilizer_norm_estimation_repetitions`` option. - -- Fixes bug with applying the ``unitary`` gate in using the ``matrix_product_state`` - simulation method which did not correctly support permutations in the ordering of - the qubits on which the gate is applied. - -- Fixes an issue where gate fusion could still be enabled for the - ``matrix_product_state`` simulation method even though it is not supported. - Now fusion is always disabled for this method. - -- Fixed bug in the ``matrix_product_state`` simulation method in computing the - normalization following truncation of the Schmidt coefficients after - performing the SVD. - - -.. _Release Notes_Aer_0.9.0_Other Notes: - -Other Notes ------------ - -- Improves the performance of the measurement sampling algorithm for the - ``matrix_product_state`` simulation method. - The new default behaviour is to always sample using the - improved ``mps_apply_measure`` method. The ``mps_probabilities`` sampling - method be still used by setting the custom option value - ``mps_sample_measure_algorithm="mps_probabilities"``. - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.5 -========== - -No change - -IBM Q Provider 0.16.0 -===================== - -No change - -############# -Qiskit 0.29.1 -############# - -.. _Release Notes_0.18.2: - -Terra 0.18.2 -============ - -.. _Release Notes_0.18.2_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue with the :func:`~qiskit.compiler.assemble` function when - called with the ``backend`` kwarg set and the ``parametric_pulses`` kwarg - was set to an empty list the output qobj would contain the - ``parametric_pulses`` setting from the given backend's - :class:`~qiskit.providers.models.BackendConfiguration` instead of the - expected empty list. - Fixed `#6898 `__ - -- The Matplotlib circuit drawer will no longer duplicate drawings when using - ``ipykernel>=6.0.0``. - Fixes `#6889 `__. - -Aer 0.8.2 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -.. _Release Notes_Aqua_0.9.5: - -Aqua 0.9.5 -========== - -.. _Release Notes_Aqua_0.9.5_Bug Fixes: - -Bug Fixes ---------- - -- Fixed a handling error in the Yahoo provider when only one ticker is entered. - Added exception error if no ticker is entered. - Limit yfinance to >=0.1.62 as previous versions have a JSON decoder error. - -IBM Q Provider 0.16.0 -===================== - -No change - - -############# -Qiskit 0.29.0 -############# - -.. _Release Notes_0.18.1: - -Terra 0.18.1 -============ - -.. _Release Notes_0.18.1_Prelude: - -Prelude -------- - -This bugfix release fixes a few minor issues and regressions in the 0.18.0 -release. There is also a minor change to how ``pip`` handles the ``[all]`` -extra when installing ``qiskit-terra`` directly, compared to 0.18.0. - -.. _Release Notes_0.18.1_Upgrade Notes: - -Upgrade Notes -------------- - -- ``pip install qiskit-terra[all]`` will no longer attempt to install the - ``bip-mapper`` extra. This is because the dependency ``cplex`` is not well - supported on the range of Python versions and OSes that Terra supports, and - a failed extra dependency would fail the entire package resolution. If you - are using Python 3.7 or 3.8 and are on Linux-x64 or -ppc64le, macOS-x64 or - Windows-x64 you should be able to install ``qiskit-terra[bip-mapper]`` - explicitly, if desired, while other combinations of OS, platform - architectures and Python versions will likely fail. - -.. _Release Notes_0.18.1_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue where the :class:`~qiskit.utils.QuantumInstance` class would potentially - try to use the :class:`~qiskit.ignis.mitigation.CompleteMeasFitter` class - before it was imported resulting in an error. - Fixed `#6774 `__ - -- Fixed the missing Linux aarch64 wheels which were not published for the - 0.18.0 release. They should now continue to be built as expected for all - future releases. - -- Fixed an issue with the mock backends located in ``qiskit.test.mock`` where - in some situations (mainly fake backends with stored - :class:`~qiskit.providers.models.BackendProperties` running a - :class:`~qiskit.circuit.QuantumCircuit` with ``qiskit-aer`` installed) - passing run time options to the ``run()`` method of a fake backend object - would not actually be passed to the simulator underlying the ``run()`` - method and not have any effect. - Fixed `#6741 `__ - -- Fix a bug in :class:`~qiskit.circuit.library.EvolvedOperatorAnsatz` when the - global phase is 0 (such as for :class:`~qiskit.circuit.library.QAOAAnsatz`) but - was still a :class:`~qiskit.circuit.ParameterExpression`. - -- Fixed an issue with the :attr:`~qiskit.algorithms.optimizers.QNSPSA.settings` - attribute of :obj:`~qiskit.algorithms.optimizers.QNSPSA`, which was missing - the ``fidelity`` argument from the output. This is now correctly included - in the attribute's output. - -- Fixed an issue with the :meth:`~qiskit.transpiler.CouplingMap.subgraph` - method of the :class:`~qiskit.transpiler.CouplingMap` class where it would - incorrectly add nodes to the output :class:`~qiskit.transpiler.CouplingMap` - object when the ``nodelist`` argument contained a non-contiguous list - of qubit indices. This has been fixed so regardless of the input - indices in ``nodelist`` the output - :class:`~qiskit.transpiler.CouplingMap` will only contained the specified - nodes reindexed starting at 0. - Fixes `#6736 `__ - -- Previously, :obj:`~qiskit.transpiler.passes.Optimize1qGatesDecomposition` - failed to properly optimize one qubit gates that are sufficiently close to - the identity matrix. This was fixed so that any gates that differ from the - identity by less than 1e-15 are removed. - -- Fixed the generation and loading of QPY files with - :func:`qiskit.circuit.qpy_serialization.dump` and - :func:`qiskit.circuit.qpy_serialization.load` for - :class:`~qiskit.circuit.QuantumCircuit` objects that contain instructions - with classical conditions on a single :class:`~qiskit.circuit.Clbit` instead - of a :class:`~qiskit.circuit.ClassicalRegister`. While the use of single - :class:`~qiskit.circuit.Clbit` conditions is not yet fully supported, if you - were using them in a circuit they are now correctly serialized by QPY. - -Aer 0.8.2 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.4 -========== - -No change - -.. _Release Notes_IBMQ_0.16.0: - -IBM Q Provider 0.16.0 -===================== - -.. _Release Notes_IBMQ_0.16.0_New Features: - -New Features ------------- -- A user can now set and retrieve preferences for - :class:`qiskit.providers.ibmq.experiment.IBMExperimentService`. - Preferences are saved on disk in the ``$HOME/.qiskit/qiskitrc`` file. - Currently the only preference option is ``auto_save``, which tells - applications that use this service, such as `qiskit-experiments`, - whether you want changes to be automatically saved. - Usage examples:: - - provider.experiment.save_preferences(auto_save=True) # set and save preferences - provider.experiment.preferences # return all saved preferences - -- The methods - :meth:`qiskit.providers.ibmq.experiment.IBMExperimentService.create_figure` - and - :meth:`qiskit.providers.ibmq.experiment.IBMExperimentService.update_figure` - now accept the ``sync_upload`` keyword. This controls whether or not the figure - will be uploaded asynchronously or synchronously to backend storage. By default - ``sync_upload`` is ``True`` for synchronous upload. - -.. _Release Notes_IBMQ_0.16.0_Upgrade Notes: - -Upgrade Notes -------------- -- :class:`~qiskit.providers.ibmq.experiment.IBMExperimentService` is - updated to work with the new ``qiskit-experiments``. As a result, - the syntax of the experiment service is drastically changed. This change, - however, takes the experiment service out of beta mode, and future changes - will provide backward compatibility according to Qiskit deprecation policy. -- :class:`qiskit.providers.ibmq.runtime.utils.RuntimeEncoder` now convert a - callable object to ``None``, since callables are not JSON serializable. -- :meth:`qiskit.providers.ibmq.IBMQBackend.run` no longer - accepts `validate_qobj` as a parameter. - If you were relying on this schema validation you should pull the schemas - from the `Qiskit/ibm-quantum-schemas `_ - and directly validate your payloads with that. - -############# -Qiskit 0.28.0 -############# - -.. _Release Notes_0.18.0: - -Terra 0.18.0 -============ - -.. _Release Notes_0.18.0_Prelude: - -Prelude -------- - -This release includes many new features and bug fixes. The highlights of -this release are the introduction of two new transpiler -passes, :class:`~qiskit.transpiler.passes.BIPMapping` and -:class:`~qiskit.transpiler.passes.DynamicalDecoupling`, which when combined -with the new ``pulse_optimize`` kwarg on the -:class:`~qiskit.transpiler.passes.UnitarySynthesis` pass enables recreating -the Quantum Volume 64 results using the techniques -described in: https://arxiv.org/abs/2008.08571. These new transpiler passes -and options and are also generally applicable to optimizing any circuit. - - -.. _Release Notes_0.18.0_New Features: - -New Features ------------- - -- The ``measurement_error_mitgation`` kwarg for the - :class:`~qiskit.utils.QuantumInstance` constructor can now be set to the - :class:`~qiskit.ignis.mitigation.TensoredMeasFitter` class from - qiskit-ignis in addition to - :class:`~qiskit.ignis.mitigation.CompleteMeasFitter` that was already - supported. If you use :class:`~qiskit.ignis.mitigation.TensoredMeasFitter` - you will also be able to set the new ``mit_pattern`` kwarg to specify the - qubits on which to use :class:`~qiskit.ignis.mitigation.TensoredMeasFitter` - You can refer to the documentation for ``mit_pattern`` in the - :class:`~qiskit.ignis.mitigation.TensoredMeasFitter` documentation for - the expected format. - -- The decomposition methods for single-qubit gates, specified via the - ``basis`` kwarg, in - :class:`~qiskit.quantum_info.OneQubitEulerDecomposer` has been expanded to - now also include the ``'ZSXX'`` basis, for making use of direct - :math:`X` gate as well as :math:`\sqrt{X}` gate. - -- Added two new passes :class:`~qiskit.transpiler.passes.AlignMeasures` and - :class:`~qiskit.transpiler.passes.ValidatePulseGates` to the - :mod:`qiskit.transpiler.passes` module. These passes are a hardware-aware - optimization, and a validation routine that are used to manage alignment - restrictions on time allocation of instructions for a backend. - - If a backend has a restriction on the alignment of - :class:`~qiskit.circuit.Measure` instructions (in terms of quantization in time), the - :class:`~qiskit.transpiler.passes.AlignMeasures` pass is used to adjust - delays in a scheduled circuit to ensure that any - :class:`~qiskit.circuit.Measure` instructions in the circuit - are aligned given the constraints of the backend. The - :class:`~qiskit.transpiler.passes.ValidatePulseGates` pass is used to - check if any custom pulse gates (gates that have a custom pulse definition - in the :attr:`~qiskit.circuit.QuantumCircuit.calibrations` attribute of - a :class:`~qiskit.circuit.QuantumCircuit` object) are valid given - an alignment constraint for the target backend. - - In the built-in :mod:`~qiskit.transpiler.preset_passmangers` used by the - :func:`~qiskit.compiler.transpile` function, these passes get automatically - triggered if the alignment constraint, either via the dedicated - ``timing_constraints`` kwarg on :func:`~qiskit.compiler.transpile` or has an - ``timing_constraints`` attribute in the - :class:`~qiskit.providers.models.BackendConfiguration` object of the - backend being targetted. - - The backends from IBM Quantum Services (accessible via the - `qiskit-ibmq-provider `__ - package) will provide the alignment information in the near future. - - For example: - - .. code-block:: python - - from qiskit import circuit, transpile - from qiskit.test.mock import FakeArmonk - - backend = FakeArmonk() - - qc = circuit.QuantumCircuit(1, 1) - qc.x(0) - qc.delay(110, 0, unit="dt") - qc.measure(0, 0) - qc.draw('mpl') - - .. code-block:: python - - qct = transpile(qc, backend, scheduling_method='alap', - timing_constraints={'acquire_alignment': 16}) - qct.draw('mpl') - -- A new transpiler pass class :class:`qiskit.transpiler.passes.BIPMapping` - that tries to find the best layout and routing at once by solving a BIP - (binary integer programming) problem as described in - `arXiv:2106.06446 `__ has been added. - - The ``BIPMapping`` pass (named "mapping" to refer to "layout and routing") - represents the mapping problem as a BIP (binary integer programming) - problem and relies on CPLEX (``cplex``) to solve the BIP problem. - The dependent libraries including CPLEX can be installed along with qiskit-terra: - - .. code-block:: - - pip install qiskit-terra[bip-mapper] - - Since the free version of CPLEX can solve only small BIP problems, i.e. mapping - of circuits with less than about 5 qubits, the paid version of CPLEX may be - needed to map larger circuits. - - The BIP mapper scales badly with respect to the number of qubits or gates. - For example, it would not work with ``coupling_map`` beyond 10 qubits because - the BIP solver (CPLEX) could not find any solution within the default time limit. - - Note that, if you want to fix physical qubits to be used in the mapping - (e.g. running Quantum Volume (QV) circuits), you need to specify ``coupling_map`` - which contains only the qubits to be used. - - Here is a minimal example code to build pass manager to transpile a QV circuit: - - .. code-block:: python - - num_qubits = 4 # QV16 - circ = QuantumVolume(num_qubits=num_qubits) - - backend = ... - basis_gates = backend.configuration().basis_gates - coupling_map = CouplingMap.from_line(num_qubits) # supply your own coupling map - - def _not_mapped(property_set): - return not property_set["is_swap_mapped"] - - def _opt_control(property_set): - return not property_set["depth_fixed_point"] - - from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - pm = PassManager() - # preparation - pm.append([ - Unroll3qOrMore(), - TrivialLayout(coupling_map), - FullAncillaAllocation(coupling_map), - EnlargeWithAncilla(), - BarrierBeforeFinalMeasurements() - ]) - # mapping - pm.append(BIPMapping(coupling_map)) - pm.append(CheckMap(coupling_map)) - pm.append(Error(msg="BIP mapper failed to map", action="raise"), - condition=_not_mapped) - # post optimization - pm.append([ - Depth(), - FixedPoint("depth"), - Collect2qBlocks(), - ConsolidateBlocks(basis_gates=basis_gates), - UnitarySynthesis(basis_gates), - Optimize1qGatesDecomposition(basis_gates), - CommutativeCancellation(), - UnrollCustomDefinitions(sel, basis_gates), - BasisTranslator(sel, basis_gates) - ], do_while=_opt_control) - - transpile_circ = pm.run(circ) - -- A new constructor method - :meth:`~qiskit.pulse.Schedule.initialize_from` was added to the - :class:`~qiskit.pulse.Schedule` and :class:`~qiskit.pulse.ScheduleBlock` - classes. This method initializes a new empty schedule which - takes the attributes from other schedule. For example: - - .. code-block:: python - - sched = Schedule(name='my_sched') - new_sched = Schedule.initialize_from(sched) - - assert sched.name == new_sched.name - -- A new kwarg, ``line_discipline``, has been added to the :func:`~qiskit.tools.job_monitor` - function. This kwarg enables changing the carriage return characters used in the - ``job_monitor`` output. The ``line_discipline`` kwarg defaults to ``'\r'``, which is what was - in use before. - -- The abstract ``Pulse`` class (which is the parent class for classes such - as :class:`~qiskit.pulse.library.Waveform`, - :class:`~qiskit.pulse.library.Constant`, and - :class:`~qiskit.pulse.library.Gaussian` now has a new kwarg on the - constructor, ``limit_amplitude``, which can be set to ``False`` to disable - the previously hard coded amplitude limit of ``1``. This can also be set as - a class attribute directly to change the global default for a Pulse class. - For example:: - - from qiskit.pulse.library import Waveform - - # Change the default value of limit_amplitude to False - Waveform.limit_amplitude = False - wave = Waveform(2.0 * np.exp(1j * 2 * np.pi * np.linspace(0, 1, 1000))) - - -- A new class, :class:`~qiskit.quantum_info.PauliList`, has been added to - the :mod:`qiskit.quantum_info` module. This class is used to - efficiently represent a list of :class:`~qiskit.quantum_info.Pauli` - operators. This new class inherets from the same parent class as the - existing :class:`~qiskit.quantum_info.PauliTable` (and therefore can be - mostly used interchangeably), however it differs from the - :class:`~qiskit.quantum_info.PauliTable` - because the :class:`qiskit.quantum_info.PauliList` class - can handle Z4 phases. - -- Added a new transpiler pass, :class:`~qiskit.transpiler.passes.RemoveBarriers`, - to :mod:`qiskit.transpiler.passes`. This pass is used to remove all barriers in a - circuit. - -- Add a new optimizer class, - :class:`~qiskit.algorithms.optimizers.SciPyOptimizer`, to the - :mod:`qiskit.algorithms.optimizers` module. This class is a simple wrapper class - of the ``scipy.optimize.minimize`` function - (`documentation `__) - which enables the use of all optimization solvers and all - parameters (e.g. callback) which are supported by ``scipy.optimize.minimize``. - For example: - - .. code-block:: python - - from qiskit.algorithms.optimizers import SciPyOptimizer - - values = [] - - def callback(x): - values.append(x) - - optimizer = SciPyOptimizer("BFGS", options={"maxiter": 1000}, callback=callback) - -- The :class:`~qiskit.transpiler.passes.HoareOptimizer` pass has been - improved so that it can now replace a - :class:`~qiskit.circuit.ControlledGate` in a circuit with - with the base gate if all the control qubits are in the - :math:`|1\rangle` state. - -- Added two new methods, :meth:`~qiskit.dagcircuit.DAGCircuit.is_successor` and - :meth:`~qiskit.dagcircuit.DAGCircuit.is_predecessor`, to the - :class:`~qiskit.dagcircuit.DAGCircuit` class. These functions are used to check if a node - is either a successor or predecessor of another node on the - :class:`~qiskit.dagcircuit.DAGCircuit`. - -- A new transpiler pass, - :class:`~qiskit.transpiler.passes.RZXCalibrationBuilderNoEcho`, was added - to the :mod:`qiskit.transpiler.passes` module. This pass is similar - to the existing :class:`~qiskit.transpiler.passes.RZXCalibrationBuilder` - in that it creates calibrations for an ``RZXGate(theta)``, - however :class:`~qiskit.transpiler.passes.RZXCalibrationBuilderNoEcho` - does this without inserting the echo pulses in the pulse schedule. This - enables exposing the echo in the cross-resonance sequence as gates so that - the transpiler can simplify them. The - :class:`~qiskit.transpiler.passes.RZXCalibrationBuilderNoEcho` pass only - supports the hardware-native direction of the - :class:`~qiskit.circuit.library.CXGate`. - -- A new kwarg, ``wrap``, has been added to the - :meth:`~qiskit.circuit.QuantumCircuit.compose` method of - :class:`~qiskit.circuit.QuantumCircuit`. This enables choosing whether - composed circuits should be wrapped into an instruction or not. By - default this is ``False``, i.e. no wrapping. For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - circuit = QuantumCircuit(2) - circuit.h([0, 1]) - other = QuantumCircuit(2) - other.x([0, 1]) - print(circuit.compose(other, wrap=True)) # wrapped - print(circuit.compose(other, wrap=False)) # not wrapped - -- A new attribute, - :attr:`~qiskit.providers.models.PulseBackendConfiguration.control_channels`, - has been added to the - :class:`~qiskit.providers.models.PulseBackendConfiguration` class. This - attribute represents the control channels on a backend as a mapping of - qubits to a list of :class:`~qiskit.pulse.channels.ControlChannel` objects. - -- A new kwarg, ``epsilon``, has been added to the constructor for the - :class:`~qiskit.extensions.Isometry` class and the corresponding - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.isometry`. This kwarg enables - optionally setting the epsilon tolerance used by an - :class:`~qiskit.extensions.Isometry` gate. For example:: - - import numpy as np - from qiskit import QuantumRegister, QuantumCircuit - - tolerance = 1e-8 - iso = np.eye(2,2) - num_q_output = int(np.log2(iso.shape[0])) - num_q_input = int(np.log2(iso.shape[1])) - q = QuantumRegister(num_q_output) - qc = QuantumCircuit(q) - - qc.isometry(iso, q[:num_q_input], q[num_q_input:], epsilon=tolerance) - -- Added a transpiler pass, - :class:`~qiskit.transpiler.passes.DynamicalDecoupling`, to - :mod:`qiskit.transpiler.passes` for inserting dynamical decoupling sequences - in idle periods of a circuit (after mapping to physical qubits and - scheduling). The pass allows control over the sequence of DD gates, the - spacing between them, and the qubits to apply on. For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import XGate - from qiskit.transpiler import PassManager, InstructionDurations - from qiskit.transpiler.passes import ALAPSchedule, DynamicalDecoupling - from qiskit.visualization import timeline_drawer - - circ = QuantumCircuit(4) - circ.h(0) - circ.cx(0, 1) - circ.cx(1, 2) - circ.cx(2, 3) - circ.measure_all() - - durations = InstructionDurations( - [("h", 0, 50), ("cx", [0, 1], 700), ("reset", None, 10), - ("cx", [1, 2], 200), ("cx", [2, 3], 300), - ("x", None, 50), ("measure", None, 1000)] - ) - - dd_sequence = [XGate(), XGate()] - - pm = PassManager([ALAPSchedule(durations), - DynamicalDecoupling(durations, dd_sequence)]) - circ_dd = pm.run(circ) - timeline_drawer(circ_dd) - -- The :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.qasm` has a new kwarg, ``encoding``, - which can be used to optionally set the character encoding of an output QASM - file generated by the function. This can be set to any valid codec or alias - string from the Python standard library's - `codec module `__. - -- Added a new class, :class:`~qiskit.circuit.library.EvolvedOperatorAnsatz`, - to the :mod:`qiskit.circuit.library` module. This library circuit, which - had previously been located in - `Qiskit Nature `__ , can be used - to construct ansatz circuits that consist of time-evolved operators, where - the evolution time is a variational parameter. Examples of such ansatz - circuits include ``UCCSD`` class in the ``chemistry`` module of - Qiskit Nature or the :class:`~qiskit.circuit.library.QAOAAnsatz` class. - -- A new fake backend class is available under ``qiskit.test.mock`` for the - ``ibmq_guadalupe`` backend. As with the other fake backends, this includes - a snapshot of calibration data (i.e. ``backend.defaults()``) and error data - (i.e. ``backend.properties()``) taken from the real system, and can be used - for local testing, compilation and simulation. - -- A new method :meth:`~qiskit.pulse.Schedule.children` for the - :class:`~qiskit.pulse.Schedule` class has been added. This method is used - to return the child schedule components of the - :class:`~qiskit.pulse.Schedule` object as a tuple. It returns nested - schedules without flattening. This method is equivalent to the private - ``_children()`` method but has a public and stable interface. - -- A new optimizer class, - :class:`~qiskit.algorithms.optimizers.GradientDescent`, has been added - to the :mod:`qiskit.algorithms.optimizers` module. This optimizer class - implements a standard gradient descent optimization algorithm for use - with quantum variational algorithms, such as - :class:`~qiskit.algorithms.VQE`. - For a detailed description and examples on how to use this class, please - refer to the :class:`~qiskit.algorithms.optimizers.GradientDescent` class - documentation. - -- A new optimizer class, :class:`~qiskit.algorithms.optimizers.QNSPSA`, - has been added to the :mod:`qiskit.algorithms.optimizers` module. This - class implements the - `Quantum Natural SPSA (QN-SPSA) `__ - algorithm, a generalization of the 2-SPSA algorithm, and estimates the - Quantum Fisher Information Matrix instead of the Hessian to obtain a - stochastic estimate of the Quantum Natural Gradient. For examples on - how to use this new optimizer refer to the - :class:`~qiskit.algorithms.optimizers.QNSPSA` class documentation. - -- A new kwarg, ``second_order``, has been added to the constructor - of the :class:`~qiskit.algorithms.optimizers.SPSA` class in the - :mod:`qiskit.algorithms.optimizers` module. When set to ``True`` this - enables using - `second-order SPSA `__. - Second order SPSA, or 2-SPSA, is an extension of the ordinary SPSA algorithm that - enables estimating the Hessian alongside the gradient, which is used - to precondition the gradient before the parameter update step. As a - second-order method, this tries to improve convergence of SPSA. - For examples on how to use this option refer to the - :class:`~qiskit.algorithms.optimizers.SPSA` class documentation. - -- When using the ``latex`` or ``latex_source`` output mode of - :meth:`~qiskit.visualization.circuit_drawer` or the - :meth:`~qiskit.circuit.QuantumCircuit.draw` of - :class:`~qiskit.circuit.QuantumCircuit` the ``style`` kwarg - can now be used just as with the ``mpl`` output formatting. - However, unlike the ``mpl`` output mode only the ``displaytext`` - field will be used when using the ``latex`` or ``latex_source`` output - modes (because neither supports color). - -- When using the ``mpl`` or ``latex`` output methods for the - :meth:`~qiskit.visualization.circuit_drawer` function or the - :meth:`~qiskit.circuit.QuantumCircuit.draw` of - :class:`~qiskit.circuit.QuantumCircuit`, you can now use math mode - formatting for text and set color formatting (``mpl`` only) - by setting the ``style`` kwarg as a dict - with a user-generated name or label. For example, to add subscripts and to - change a gate color: - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.circuit.library import HGate - qc = QuantumCircuit(3) - qc.append(HGate(label='h1'), [0]) - qc.append(HGate(label='h2'), [1]) - qc.append(HGate(label='h3'), [2]) - qc.draw('mpl', style={'displaytext': {'h1': 'H_1', 'h2': 'H_2', 'h3': 'H_3'}, - 'displaycolor': {'h2': ('#EEDD00', '#FF0000')}}) - -- Added three new classes, - :class:`~qiskit.circuit.library.CDKMRippleCarryAdder`, - :class:`~qiskit.circuit.library.ClassicalAdder` and - :class:`~qiskit.circuit.library.DraperQFTAdder`, to the - :mod:`qiskit.circuit.library` module. These new circuit classes are used to - perform classical addition of two equally-sized qubit registers. For two - registers :math:`|a\rangle_n` and :math:`|b\rangle_n` on :math:`n` - qubits, the three new classes perform the operation: - - .. math:: - - |a\rangle_n |b\rangle_n \mapsto |a\rangle_n |a + b\rangle_{n + 1}. - - For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import CDKMRippleCarryAdder - from qiskit.quantum_info import Statevector - - # a encodes |01> = 1 - a = QuantumCircuit(2) - a.x(0) - - # b encodes |10> = 2 - b = QuantumCircuit(2) - b.x(1) - - # adder on 2-bit numbers - adder = CDKMRippleCarryAdder(2) - - # add the state preparations to the front of the circuit - adder.compose(a, [0, 1], inplace=True, front=True) - adder.compose(b, [2, 3], inplace=True, front=True) - - # simulate and get the state of all qubits - sv = Statevector(adder) - counts = sv.probabilities_dict() - state = list(counts.keys())[0] # we only have a single state - - # skip the input carry (first bit) and the register |a> (last two bits) - result = state[1:-2] - print(result) # '011' = 3 = 1 + 2 - -- Added two new classes, - :class:`~qiskit.circuit.library.RGQFTMultiplier` and - :class:`~qiskit.circuit.library.HRSCumulativeMultiplier`, to the - :mod:`qiskit.circuit.library` module. These classes are used to perform - classical multiplication of two equally-sized qubit registers. For two - registers :math:`|a\rangle_n` and :math:`|b\rangle_n` on :math:`n` - qubits, the two new classes perform the operation - - .. math:: - - |a\rangle_n |b\rangle_n |0\rangle_{2n} \mapsto |a\rangle_n |b\rangle_n |a \cdot b\rangle_{2n}. - - For example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit.library import RGQFTMultiplier - from qiskit.quantum_info import Statevector - - num_state_qubits = 2 - - # a encodes |11> = 3 - a = QuantumCircuit(num_state_qubits) - a.x(range(num_state_qubits)) - - # b encodes |11> = 3 - b = QuantumCircuit(num_state_qubits) - b.x(range(num_state_qubits)) - - # multiplier on 2-bit numbers - multiplier = RGQFTMultiplier(num_state_qubits) - - # add the state preparations to the front of the circuit - multiplier.compose(a, [0, 1], inplace=True, front=True) - multiplier.compose(b, [2, 3], inplace=True, front=True) - - # simulate and get the state of all qubits - sv = Statevector(multiplier) - counts = sv.probabilities_dict(decimals=10) - state = list(counts.keys())[0] # we only have a single state - - # skip both input registers - result = state[:-2*num_state_qubits] - print(result) # '1001' = 9 = 3 * 3 - -- The :class:`~qiskit.circuit.Delay` class now can accept a - :class:`~qiskit.circuit.ParameterExpression` or - :class:`~qiskit.circuit.Parameter` value for the ``duration`` kwarg on its - constructor and for its :attr:`~qiskit.circuit.Delay.duration` attribute. - - For example:: - - idle_dur = Parameter('t') - qc = QuantumCircuit(1, 1) - qc.x(0) - qc.delay(idle_dur, 0, 'us') - qc.measure(0, 0) - print(qc) # parameterized delay in us (micro seconds) - - # assign before transpilation - assigned = qc.assign_parameters({idle_dur: 0.1}) - print(assigned) # delay in us - transpiled = transpile(assigned, some_backend_with_dt) - print(transpiled) # delay in dt - - # assign after transpilation - transpiled = transpile(qc, some_backend_with_dt) - print(transpiled) # parameterized delay in dt - assigned = transpiled.assign_parameters({idle_dur: 0.1}) - print(assigned) # delay in dt - -- A new binary serialization format, `QPY`, has been introduced. It is - designed to be a fast binary serialization format that is backwards - compatible (QPY files generated with older versions of Qiskit can be - loaded by newer versions of Qiskit) that is native to Qiskit. The QPY - serialization tooling is available via the - :mod:`qiskit.circuit.qpy_serialization` module. For example, to generate a - QPY file:: - - from datetime import datetime - - from qiskit.circuit import QuantumCircuit - from qiskit.circuit import qpy_serialization - - qc = QuantumCircuit( - 2, metadata={'created_at': datetime.utcnow().isoformat()} - ) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - - circuits = [qc] * 5 - - with open('five_bells.qpy', 'wb') as qpy_file: - qpy_serialization.dump(circuits, qpy_file) - - Then the five circuits saved in the QPY file can be loaded with:: - - from qiskit.circuit.qpy_serialization - - with open('five_bells.qpy', 'rb') as qpy_file: - circuits = qpy_serialization.load(qpy_file) - - The QPY file format specification is available in the module documentation. - -- The :class:`~qiskit.quantum_info.TwoQubitBasisDecomposer` class has been - updated to perform pulse optimal decompositions for a basis with CX, √X, and - virtual Rz gates as described in - https://arxiv.org/pdf/2008.08571. Pulse optimal here means that - the duration of gates between the CX gates of the decomposition is - reduced in exchange for possibly more local gates before or after - all the CX gates such that, when composed into a circuit, there is the - possibility of single qubit compression with neighboring gates - reducing the overall sequence duration. - - A new keyword argument, ```pulse_optimize``, has been added to the constructor - for :class:`~qiskit.quantum_info.TwoQubitBasisDecomposer` to control this: - - * ``None``: Attempt pulse optimal decomposition. If a pulse optimal - decomposition is unknown for the basis of the decomposer, drop - back to the standard decomposition without warning. This is the default - setting. - * ``True``: Attempt pulse optimal decomposition. If a pulse optimal - decomposition is unknown for the basis of the decomposer, raise - `QiskitError`. - * ``False``: Do not attempt pulse optimal decomposition. - - For example: - - .. code-block:: python - - from qiskit.quantum_info import TwoQubitBasisDecomposer - from qiskit.circuit.library import CXGate - from qiskit.quantum_info import random_unitary - - unitary_matrix = random_unitary(4) - - decomposer = TwoQubitBasisDecomposer(CXGate(), euler_basis="ZSX", pulse_optimize=True) - circuit = decomposer(unitary_matrix) - -- The transpiler pass :class:`~qiskit.transpiler.passes.synthesis.UnitarySynthesis` - located in :mod:`qiskit.transpiler.passes` has been updated to support performing - pulse optimal decomposition. This is done primarily with the the - ``pulse_optimize`` keyword argument which was added to the constructor and - used to control whether pulse optimal synthesis is performed. The behavior of - this kwarg mirrors the ``pulse_optimize`` kwarg in the - :class:`~qiskit.quantum_info.TwoQubitBasisDecomposer` class's constructor. - Additionally, the constructor has another new keyword argument, ``synth_gates``, - which is used to specify the list of gate names over which synthesis should be attempted. If - ``None`` and ``pulse_optimize`` is ``False`` or ``None``, use ``"unitary"``. - If `None` and `pulse_optimize` is ``True``, use ``"unitary"`` and ``"swap"``. - Since the direction of the CX gate in the synthesis is arbitrary, another - keyword argument, ``natural_direction``, is added to consider first - a coupling map and then :class:`~qiskit.circuit.library.CXGate` durations in - choosing for which direction of CX to generate the synthesis. - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - from qiskit.transpiler import PassManager, CouplingMap - from qiskit.transpiler.passes import TrivialLayout, UnitarySynthesis - from qiskit.test.mock import FakeVigo - from qiskit.quantum_info.random import random_unitary - - backend = FakeVigo() - conf = backend.configuration() - coupling_map = CouplingMap(conf.coupling_map) - triv_layout_pass = TrivialLayout(coupling_map) - circ = QuantumCircuit(2) - circ.unitary(random_unitary(4), [0, 1]) - unisynth_pass = UnitarySynthesis( - basis_gates=conf.basis_gates, - coupling_map=None, - backend_props=backend.properties(), - pulse_optimize=True, - natural_direction=True, - synth_gates=['unitary']) - pm = PassManager([triv_layout_pass, unisynth_pass]) - optimal_circ = pm.run(circ) - -- A new basis option, ``'XZX'``, was added for the ``basis`` argument - :class:`~qiskit.quantum_info.OneQubitEulerDecomposer` class. - -- Added a new method, :meth:`~qiskit.circuit.QuantumCircuit.get_instructions`, - was added to the :class:`~qiskit.circuit.QuantumCircuit` class. This method - is used to return all :class:`~qiskit.circuit.Instruction` objects in the - circuit which have a :attr:`~qiskit.circuit.Instruction.name` that matches - the provided ``name`` argument along with its associated ``qargs`` and - ``cargs`` lists of :class:`~qiskit.circuit.Qubit` and - :class:`~qiskit.circuit.Clbit` objects. - -- A new optional extra ``all`` has been added to the qiskit-terra package. - This enables installing all the optional requirements with a single - extra, for example: ``pip install 'qiskit-terra[all]'``, Previously, it - was necessary to list all the extras individually to install all the - optional dependencies simultaneously. - -- Added two new classes :class:`~qiskit.result.ProbDistribution` and - :class:`~qiskit.result.QuasiDistribution` for dealing with probability - distributions and quasiprobability distributions respectively. These objects - both are dictionary subclasses that add additional methods for working - with probability and quasiprobability distributions. - -- Added a new :attr:`~qiskit.algorithms.optimizers.Optimizer.settings` - property to the :class:`~qiskit.algorithms.optimizers.Optimizer` abstract - base class that all the optimizer classes in the - :mod:`qiskit.algorithms.optimizers` module are based on. This property - will return a Python dictionary of the settings for the optimizer - that can be used to instantiate another instance of the same optimizer - class. For example:: - - from qiskit.algorithms.optimizers import GradientDescent - - optimizer = GradientDescent(maxiter=10, learning_rate=0.01) - settings = optimizer.settings - new_optimizer = GradientDescent(**settings) - - The ``settings`` dictionary is also potentially useful for serializing - optimizer objects using JSON or another serialization format. - -- A new function, :func:`~qiskit.user_config.set_config`, has been added - to the :mod:`qiskit.user_config` module. This function enables setting - values in a user config from the Qiskit API. For example:: - - from qiskit.user_config import set_config - set_config("circuit_drawer", "mpl", section="default", file="settings.conf") - - which will result in adding a value of ``circuit_drawer = mpl`` to the - ``default`` section in the ``settings.conf`` file. - - If no ``file_path`` argument is specified, the currently used path to the - user config file (either the value of the ``QISKIT_SETTINGS`` environment - variable if set or the default location ``~/.qiskit/settings.conf``) will be - updated. However, changes to the existing config file will not be reflected in - the current session since the config file is parsed at import time. - -- Added a new state class, :class:`~qiskit.quantum_info.StabilizerState`, - to the :mod:`qiskit.quantum_info` module. This class represents a - stabilizer simulator state using the convention from - `Aaronson and Gottesman (2004) `__. - -- Two new options, ``'value'`` and ``'value_desc'`` were added to the - ``sort`` kwarg of the :func:`qiskit.visualization.plot_histogram` function. - When ``sort`` is set to either of these options the output visualization - will sort the x axis based on the maximum probability for each bitstring. - For example: - - .. code-block:: python - - from qiskit.visualization import plot_histogram - - counts = { - '000': 5, - '001': 25, - '010': 125, - '011': 625, - '100': 3125, - '101': 15625, - '110': 78125, - '111': 390625, - } - plot_histogram(counts, sort='value') - - -.. _Release Notes_0.18.0_Known Issues: - -Known Issues ------------- - -- When running :func:`~qiskit.tools.parallel_map` (and functions that - internally call :func:`~qiskit.tools.parallel_map` such as - :func:`~qiskit.compiler.transpile` and :func:`~qiskit.compiler.assemble`) - on Python 3.9 with ``QISKIT_PARALLEL`` set to True in some scenarios it is - possible for the program to deadlock and never finish running. To avoid - this from happening the default for Python 3.9 was changed to not run in - parallel, but if ``QISKIT_PARALLEL`` is explicitly enabled then this - can still occur. - - -.. _Release Notes_0.18.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The minimum version of the `retworkx `_ dependency - was increased to version `0.9.0`. This was done to use new APIs introduced in that release - which improved the performance of some transpiler passes. - -- The default value for ``QISKIT_PARALLEL`` on Python 3.9 environments has - changed to ``False``, this means that when running on Python 3.9 by default - multiprocessing will not be used. This was done to avoid a potential - deadlock/hanging issue that can occur when running multiprocessing on - Python 3.9 (see the known issues section for more detail). It is still - possible to manual enable it by explicitly setting the ``QISKIT_PARALLEL`` - environment variable to ``TRUE``. - -- The existing fake backend classes in ``qiskit.test.mock`` now strictly - implement the :class:`~qiskit.providers.BackendV1` interface. This means - that if you were manually constructing :class:`~qiskit.qobj.QasmQobj` or - :class:`~qiskit.qobj.PulseQobj` object for use with the ``run()`` method - this will no longer work. The ``run()`` method only accepts - :class:`~qiskit.circuit.QuantumCircuit` or :class:`~qiskit.pulse.Schedule` - objects now. This was necessary to enable testing of new backends - implemented without qobj which previously did not have any testing inside - qiskit terra. If you need to leverage the fake backends with - :class:`~qiskit.qobj.QasmQobj` or :class:`~qiskit.qobj.PulseQobj` new - fake legacy backend objects were added to explicitly test the legacy - providers interface. This will be removed after the legacy interface is - deprecated and removed. Moving forward new fake backends will only - implement the :class:`~qiskit.providers.BackendV1` interface and will not - add new legacy backend classes for new fake backends. - -- When creating a :class:`~qiskit.quantum_info.Pauli` object with an invalid - string label, a :class:`~qiskit.exceptions.QiskitError` is now raised. - This is a change from previous releases which would raise an - ``AttributeError`` on an invalid string label. This change was made to - ensure the error message is more informative and distinct from a generic - ``AttributeError``. - -- The output program representation from the pulse builder - (:func:`qiskit.pulse.builder.build`) has changed from a - :class:`~qiskit.pulse.Schedule` to a - :class:`~qiskit.pulse.ScheduleBlock`. This new representation disables - some timing related operations such as shift and insert. However, this - enables parameterized instruction durations within the builder context. - For example: - - .. code-block:: python - - from qiskit import pulse - from qiskit.circuit import Parameter - - dur = Parameter('duration') - - with pulse.build() as sched: - with pulse.align_sequential(): - pulse.delay(dur, pulse.DriveChannel(1)) - pulse.play(pulse.Gaussian(dur, 0.1, dur/4), pulse.DriveChannel(0)) - - assigned0 = sched.assign_parameters({dur: 100}) - assigned1 = sched.assign_parameters({dur: 200}) - - You can directly pass the duration-assigned schedules to the assembler (or backend), - or you can attach them to your quantum circuit as pulse gates. - -- The `tweedledum `__ library which - was previously an optional dependency has been made a requirement. This - was done because of the wide use of the - :class:`~qiskit.circuit.library.PhaseOracle` (which depends on - having tweedledum installed) with several algorithms - from :mod:`qiskit.algorithms`. - -- The optional extra ``full-featured-simulators`` which could previously used - to install ``qiskit-aer`` with something like - ``pip install qiskit-terra[full-featured-simulators]`` has been removed - from the qiskit-terra package. If this was being used to install - ``qiskit-aer`` with ``qiskit-terra`` instead you should rely on the - `qiskit `__ metapackage or just install - qiskit-terra and qiskit-aer together with - ``pip install qiskit-terra qiskit-aer``. - -- A new requirement `symengine `__ has - been added for Linux (on x86_64, aarch64, and ppc64le) and macOS users - (x86_64 and arm64). It is an optional dependency on Windows (and available - on PyPi as a precompiled package for 64bit Windows) and other - architectures. If it is installed it provides significantly improved - performance for the evaluation of :class:`~qiskit.circuit.Parameter` and - :class:`~qiskit.circuit.ParameterExpression` objects. - -- All library circuit classes, i.e. all :class:`~qiskit.circuit.QuantumCircuit` derived - classes in :mod:`qiskit.circuit.library`, are now wrapped in a - :class:`~qiskit.circuit.Instruction` (or :class:`~qiskit.circuit.Gate`, if they are - unitary). For example, importing and drawing the :class:`~qiskit.circuit.library.QFT` - circuit: - - .. code-block::python - - from qiskit.circuit.library import QFT - - qft = QFT(3) - print(qft.draw()) - - before looked like - - .. code-block:: - - ┌───┐ - q_0: ────────────────────■────────■───────┤ H ├─X─ - ┌───┐ │ │P(π/2) └───┘ │ - q_1: ──────■───────┤ H ├─┼────────■─────────────┼─ - ┌───┐ │P(π/2) └───┘ │P(π/4) │ - q_2: ┤ H ├─■─────────────■──────────────────────X─ - └───┘ - - and now looks like - - .. code-block:: - - ┌──────┐ - q_0: ┤0 ├ - │ │ - q_1: ┤1 QFT ├ - │ │ - q_2: ┤2 ├ - └──────┘ - - To obtain the old circuit, you can call the - :meth:`~qiskit.circuit.QuantumCircuit.decompose` method on the circuit - - .. code-block::python - - from qiskit.circuit.library import QFT - - qft = QFT(3) - print(qft.decompose().draw()) - - This change was primarily made for consistency as before this release some - circuit classes in :mod:`qiskit.circuit.library` were previously wrapped - in an :class:`~qiskit.circuit.Instruction` or :class:`~qiskit.circuit.Gate` - but not all. - - -.. _Release Notes_0.18.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The class :class:`qiskit.exceptions.QiskitIndexError` - is deprecated and will be removed in a future release. This exception - was not actively being used by anything in Qiskit, if you were using - it you can create a custom exception class to replace it. - -- The kwargs ``epsilon`` and ``factr`` for the - :class:`qiskit.algorithms.optimizers.L_BFGS_B` constructor - and ``factr`` kwarg of the :class:`~qiskit.algorithms.optimizers.P_BFGS` - optimizer class are deprecated and will be removed in a future release. Instead, please - use the ``eps`` karg instead of ``epsilon``. The ``factr`` kwarg is - replaced with ``ftol``. The relationship between the two is - :code:`ftol = factr * numpy.finfo(float).eps`. This change was made - to be consistent with the usage of the ``scipy.optimize.minimize`` - functions ``'L-BFGS-B'`` method. See the: - ``scipy.optimize.minimize(method='L-BFGS-B')`` - `documentation `__ - for more information on how these new parameters are used. - -- The legacy providers interface, which consisted of the - :class:`qiskit.providers.BaseBackend`, :class:`qiskit.providers.BaseJob`, - and :class:`qiskit.providers.BaseProvider` abstract classes, has been - deprecated and will be removed in a future release. Instead you should use - the versioned interface, which the current abstract class versions are - :class:`qiskit.providers.BackendV1`, :class:`qiskit.providers.JobV1`, and - :class:`qiskit.providers.ProviderV1`. The V1 objects are mostly backwards - compatible to ease migration from the legacy interface to the versioned - one. However, expect future versions of the abstract interfaces to diverge - more. You can refer to the :mod:`qiskit.providers` documentation for - more high level details about the versioned interface. - -- The ``condition`` kwarg to the - :class:`~qiskit.dagcircuit.DAGDepNode` constructor along with the - corresponding :attr:`~qiskit.dagcircuit.DAGDepNode.condition` attribute - of the :class:`~qiskit.dagcircuit.DAGDepNode` have been deprecated and - will be removed in a future release. Instead, you can access the - ``condition`` of a ``DAGDepNode`` if the node is of type ``op``, by using - ``DAGDepNode.op.condition``. - -- The :attr:`~qiskit.dagcircuit.DAGNode.condition` attribute of the - :class:`~qiskit.dagcircuit.DAGNode` class has been deprecated and - will be removed in a future release. Instead, you can access the - ``condition`` of a ``DAGNode`` object if the node is of type ``op``, by - using ``DAGNode.op.condition``. - -- The pulse builder (:func:`qiskit.pulse.builder.build`) syntax - :func:`qiskit.pulse.builder.inline` is deprecated and will be removed in a - future release. Instead of using this context, you can just remove alignment - contexts within the inline context. - -- The pulse builder (:func:`qiskit.pulse.builder.build`) syntax - :func:`qiskit.pulse.builder.pad` is deprecated and will be removed in a - future release. This was done because the :class:`~qiskit.pulse.ScheduleBlock` - now being returned by the pulse builder doesn't support the ``.insert`` method - (and there is no insert syntax in the builder). The use of timeslot placeholders - to block the insertion of other instructions is no longer necessary. - - -.. _Release Notes_0.18.0_Bug Fixes: - -Bug Fixes ---------- - -- The :class:`~qiskit.quantum_info.OneQubitEulerDecomposer` and - :class:`~qiskit.quantum_info.TwoQubitBasisDecomposer` classes for - one and two qubit gate synthesis have been improved to tighten up - tolerances, improved repeatability and simplification, and fix - several global-phase-tracking bugs. - -- Fixed an issue in the assignment of the :attr:`~qiskit.circuit.Gate.name` - attribute to :class:`~qiskit.circuit.Gate` generated by multiple calls to - the :meth:`~qiskit.circuit.Gate.inverse`` method. Prior to this fix - when the :meth:`~qiskit.circuit.Gate.inverse`` was called it would - unconditionally append ``_dg`` on each call to inverse. This has - been corrected so on a second call of - :meth:`~qiskit.circuit.Gate.inverse`` the ``_dg`` suffix is now removed. - -- Fixes the triviality check conditions of :class:`~qiskit.circuit.library.CZGate`, - :class:`~qiskit.circuit.library.CRZGate`, :class:`~qiskit.circuit.library.CU1Gate` - and :class:`~qiskit.circuit.library.MCU1Gate` in the - :class:`~qiskit.transpiler.passes.HoareOptimizer` pass. Previously, in some cases - the optimizer would remove these gates breaking the semantic equivalence of - the transformation. - -- Fixed an issue when converting a :class:`~qiskit.opflow.list_ops.ListOp` - object of :class:`~qiskit.opflow.primitive_ops.PauliSumOp` objects using - :class:`~qiskit.opflow.expectations.PauliExpectation` or - :class:`~qiskit.opflow.expectations.AerPauliExpectation`. Previously, it would raise - a warning about it converting to a Pauli representation which is - potentially expensive. This has been fixed by instead of internally - converting the :class:`~qiskit.opflow.list_ops.ListOp` to a - :class:`~qiskit.opflow.list_ops.SummedOp` of - :class:`~qiskit.opflow.primitive_ops.PauliOp` objects, it now creates - a :class:`~qiskit.opflow.primitive_ops.PauliSumOp` which is more - efficient. - Fixed `#6159 `__ - -- Fixed an issue with the :class:`~qiskit.circuit.library.NLocal` class - in the :mod:`qiskit.circuit.library` module where it wouldn't properly - raise an exception at object initialization if an invalid type was - used for the ``reps`` kwarg which would result in an unexpected runtime - error later. A ``TypeError`` will now be properly raised if the ``reps`` - kwarg is not an ``int`` value. - Fixed `#6515 `__ - -- Fixed an issue where the :class:`~qiskit.circuit.library.TwoLocal` class - in the :mod:`qiskit.circuit.library` module did not accept numpy integer - types (e.g. ``numpy.int32``, ``numpy.int64``, etc) as a valid input for - the ``entanglement`` kwarg. - Fixed `#6455 `__ - -- When loading an OpenQASM2 file or string with the - :meth:`~qiskit.circuitQuantumCircuit.from_qasm_file` or - :meth:`~qiskit.circuitQuantumCircuit.from_qasm_str` constructors for the - :class:`~qiskit.circuit.QuantumCircuit` class, if the OpenQASM2 circuit - contains an instruction with the name ``delay`` this will be mapped to - a :class:`qiskit.circuit.Delay` instruction. For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - - qasm = """OPENQASM 2.0; - include "qelib1.inc"; - opaque delay(time) q; - qreg q[1]; - delay(172) q[0]; - u3(0.1,0.2,0.3) q[0]; - """ - circuit = QuantumCircuit.from_qasm_str(qasm) - circuit.draw() - - Fixed `#6510 `__ - -- Fixed an issue with addition between - :class:`~qiskit.opflow.primitive_ops.PauliSumOp` objects that had - :class:`~qiskit.circuit.ParameterExpression` coefficients. Previously - this would result in a ``QiskitError`` exception being raised because - the addition of the :class:`~qiskit.circuit.ParameterExpression` was - not handled correctly. This has been fixed so that addition can be - performed between :class:`~qiskit.opflow.primitive_ops.PauliSumOp` - objects with :class:`~qiskit.circuit.ParameterExpression` coefficients. - -- Fixed an issue with the initialization of the - :class:`~qiskit.algorithms.AmplificationProblem` class. The - ``is_good_state`` kwarg was a required field but incorrectly being treated - as optional (and documented as such). This has been fixed and also - updated so unless the input ``oracle`` is a - :class:`~qiskit.circuit.library.PhaseOracle` object (which provides it's - on evaluation method) the field is required and will raise a ``TypeError`` - when constructed without ``is_good_state``. - -- Fixed an issue where adding a control to a - :class:`~qiskit.circuit.ControlledGate` with open controls would unset - the inner open controls. - Fixes `#5857 `__ - -- Fixed an issue with the - :meth:`~qiskit.opflow.expectations.PauliExpectation.convert` method of - the :class:`~qiskit.opflow.expectations.PauliExpectation` class where - calling it on an operator that was non-Hermitian would return - an incorrect result. - Fixed `#6307 `__ - -- Fixed an issue with the :func:`qiskit.pulse.transforms.inline_subroutines` - function which would previously incorrectly not remove all the nested - components when called on nested schedules. - Fixed `#6321 `__ - -- Fixed an issue when passing a partially bound callable created with - the Python standard library's ``functools.partial()`` function as the - ``schedule`` kwarg to the - :meth:`~qiskit.pulse.InstructionScheduleMap.add` method of the - :class:`~qiskit.pulse.InstructionScheduleMap` class, which would previously - result in an error. - Fixed `#6278 `__ - -- Fixed an issue with the :class:`~qiskit.circuit.library.PiecewiseChebyshev` - when setting the - :attr:`~qiskit.circuit.library.PiecewiseChebyshev.breakpoints` to ``None`` - on an existing object was incorrectly being treated as a breakpoint. This - has been corrected so that when it is set to ``None`` this will switch back - to the default behavior of approximating over the full interval. - Fixed `#6198 `__ - -- Fixed an issue with the - :meth:`~qiskit.circuit.QuantumCircuit.num_connected_components` method of - :class:`~qiskit.circuit.QuantumCircuit` which was returning the incorrect - number of components when the circuit contains two or more gates conditioned - on classical registers. - Fixed `#6477 `__ - -- Fixed an issue with the :mod:`qiskit.opflow.expectations` module - where coefficients of a statefunction were not being multiplied correctly. - This also fixed the calculations - of Gradients and QFIs when using the - :class:`~qiskit.opflow.expectations.PauliExpectation` or - :class:`~qiskit.opflow.expectations.AerPauliExpectation` classes. For - example, previously:: - - from qiskit.opflow import StateFn, I, One - - exp = ~StateFn(I) @ (2 * One) - - evaluated to ``2`` - for :class:`~qiskit.opflow.expectations.AerPauliExpectation` and to ``4`` - for other expectation converters. Since ``~StateFn(I) @ (2 * One)`` is a - shorthand notation for ``~(2 * One) @ I @ (2 * One)``, the now correct - coefficient of ``4`` is returned for all expectation converters. - Fixed `#6497 `__ - -- Fixed the bug that caused :meth:`~qiskit.opflow.PauliOp.to_circuit` to fail when - :class:`~qiskit.opflow.PauliOp` had a phase. At the same time, it was made more efficient to - use :class:`~qiskit.circuit.library.generalized_gates.PauliGate`. - -- Fixed an issue where the QASM output generated by the - :meth:`~qiskit.circuit.QuantumCircuit.qasm` method of - :class:`~qiskit.circuit.QuantumCircuit` for composite gates such as - :class:`~qiskit.circuit.library.MCXGate` and its variants ( - :class:`~qiskit.circuit.library.MCXGrayCode`, - :class:`~qiskit.circuit.library.MCXRecursive`, and - :class:`~qiskit.circuit.library.MCXVChain`) would be incorrect. Now if a - :class:`~qiskit.circuit.Gate` in the circuit is not present in - ``qelib1.inc``, its definition is added to the output QASM string. - Fixed `#4943 `__ and - `#3945 `__ - -- Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` - function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of - :class:`~qiskit.circuit.QuantumCircuit`. When using the ``mpl`` or ``latex`` - output modes, with the ``cregbundle`` kwarg set to ``False`` and the - ``reverse_bits`` kwarg set to ``True``, the bits in the classical registers - displayed in the same order as when ``reverse_bits`` was set to ``False``. - -- Fixed an issue when using the :class:`qiskit.extensions.Initialize` - instruction which was not correctly setting the global phase of the - synthesized definition when constructed. - Fixed `#5320 `__ - -- Fixed an issue where the bit-order in - :meth:`qiskit.circuit.library.PhaseOracle.evaluate_bitstring` did not agree - with the order of the measured bitstring. This fix also affects the - execution of the :class:`~qiskit.algorithms.Grover` algorithm class if the - oracle is specified as a :class:`~qiskit.circuit.library.PhaseOracle`, which - now will now correctly identify the correct bitstring. - Fixed `#6314 `__ - -- Fixes a bug in :func:`~qiskit.transpiler.passes.Optimize1qGatesDecomposition` - previously causing certain short sequences of gates to erroneously not be - rewritten. - -- Fixed an issue in the :meth:`qiskit.opflow.gradients.Gradient.gradient_wrapper` - method with the gradient calculation. Previously, if the operator was - not diagonal an incorrect result would be returned in some situations. - This has been fixed by using an expectation converter to ensure the - result is always correct. - -- Fixed an issue with the :func:`~qiskit.visualization.circuit_drawer` - function and :meth:`~qiskit.circuit.QuantumCircuit.draw` method of - :class:`~qiskit.circuit.QuantumCircuit` with all output modes - where it would incorrectly render a custom instruction that includes - classical bits in some circumstances. - Fixed `#3201 `__, - `#3202 `__, and - `#6178 `__ - -- Fixed an issue in :func:`~qiskit.visualization.circuit_drawer` and the - :meth:`~qiskit.circuit.QuantumCircuit.draw` method of the - :class:`~qiskit.circuit.QuantumCircuit` class when using the ``mpl`` - output mode, controlled-Z Gates were incorrectly drawn as asymmetrical. - Fixed `#5981 `__ - -- Fixed an issue with the - :class:`~qiskit.transpiler.passes.OptimizeSwapBeforeMeasure` transpiler pass - where in some situations a :class:`~qiskit.circuit.library.SwapGate` - that that contained a classical condition would be removed. - Fixed `#6192 `__ - -- Fixed an issue with the phase of the - :class:`qiskit.opflow.gradients.QFI` class when the ``qfi_method`` is set - to ``lin_comb_full`` which caused the incorrect observable to be evaluated. - -- Fixed an issue with :class:`~qiskit.algorithms.VQE` algorithm class - when run with the :class:`~qiskit.algorithms.optimizers.L_BFGS_B` - or :class:`~qiskit.algorithms.optimizers.P_BFGS` optimizer classes and - gradients are used, the gradient was incorrectly passed as a numpy array - instead of the expected list of floats resulting in an error. This has - been resolved so you can use gradients with :class:`~qiskit.algorithms.VQE` - and the :class:`~qiskit.algorithms.optimizers.L_BFGS_B` or - :class:`~qiskit.algorithms.optimizers.P_BFGS` optimizers. - - -.. _Release Notes_0.18.0_Other Notes: - -Other Notes ------------ - -- The deprecation of the :meth:`~qiskit.pulse.Instruction.parameters` method - for the :class:`~qiskit.pulse.Instruction` class has been reversed. This - method was originally deprecated in the 0.17.0, but it is still necessary - for several applications, including when running calibration experiments. - This method will continue to be supported and will **not** be removed. - -Aer 0.8.2 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.4 -========== - -No change - -IBM Q Provider 0.15.0 -===================== - -.. _Release Notes_IBMQ_0.15.0_New Features: - -New Features ------------- - -- Add support for new method :meth:`qiskit.providers.ibmq.runtime.RuntimeJob.error_message` - which will return a string representing the reason if the job failed. - -- The `inputs` parameter to - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.run` - method can now be specified as a - :class:`qiskit.providers.ibmq.runtime.ParameterNamespace` instance which - supports auto-complete features. You can use - :meth:`qiskit.providers.ibmq.runtime.RuntimeProgram.parameters` to retrieve - an ``ParameterNamespace`` instance. - - For example:: - - from qiskit import IBMQ - - provider = IBMQ.load_account() - - # Set the "sample-program" program parameters. - params = provider.runtime.program(program_id="sample-program").parameters() - params.iterations = 2 - - # Configure backend options - options = {'backend_name': 'ibmq_qasm_simulator'} - - # Execute the circuit using the "circuit-runner" program. - job = provider.runtime.run(program_id="sample-program", - options=options, - inputs=params) - -- The user can now set the visibility (private/public) of a Qiskit Runtime program using - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.set_program_visibility`. - -- An optional boolean parameter `pending` has been added to - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.jobs` - and it allows filtering jobs by their status. - If `pending` is not specified all jobs are returned. - If `pending` is set to True, 'QUEUED' and 'RUNNING' jobs are returned. - If `pending` is set to False, 'DONE', 'ERROR' and 'CANCELLED' jobs are returned. - -- Add support for the ``use_measure_esp`` flag in the - :meth:`qiskit.providers.ibmq.IBMQBackend.run` method. If ``True``, the backend will use ESP - readout for all measurements which are the terminal instruction on that qubit. If used and - the backend does not support ESP readout, an error is raised. - - -.. _Release Notes_IBMQ_0.15.0_Upgrade Notes: - -Upgrade Notes -------------- - -- :meth:`qiskit.providers.ibmq.runtime.RuntimeProgram.parameters` is now a - method that returns a - :class:`qiskit.providers.ibmq.runtime.ParameterNamespace` instance, which - you can use to fill in runtime program parameter values and pass to - :meth:`qiskit.providers.ibmq.runtime.IBMRuntimeService.run`. - -- The ``open_pulse`` flag in backend configuration no longer indicates - whether a backend supports pulse-level control. As a result, - :meth:`qiskit.providers.ibmq.IBMQBackend.configuration` may return a - :class:`~qiskit.providers.models.PulseBackendConfiguration` instance even - if its ``open_pulse`` flag is ``False``. - -- Job share level is no longer supported due to low adoption and the - corresponding interface will be removed in a future release. - This means you should no longer pass `share_level` when creating a job or use - :meth:`qiskit.providers.ibmq.job.IBMQJob.share_level` method to get a job's share level. - - -.. _Release Notes_IBMQ_0.15.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The ``id`` instruction has been deprecated on IBM hardware - backends. Instead, please use the ``delay`` instruction which - implements variable-length delays, specified in units of - ``dt``. When running a circuit containing an ``id`` instruction, - a warning will be raised on job submission and any ``id`` - instructions in the job will be automatically replaced with their - equivalent ``delay`` instruction. - - -############# -Qiskit 0.27.0 -############# - -Terra 0.17.4 -============ - -No change - -Aer 0.8.2 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.2 -========== - -.. _Release Notes_Aqua_0.9.2_Fixes: - -Bug Fixes ---------- - -- Removed version caps from the requirements list to enable installing with newer - versions of dependencies. - -IBM Q Provider 0.14.0 -===================== - -.. _Release Notes_IBMQ_0.14.0_New Features: - -New Features ------------- - -- You can now use the :meth:`qiskit.providers.ibmq.runtime.RuntimeJob.logs` - method to retrieve job logs. Note that logs are only available after the - job finishes. - -- A new backend configuration attribute ``input_allowed`` now tells you the - types of input supported by the backend. Valid input types are ``job``, which - means circuit jobs, and ``runtime``, which means Qiskit Runtime. - - You can also use ``input_allowed`` in backend filtering. For example:: - - from qiskit import IBMQ - - provider = IBMQ.load_account() - # Get a list of all backends that support runtime. - runtime_backends = provider.backends(input_allowed='runtime') - - -.. _Release Notes_IBMQ_0.14.0_Upgrade Notes: - -Upgrade Notes -------------- - -- ``qiskit-ibmq-provider`` now uses a new package ``websocket-client`` as its - websocket client, and packages ``websockets`` and ``nest-asyncio`` are no - longer required. ``setup.py`` and ``requirements.txt`` have been updated - accordingly. - - -.. _Release Notes_IBMQ_0.14.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixes the issue that uses ``shots=1`` instead of the documented default - when no ``shots`` is specified for - :meth:`~qiskit.providers.ibmq.AccountProvider.run_circuits`. - -- Fixes the issue wherein a ``QiskitBackendNotFoundError`` exception is raised - when retrieving a runtime job that was submitted using a different provider - than the one used for retrieval. - -- Streaming runtime program interim results with proxies is now supported. - You can specify the proxies to use when enabling the account as usual, - for example:: - - from qiskit import IBMQ - - proxies = {'urls': {'https://127.0.0.1:8085'}} - provider = IBMQ.enable_account(API_TOKEN, proxies=proxies) - - -############# -Qiskit 0.26.1 -############# - -.. _Release Notes_0.17.4: - -Terra 0.17.4 -============ - -.. _Release Notes_0.17.4_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue with the :class:`~qiskit.utils.QuantumInstance` with - :class:`~qiskit.providers.BackendV1` backends with the - :attr:`~qiskit.providers.models.BackendConfiguration.`max_experiments` - attribute set to a value less than the number of circuits to run. Previously - the :class:`~qiskit.utils.QuantumInstance` would not correctly split the - circuits to run into separate jobs, which has been corrected. - -Aer 0.8.2 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.1 -========== - -No change - -IBM Q Provider 0.13.1 -===================== - -No change - -############# -Qiskit 0.26.0 -############# - -.. _Release Notes_0.17.3: - -Terra 0.17.3 -============ - -.. _Release Notes_0.17.3_Prelude: - -Prelude -------- - -This release includes 2 new classes, -:class:`~qiskit.result.ProbDistribution` and -:class:`~qiskit.result.QuasiDistribution`, which were needed for -compatibility with the recent qiskit-ibmq-provider release's beta support -for the -`qiskit-runtime `__. -These were only added for compatibility with that new feature in the -qiskit-ibmq-provider release and the API for these classes is considered -experimental and not considered stable for the 0.17.x release series. The -interface may change when 0.18.0 is released in the future. - -.. _Release Notes_0.17.3_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue in :func:`~qiskit.visualization.plot_histogram` function where a ``ValueError`` - would be raised when the function run on distributions with unequal lengths. - -Aer 0.8.2 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.1 -========== - -No change - -IBM Q Provider 0.13.1 -===================== - -.. _Release Notes_IBMQ_0.13.0_Prelude: - -Prelude -------- - -This release introduces a new feature ``Qiskit Runtime Service``. -Qiskit Runtime is a new architecture offered by IBM Quantum that significantly -reduces waiting time during computational iterations. You can execute your -experiments near the quantum hardware, without the interactions of multiple -layers of classical and quantum hardware slowing it down. - -Qiskit Runtime allows authorized users to upload their Qiskit quantum programs, -which are Python code that takes certain inputs, performs quantum and maybe -classical computation, and returns the processing results. The same or other -authorized users can then invoke these quantum programs by simply passing in the -required input parameters. - -Note that Qiskit Runtime is currently in private beta for select account but -will be released to the public in the near future. - -.. _Release Notes_IBMQ_0.13.0_New Features: - -New Features ------------- - -- :class:`qiskit.providers.ibmq.experiment.analysis_result.AnalysisResult` now has an additional - ``verified`` attribute which identifies if the ``quality`` has been verified by a human. - -- :class:`qiskit.providers.ibmq.experiment.Experiment` now has an additional - ``notes`` attribute which can be used to set notes on an experiment. - -- This release introduces a new feature ``Qiskit Runtime Service``. - Qiskit Runtime is a new architecture that - significantly reduces waiting time during computational iterations. - This new service allows authorized users to upload their Qiskit quantum - programs, which are Python code that takes - certain inputs, performs quantum and maybe classical computation, and returns - the processing results. The same or other authorized users can then invoke - these quantum programs by simply passing in the required input parameters. - - An example of using this new service:: - - from qiskit import IBMQ - - provider = IBMQ.load_account() - # Print all avaiable programs. - provider.runtime.pprint_programs() - - # Prepare the inputs. See program documentation on input parameters. - inputs = {...} - options = {"backend_name": provider.backend.ibmq_montreal.name()} - - job = provider.runtime.run(program_id="runtime-simple", - options=options, - inputs=inputs) - # Check job status. - print(f"job status is {job.status()}") - - # Get job result. - result = job.result() - -.. _Release Notes_IBMQ_0.13.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The deprecated ``Human Bad``, ``Computer Bad``, ``Computer Good`` and - ``Human Good`` enum values have been removed from - :class:`qiskit.providers.ibmq.experiment.constants.ResultQuality`. They - are replaced with ``Bad`` and ``Good`` values which should be used with - the ``verified`` attribute on - :class:`qiskit.providers.ibmq.experiment.analysis_result.AnalysisResult`: - - +---------------+-------------+----------+ - | Old Quality | New Quality | Verified | - +===============+=============+==========+ - | Human Bad | Bad | True | - +---------------+-------------+----------+ - | Computer Bad | Bad | False | - +---------------+-------------+----------+ - | Computer Good | Good | False | - +---------------+-------------+----------+ - | Human Good | Good | True | - +---------------+-------------+----------+ - - Furthermore, the ``NO_INFORMATION`` enum has been renamed to ``UNKNOWN``. - -- The :meth:`qiskit.providers.ibmq.IBMQBackend.defaults` method now always - returns pulse defaults if they are available, regardless whether open - pulse is enabled for the provider. - -.. _Release Notes_IBMQ_0.13.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixes the issue wherein passing in a noise model when sending a job to - an IBMQ simulator would raise a ``TypeError``. Fixes - `#894 `_ - -.. _Release Notes_IBMQ_0.13.0_Other Notes: - -Other Notes ------------ - -- The :class:`qiskit.providers.ibmq.experiment.analysis_result.AnalysisResult` - ``fit`` attribute is now optional. - - -############# -Qiskit 0.25.4 -############# - -.. _Release Notes_0.17.2: - -Terra 0.17.2 -============ - -.. _Release Notes_0.17.2_Prelude: - -Prelude -------- - -This is a bugfix release that fixes several issues from the 0.17.1 release. -Most importantly this release fixes compatibility for the -:class:`~qiskit.utils.QuantumInstance` class when running on backends that are -based on the :class:`~qiskit.providers.BackendV1` abstract class. This fixes -all the algorithms and applications built on :mod:`qiskit.algorithms` or -:mod:`qiskit.opflow` when running on newer backends. - -.. _Release Notes_0.17.2_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue with the :class:`~qiskit.transpiler.passes.BasisTranslator` - transpiler pass which in some cases would translate gates already in the - target basis. This would potentially result in both longer execution time - and less optimal results. - Fixed `#6085 `__ - -- Fixed an issue in the :class:`~qiskit.algorithms.optimisers.SPSA` when - the optimizer was initialized with a callback function via the ``callback`` - kwarg would potentially cause an error to be raised. - -- Fixed an issue in the - :meth:`qiskit.quantum_info.Statevector.expectation_value` - and :meth:`qiskit.quantum_info.DensityMatrix.expectation_value`methods - where the ``qargs`` kwarg was ignored if the operator was a - :class:`~qiskit.quantum_info.Pauli` or - :class:`~qiskit.quantum_info.SparsePauliOp` operator object. - Fixed `#6303 `__ - -- Fixed an issue in the :meth:`qiskit.quantum_info.Pauli.evolve` method - which could have resulted in the incorrect Pauli being returned when - evolving by a :class:`~qiskit.circuit.library.CZGate`, - :class:`~qiskit.circuit.library.CYGate`, or a - :class:`~qiskit.circuit.library.SwapGate` gate. - -- Fixed an issue in the :meth:`qiskit.opflow.SparseVectorStateFn.to_dict_fn` - method, which previously had at most one entry for the all zero state due - to an index error. - -- Fixed an issue in the :meth:`qiskit.opflow.SparseVectorStateFn.equals` - method so that is properly returning ``True`` or ``False`` instead of a - sparse vector comparison of the single elements. - -- Fixes an issue in the :class:`~qiskit.quantum_info.Statevector` and - :class:`~qiskit.quantum_info.DensityMatrix` probability methods - :meth:`qiskit.quantum_info.Statevector.probabilities`, - :meth:`qiskit.quantum_info.Statevector.probabilities_dict`, - :meth:`qiskit.quantum_info.DensityMatrix.probabilities`, - :meth:`qiskit.quantum_info.DensityMatrix.probabilities_dict` - where the returned probabilities could have incorrect ordering - for certain values of the ``qargs`` kwarg. - Fixed `#6320 `__ - -- Fixed an issue where the :class:`~qiskit.opflow.TaperedPauliSumOp` class - did not support the multiplication with - :class:`~qiskit.circuit.ParameterExpression` object and also did not have - a necessary :meth:`~qiskit.opflow.TaperedPauliSumOp.assign_parameters` - method for working with :class:`~qiskit.circuit.ParameterExpression` - objects. - Fixed `#6127 `__ - -- Fixed compatibility for the :class:`~qiskit.utils.QuantumInstance` class - when running on backends that are based on the - :class:`~qiskit.providers.BackendV1` abstract class. - Fixed `#6280 `__ - -Aer 0.8.2 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.1 -========== - -No change - -IBM Q Provider 0.12.3 -===================== - -No change - -############# -Qiskit 0.25.3 -############# - -Terra 0.17.1 -============ - -No change - -.. _Release Notes_Aer_0.8.2: - -Aer 0.8.2 -========= - -.. _Release Notes_Aer_0.8.2_Known Issues: - -Known Issues ------------- - -- The :class:`~qiskit.providers.aer.library.SaveExpectationValue` and - :class:`~qiskit.providers.aer.library.SaveExpectationValueVariance` have - been disabled for the `extended_stabilizer` method of the - :class:`~qiskit.providers.aer.QasmSimulator` and - :class:`~qiskit.providers.aer.AerSimulator` due to returning the - incorrect value for certain Pauli operator components. Refer to - `#1227 ` for more - information and examples. - - -.. _Release Notes_Aer_0.8.2_Bug Fixes: - -Bug Fixes ---------- - -- Fixes performance issue with how the ``basis_gates`` configuration - attribute was set. Previously there were unintended side-effects to the - backend class which could cause repeated simulation runtime to - incrementally increase. Refer to - `#1229 ` for more - information and examples. - -- Fixes a bug with the ``"multiplexer"`` simulator instruction where the - order of target and control qubits was reversed to the order in the - Qiskit instruction. - -- Fixes a bug introduced in 0.8.0 where GPU simulations would allocate - unneeded host memory in addition to the GPU memory. - -- Fixes a bug in the ``stabilizer`` simulator method of the - :class:`~qiskit.providers.aer.QasmSimulator` and - :class:`~qiskit.providers.aer.AerSimulator` where the expectation value - for the ``save_expectation_value`` and ``snapshot_expectation_value`` - could have the wrong sign for certain ``Y`` Pauli's. - - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.1 -========== - -No change - -IBM Q Provider 0.12.3 -===================== - -No change - - -############# -Qiskit 0.25.2 -############# - -Terra 0.17.1 -============ - -No change - -Aer 0.8.1 -========= - -No change - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.1 -========== - -No change - -IBM Q Provider 0.12.3 -===================== - -.. _Release Notes_IBMQ_0.12.3_Other Notes: - -Other Notes ------------ - -- The :class:`qiskit.providers.ibmq.experiment.analysis_result.AnalysisResult` ``fit`` - attribute is now optional. - -############# -Qiskit 0.25.1 -############# - -.. _Release Notes_0.17.1: - -Terra 0.17.1 -============ - -.. _Release Notes_0.17.1_Prelude: - -Prelude -------- - -This is a bugfix release that fixes several issues from the 0.17.0 release. -Most importantly this release fixes the incorrectly constructed sdist -package for the 0.17.0 release which was not actually buildable and was -blocking installation on platforms without precompiled binaries available. - -.. _Release Notes_0.17.1_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue where the :attr:`~qiskit.circuit.QuantumCircuit.global_phase` - attribute would not be preserved in the output - :class:`~qiskit.circuit.QuantumCircuit` object when the - :meth:`qiskit.circuit.QuantumCircuit.reverse_bits` method was called. - For example:: - - import math - from qiskit import QuantumCircuit - - qc = QuantumCircuit(3, 2, global_phase=math.pi) - qc.h(0) - qc.s(1) - qc.cx(0, 1) - qc.measure(0, 1) - qc.x(0) - qc.y(1) - - reversed = qc.reverse_bits() - print(reversed.global_phase) - - will now correctly print :math:`\pi`. - -- Fixed an issue where the transpiler pass - :class:`~qiskit.transpiler.passes.Unroller` didn't - preserve global phase in case of nested instructions with one rule in - their definition. - Fixed `#6134 `__ - -- Fixed an issue where the :attr:`~qiskit.circuit.ControlledGate.parameter` - attribute of a :class:`~qiskit.circuit.ControlledGate` object built from - a :class:`~qiskit.extensions.UnitaryGate` was not being set to the - unitary matrix of the :class:`~qiskit.extensions.UnitaryGate` object. - Previously, :meth:`~qiskit.extensions.UnitaryGate.control` was building a - :class:`~qiskit.circuit.ControlledGate` with the ``parameter`` attribute - set to the controlled version of - :class:`~qiskit.extensions.UnitaryGate` matrix. - This would lead to a modification of the ``parameter`` of the base - :class:`~qiskit.extensions.UnitaryGate` object and subsequent calls to - :meth:`~qiskit.circuit.ControlledGate.inverse` was creating - the inverse of a double-controlled :class:`~qiskit.extensions.UnitaryGate`. - Fixed `#5750 `__ - -- Fixed an issue with the preset pass managers - :class:`~qiskit.transpiler.preset_passmanagers.level_0_pass_manager` and - :class:`~qiskit.transpiler.preset_passmanagers.level_1_pass_manager` - (which corresponds to ``optimization_level`` 0 and 1 for - :func:`~qiskit.compiler.transpile`) where in some cases they would - produce circuits not in the requested basis. - -- Fix a bug where using :class:`~qiskit.algorithms.optimizers.SPSA` with automatic - calibration of the learning rate and perturbation (i.e. ``learning_rate`` and - ``perturbation`` are ``None`` in the initializer), stores the calibration for all - future optimizations. Instead, the calibration should be done for each new objective - function. - -.. _Aer_Release Notes_0.8.1: - -Aer 0.8.1 -========= - -.. _Aer_Release Notes_0.8.1_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue with use of the ``matrix_product_state`` method of the - :class:`~qiskit.providers.aer.AerSimulator` and - :class:`~qiskit.providers.aer.QasmSimulator` simulators when running a - noisy simulation with Kraus errors. Previously, the matrix product state - simulation method would not propogate changes to neighboring qubits after - applying the Kraus matrix. This has been fixed so the output from the - simulation is correct. - Fixed `#1184 `__ and - `#1205 `__ - -- Fixed an issue where the :class:`qiskit.extensions.Initialize` instruction - would disable measurement sampling optimization for the ``statevector`` and - ``matrix_product_state`` simulation methods of the - :class:`~qiskit.providers.aer.AerSimulator` and - :class:`~qiskit.providers.aer.QasmSimulator` simulators, even when it was - the first circuit instruction or applied to all qubits and hence - deterministic. - Fixed `#1210 `__ - -- Fix an issue with the :class:`~qiskit.providers.aer.library.SaveStatevector` - and :class:`~qiskit.providers.aer.extensions.SnapshotStatevector` - instructions when used with the ``extended_stabilizer`` simulation method - of the :class:`~qiskit.providers.aer.AerSimulator` and - :class:`~qiskit.providers.aer.QasmSimulator` simulators where it would - return an unnormalized statevector. - Fixed `#1196 `__ - -- The ``matrix_product_state`` simulation method now has support for it's - previously missing set state instruction, - :class:`qiskit.providers.aer.library.SetMatrixProductState`, which enables - setting the state of a simulation in a circuit. - -Ignis 0.6.0 -=========== - -No change - -Aqua 0.9.1 -========== - -IBM Q Provider 0.12.2 -===================== - -No change - -############# -Qiskit 0.25.0 -############# - -This release officially deprecates the Qiskit Aqua project. Accordingly, in a -future release the ``qiskit-aqua`` package will be removed from the Qiskit -metapackage, which means in that future release ``pip install qiskit`` will no -longer include ``qiskit-aqua``. The application modules that are provided by -qiskit-aqua have been split into several new packages: -``qiskit-optimization``, ``qiskit-nature``, ``qiskit-machine-learning``, and -``qiskit-finance``. These packages can be installed by themselves (via the -standard pip install command, e.g. ``pip install qiskit-nature``) or with the -rest of the Qiskit metapackage as optional extras (e.g. -``pip install 'qiskit[finance,optimization]'`` or ``pip install 'qiskit[all]'`` -The core algorithms and the operator flow now exist as part of qiskit-terra at -:mod:`qiskit.algorithms` and :mod:`qiskit.opflow`. Depending on your existing -usage of Aqua you should either use the application packages or the new modules -in Qiskit Terra. For more details on how to migrate from Qiskit Aqua, you can -refer to the `migration guide `_. - -.. _Release Notes_0.17.0: - -Terra 0.17.0 -============ - -.. _Release Notes_0.17.0_Prelude: - -Prelude -------- - -The Qiskit Terra 0.17.0 includes many new features and bug fixes. The major -new feature for this release is the introduction of the -:mod:`qiskit.algorithms` and :mod:`qiskit.opflow` modules which were -migrated and adapted from the :mod:`qiskit.aqua` project. - - -.. _Release Notes_0.17.0_New Features: - -New Features ------------- - -- The :py:func:`qiskit.pulse.call` function can now take a - :class:`~qiskit.circuit.Parameter` object along with a parameterized - subroutine. This enables assigning different values to the - :class:`~qiskit.circuit.Parameter` objects for each subroutine call. - - For example, - - .. code-block:: python - - from qiskit.circuit import Parameter - from qiskit import pulse - - amp = Parameter('amp') - - with pulse.build() as subroutine: - pulse.play(pulse.Gaussian(160, amp, 40), DriveChannel(0)) - - with pulse.build() as main_prog: - pulse.call(subroutine, amp=0.1) - pulse.call(subroutine, amp=0.3) - -- The :class:`qiskit.providers.models.QasmBackendConfiguration` has a new - field ``processor_type`` which can optionally be used to provide - information about a backend's processor in the form: - ``{"family": , "revision": , segment: }``. For example: - ``{"family": "Canary", "revision": "1.0", segment: "A"}``. - -- The :py:class:`qiskit.pulse.Schedule`, - :py:class:`qiskit.pulse.Instruction`, and :py:class:`qiskit.pulse.Channel` - classes now have a :attr:`~qiiskit.pulse.Schedule.parameter` property - which will return any :class:`~qiskit.circuit.Parameter` objects used - in the object and a :meth:`~qiskit.pulse.Schedule.is_parameterized()` - method which will return ``True`` if any parameters are used in the - object. - - For example: - - .. code-block:: python - - from qiskit.circuit import Parameter - from qiskit import pulse - - shift = Parameter('alpha') - - schedule = pulse.Schedule() - schedule += pulse.SetFrequency(shift, pulse.DriveChannel(0)) - - assert schedule.is_parameterized() == True - print(schedule.parameters) - -- Added a :class:`~qiskit.circuit.library.PiecewiseChebyshev` to the - :mod:`qiskit.circuit.library` for implementing a piecewise Chebyshev - approximation of an input function. For a given function :math:`f(x)` - and degree :math:`d`, this class class implements - a piecewise polynomial Chebyshev approximation on :math:`n` qubits - to :math:`f(x)` on the given intervals. All the polynomials in the - approximation are of degree :math:`d`. - - For example: - - .. code-block:: python - - import numpy as np - from qiskit import QuantumCircuit - from qiskit.circuit.library.arithmetic.piecewise_chebyshev import PiecewiseChebyshev - f_x, degree, breakpoints, num_state_qubits = lambda x: np.arcsin(1 / x), 2, [2, 4], 2 - pw_approximation = PiecewiseChebyshev(f_x, degree, breakpoints, num_state_qubits) - pw_approximation._build() - qc = QuantumCircuit(pw_approximation.num_qubits) - qc.h(list(range(num_state_qubits))) - qc.append(pw_approximation.to_instruction(), qc.qubits) - qc.draw(output='mpl') - -- The :py:class:`~qiskit.providers.models.BackendProperties` class now - has a :meth:`~qiskit.providers.models.BackendProperties.readout_length` - method, which returns the readout length [sec] of the given qubit. - -- A new class, :py:class:`~qiskit.pulse.ScheduleBlock`, has been added to - the :class:`qiskit.pulse` module. This class provides a new representation - of a pulse program. This representation is best suited for the pulse - builder syntax and is based on relative instruction ordering. - - This representation takes ``alignment_context`` instead of specifying - starting time ``t0`` for each instruction. The start time of instruction is - implicitly allocated with the specified transformation and relative - position of instructions. - - The :py:class:`~qiskit.pulse.ScheduleBlock` allows for lazy instruction - scheduling, meaning we can assign arbitrary parameters to the duration of - instructions. - - For example: - - .. code-block:: python - - from qiskit.pulse import ScheduleBlock, DriveChannel, Gaussian - from qiskit.pulse.instructions import Play, Call - from qiskit.pulse.transforms import AlignRight - from qiskit.circuit import Parameter - - dur = Parameter('rabi_duration') - - block = ScheduleBlock(alignment_context=AlignRight()) - block += Play(Gaussian(dur, 0.1, dur/4), DriveChannel(0)) - block += Call(measure_sched) # subroutine defined elsewhere - - this code defines an experiment scanning a Gaussian pulse's duration - followed by a measurement ``measure_sched``, i.e. a Rabi experiment. - You can reuse the ``block`` object for every scanned duration - by assigning a target duration value. - -- Added a new function :func:`~qiskit.visualization.array_to_latex` to - the :mod:`qiskit.visualization` module that can be used to represent - and visualize vectors and matrices with LaTeX. - - .. code-block:: python - - from qiskit.visualization import array_to_latex - from numpy import sqrt, exp, pi - mat = [[0, exp(pi*.75j)], - [1/sqrt(8), 0.875]] - array_to_latex(mat) - -- The :class:`~qiskit.quantum_info.Statevector` and - :class:`~qiskit.quantum_info.DensityMatrix` classes now have - :meth:`~qiskit.quantum_info.Statevector.draw` methods which allow objects - to be drawn as either text matrices, IPython Latex objects, Latex source, - Q-spheres, Bloch spheres and Hinton plots. By default the output type - is the equivalent output from ``__repr__`` but this default can be changed - in a user config file by setting the ``state_drawer`` option. For example: - - .. code-block:: python - - from qiskit.quantum_info import DensityMatrix - dm = DensityMatrix.from_label('r0') - dm.draw('latex') - - .. code-block:: python - - from qiskit.quantum_info import Statevector - sv = Statevector.from_label('+r') - sv.draw('qsphere') - - Additionally, the :meth:`~qiskit.quantum_info.DensityMatrix.draw` method - is now used for the ipython display of these classes, so if you change the - default output type in a user config file then when a - :class:`~qiskit.quantum_info.Statevector` or a - :class:`~qiskit.quantum_info.DensityMatrix` object are displayed in - a jupyter notebook that output type will be used for the object. - -- Pulse :class:`qiskit.pulse.Instruction` objects and - parametric pulse objects (eg :class:`~qiskit.pulse.library.Gaussian` now - support using :class:`~qiskit.circuit.Parameter` and - :class:`~qiskit.circuit.ParameterExpression` objects for the ``duration`` - parameter. For example: - - .. code-block:: python - - from qiskit.circuit import Parameter - from qiskit.pulse import Gaussian - - dur = Parameter('x_pulse_duration') - double_dur = dur * 2 - rx_pulse = Gaussian(dur, 0.1, dur/4) - double_rx_pulse = Gaussian(double_dir, 0.1, dur/4) - - Note that while we can create an instruction with a parameterized - ``duration`` adding an instruction with unbound parameter ``duration`` - to a schedule is supported only by the newly introduced representation - :class:`~qiskit.pulse.ScheduleBlock`. See the known issues release notes - section for more details. - -- The :meth:`~qiskit.providers.basicaer.QasmSimulatorPy.run` method for the - :class:`~qiskit.providers.basicaer.QasmSimulatorPy`, - :class:`~qiskit.providers.basicaer.StatevectorSimulatorPy`, and - :class:`~qiskit.providers.basicaer.UnitarySimulatorPy` backends now takes a - :class:`~qiskit.circuit.QuantumCircuit` (or a list of - :class:`~qiskit.circuit.QuantumCircuit` objects) as its input. - The previous :class:`~qiskit.qobj.QasmQobj` object is still supported for - now, but will be deprecated in a future release. - - For an example of how to use this see:: - - from qiskit import transpile, QuantumCircuit - - from qiskit.providers.basicaer import BasicAer - - backend = BasicAer.get_backend('qasm_simulator') - - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.cx(0, 1) - circuit.measure_all() - - tqc = transpile(circuit, backend) - result = backend.run(tqc, shots=4096).result() - -- The :class:`~qiskit.transpiler.passes.CommutativeCancellation` transpiler - pass has a new optional kwarg on the constructor ``basis_gates``, which - takes the a list of the names of basis gates for the target backend. - When specified the pass will only use gates in the ``basis_gates`` kwarg. - Previously, the pass would automatically replace consecutive gates which - commute with :class:`~qiskit.circuit.library.ZGate` with the - :class:`~qiskit.circuit.library.U1Gate` unconditionally. The ``basis_gates`` - kwarg enables you to specify which z-rotation gates are present in - the target basis to avoid this. - -- The constructors of the :class:`~qiskit.circuit.Bit` class and subclasses, - :class:`~qiskit.circuit.Qubit`, :class:`~qiskit.circuit.Clbit`, and - :class:`~qiskit.circuit.AncillaQubit`, have been updated such that their - two parameters, ``register`` and ``index`` are now optional. This enables - the creation of bit objects that are independent of a register. - -- A new class, - :class:`~qiskit.circuit.classicalfunction.BooleanExpression`, has been - added to the :mod:`qiskit.circuit.classicalfunction` module. This class - allows for creating an oracle from a Python boolean expression. For example: - - .. code-block:: python - - from qiskit.circuit import BooleanExpression, QuantumCircuit - - expression = BooleanExpression('~x & (y | z)') - circuit = QuantumCircuit(4) - circuit.append(expression, [0, 1, 2, 3]) - circuit.draw('mpl') - - .. code-block:: python - - circuit.decompose().draw('mpl') - - The :class:`~qiskit.circuit.classicalfunction.BooleanExpression` also - includes a method, - :meth:`~qiskit.circuit.classicalfunction.BooleanExpression.from_dimacs_file`, - which allows loading formulas described in the - `DIMACS-CNF `__ - format. For example: - - .. code-block:: - - from qiskit.circuit import BooleanExpression, QuantumCircuit - - boolean_exp = BooleanExpression.from_dimacs_file("simple_v3_c2.cnf") - circuit = QuantumCircuit(boolean_exp.num_qubits) - circuit.append(boolean_exp, range(boolean_exp.num_qubits)) - circuit.draw('text') - - .. parsed-literal:: - - ┌───────────────────┐ - q_0: ┤0 ├ - │ │ - q_1: ┤1 ├ - │ SIMPLE_V3_C2.CNF │ - q_2: ┤2 ├ - │ │ - q_3: ┤3 ├ - └───────────────────┘ - - .. code-block:: - - circuit.decompose().draw('text') - - .. parsed-literal:: - - q_0: ──o────o──────────── - │ │ - q_1: ──■────o────■─────── - │ │ │ - q_2: ──■────┼────o────■── - ┌─┴─┐┌─┴─┐┌─┴─┐┌─┴─┐ - q_3: ┤ X ├┤ X ├┤ X ├┤ X ├ - └───┘└───┘└───┘└───┘ - -- Added a new class, :class:`~qiskit.circuit.library.PhaseOracle`, has been - added to the :mod:`qiskit.circuit.library` module. This class enables the - construction of phase oracle circuits from Python boolean expressions. - - .. code-block:: python - - from qiskit.circuit.library.phase_oracle import PhaseOracle - - oracle = PhaseOracle('x1 & x2 & (not x3)') - oracle.draw('mpl') - - These phase oracles can be used as part of a larger algorithm, for example - with :class:`qiskit.algorithms.AmplificationProblem`: - - .. code-block:: python - - from qiskit.algorithms import AmplificationProblem, Grover - from qiskit import BasicAer - - backend = BasicAer.get_backend('qasm_simulator') - - problem = AmplificationProblem(oracle, is_good_state=oracle.evaluate_bitstring) - grover = Grover(quantum_instance=backend) - result = grover.amplify(problem) - result.top_measurement - - The :class:`~qiskit.circuit.library.PhaseOracle` class also includes a - :meth:`~qiskit.circuit.library.PhaseOracle.from_dimacs_file` method which - enables constructing a phase oracle from a file describing a formula in the - `DIMACS-CNF `__ - format. - - .. code-block:: - - from qiskit.circuit.library.phase_oracle import PhaseOracle - - oracle = PhaseOracle.from_dimacs_file("simple_v3_c2.cnf") - oracle.draw('text') - - .. parsed-literal:: - - state_0: ─o───────o────────────── - │ ┌───┐ │ ┌───┐ - state_1: ─■─┤ X ├─■─┤ X ├─■────── - │ └───┘ └───┘ │ ┌───┐ - state_2: ─■───────────────o─┤ Z ├ - └───┘ - -- All transpiler passes (ie any instances of - :class:`~qiskit.transpiler.BasePass`) are now directly callable. - Calling a pass provides a convenient interface for running the pass - on a :class:`~qiskit.circuit.QuantumCircuit` object. - - For example, running a single transformation pass, such as - :class:`~qiskit.transpiler.passes.BasisTranslator`, can be done with: - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.transpiler.passes import BasisTranslator - from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - - circuit = QuantumCircuit(1) - circuit.h(0) - - pass_instance = BasisTranslator(sel, ['rx', 'rz', 'cx']) - result = pass_instance(circuit) - result.draw(output='mpl') - - When running an analysis pass, a property set (as ``dict`` or as - :class:`~qiskit.transpiler.PropertySet`) - needs to be added as a parameter and it might be modified "in-place". - For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.transpiler.passes import Depth - - circuit = QuantumCircuit(1) - circuit.h(0) - - property_set = {} - pass_instance = Depth() - pass_instance(circuit, property_set) - print(property_set) - -- The :class:`~qiskit.qobj.QasmQobjConfig` class now has an optional - kwarg for ``meas_level`` and ``meas_return``. These fields can be used - to enable generating :class:`~qiskit.qobj.QasmQobj` job payloads that - support ``meas_level=1`` (kerneled data) for circuit jobs (previously - this was only exposed for :class:`~qiskit.qobj.PulseQobj` objects). - The :func:`~qiskit.compiler.assemble` function has been updated - to set this field for :class:`~qiskit.qobj.QasmQobj` objects it - generates. - -- A new :meth:`~qiskit.circuit.QuantumCircuit.tensor` method has been - added to the :class:`~qiskit.circuit.QuantumCircuit` class. This - method enables tensoring another circuit with an existing circuit. - This method works analogously to - :meth:`qiskit.quantum_info.Operator.tensor` - and is consistent with the little-endian convention of Qiskit. - - For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - top = QuantumCircuit(1) - top.x(0); - bottom = QuantumCircuit(2) - bottom.cry(0.2, 0, 1); - bottom.tensor(top).draw(output='mpl') - -- The :class:`qiskit.circuit.QuantumCircuit` class now supports arbitrary - free form metadata with the :attr:`~qiskit.circuit.QuantumCircuit.metadata` - attribute. A user (or program built on top of - :class:`~qiskit.circuit.QuantumCircuit`) can attach metadata to a circuit - for use in tracking the circuit. For example:: - - from qiskit.circuit import QuantumCircuit - - qc = QuantumCircuit(2, user_metadata_field_1='my_metadata', - user_metadata_field_2='my_other_value') - - or:: - - from qiskit.circuit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.metadata = {'user_metadata_field_1': 'my_metadata', - 'user_metadata_field_2': 'my_other_value'} - - This metadata will **not** be used for influencing the execution of the - circuit but is just used for tracking the circuit for the lifetime of the - object. The ``metadata`` attribute will persist between any circuit - transforms including :func:`~qiskit.compiler.transpile` and - :func:`~qiskit.compiler.assemble`. The expectation is for providers to - associate the metadata in the result it returns, so that users can - filter results based on circuit metadata the same way they can currently - do with ``QuantumCircuit.name``. - -- Add a new operator class :class:`~qiskit.quantum_info.CNOTDihedral` has - been added to the :mod:`qiskit.quantum_info` module. This class is - used to represent the CNOT-Dihedral group, which is generated by the - quantum gates :class:`~qiskit.circuit.library.CXGate`, - :class:`~qiskit.circuit.library.TGate`, - and :class:`~qiskit.circuit.library.XGate`. - -- Adds a ``&`` (``__and__``) binary operator to ``BaseOperator`` subclasses - (eg :class:`qiskit.quantum_info.Operator`) in the - :mod:`qiskit.quantum_info` module. This is shorthand to call the - classes :meth:`~qiskit.quantum_info.Operator.compose` method - (ie ``A & B == A.compose(B)``). - - For example: - - .. code:: python - - import qiskit.quantum_info as qi - - qi.Pauli('X') & qi.Pauli('Y') - -- Adds a ``&`` (``__and__``) binary operator to - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes. This is shorthand to - call the classes :meth:`~qiskit.quantum_info.Statevector.evolve` method - (ie ``psi & U == psi.evolve(U)``). - - For example: - - .. code:: python - - import qiskit.quantum_info as qi - - qi.Statevector.from_label('0') & qi.Pauli('X') - -- A new a new 2-qubit gate, :class:`~qiskit.circuit.library.ECRGate`, - the echo cross-resonance (ECR), has been added to the - :mod:`qiskit.circuit.library` module along with a corresponding method, - :meth:`~qiskit.circuit.QuantumCircuit.ecr` for the - :class:`~qiskit.circuit.QuantumCircuit` class. The ECR gate is two - :math:`CR(\frac{π}{4})` pulses with an - :class:`~qiskit.circuit.library.XGate` between them for the echo. This gate - is locally equivalent to a :class:`~qiskit.circuit.library.CXGate` (can - convert to a CNOT with local pre- or post-rotation). It is the native gate - on current IBM hardware and compiling to it allows the pre-/post-rotations - to be merged into the rest of the circuit. - -- A new kwarg ``approximation_degree`` has been added to the - :func:`~qiskit.compiler.transpile` function for enabling - approximate compilation. Valid values range from 0 to 1, and higher - means less approximation. This is a heuristic dial - to experiment with circuit approximations. The concrete interpretation - of this number is left to each pass, which may use it to perform - some approximate version of the pass. Specific examples include - the :class:`~qiskit.transpiler.passes.UnitarySynthesis` pass or the - or translators to discrete gate sets. If a pass does not support this - option, it implies exact transformation. - -- Two new transpiler passess, :class:`~qiskit.transpiler.passes.GateDirection` - and :class:`qiskit.transpiler.passes.CheckGateDirection`, were added to the - :mod:`qiskit.transpiler.passes` module. These new passes are inteded to - be more general replacements for - :class:`~qiskit.transpiler.passes.CXDirection` and - :class:`~qiskit.transpiler.passes.CheckCXDirection` (which are both now - deprecated, see the deprecation notes for more details) that perform the - same function but work with other gates beside just - :class:`~qiskit.circuit.library.CXGate`. - -- When running on Windows, parallel execution with the - :func:`~qiskit.tools.parallel_map` function can now be enabled (it is - still disabled by default). To do this you can either set - ``parallel = True`` in a user config file, or set the ``QISKIT_PARALLEL`` - environment variable to ``TRUE`` (this will also effect - :func:`~qiskit.compiler.transpile` and :func:`~qiskit.compiler.assemble` - which both use :func:`~qiskit.tools.parallel_map` internally). It is - important to note that when enabling parallelism on Windows there are - limitations around how Python launches processes for Windows, see the - Known Issues section below for more details on the limitations with - parallel execution on Windows. - -- A new function, :func:`~qiskit.quantum_info.hellinger_distance`, for - computing the Hellinger distance between two counts distributions has - been added to the :mod:`qiskit.quantum_info` module. - -- The :func:`~qiskit.quantum_info.decompose_clifford` function in the - :mod:`qiskit.quantum_info` module (which gets used internally by the - :meth:`qiskit.quantum_info.Clifford.to_circuit` method) has a new kwarg - ``method`` which enables selecting the synthesis method used by either - setting it to ``'AG'`` or ``'greedy'``. By default for more than three - qubits it is set to ``'greedy'`` which uses a non-optimal greedy compilation - routine for Clifford elements synthesis, by Bravyi et. al., which typically - yields better CX cost compared to the previously used Aaronson-Gottesman - method (for more than two qubits). You can use the ``method`` kwarg to revert - to the previous default Aaronson-Gottesman method by setting ``method='AG'``. - -- The :class:`~qiskit.extensions.Initialize` class in the - :mod:`qiskit.extensions` module can now be constructed using an integer. - The '1' bits of the integer will insert a :class:`~qiskit.circuit.Reset` - and an :class:`~qiskit.circuit.library.XGate` into the circuit for the - corresponding qubit. This will be done using the standard little-endian - convention is qiskit, ie the rightmost bit of the integer will set qubit - 0. For example, setting the parameter in - :class:`~qiskit.extensions.Initialize` equal to ``5`` will set qubits 0 - and 2 to value 1. - - .. code-block:: python - - from qiskit.extensions import Initialize - - initialize = Initialize(13) - initialize.definition.draw('mpl') - -- The :class:`~qiskit.extensions.Initialize` class in the - :mod:`qiskit.extensions` module now supports constructing directly from - a Pauli label (analogous to the - :meth:`qiskit.quantum_info.Statevector.from_label` method). The Pauli label - refer to basis states of the Pauli eigenstates Z, X, Y. These labels use - Qiskit's standard little-endian notation, for example a label of ``'01'`` - would initialize qubit 0 to :math:`|1\rangle` and qubit 1 to - :math:`|0\rangle`. - - .. code-block:: python - - from qiskit.extensions import Initialize - - initialize = Initialize("10+-lr") - initialize.definition.draw('mpl') - -- The kwarg, ``template_list``, for the constructor of the - :class:`qiskit.transpiler.passes.TemplateOptimization` transpiler pass - now supports taking in a list of both - :class:`~qiskit.circuit.QuantumCircuit` and - :class:`~qiskit.dagcircuit.DAGDependency` objects. Previously, only - :class:`~qiskit.circuit.QuantumCircuit` were accepted (which were internally - converted to :class:`~qiskit.dagcircuit.DAGDependency` objects) in the - input list. - -- A new transpiler pass, - :py:class:`qiskit.transpiler.passes.RZXCalibrationBuilder`, capable - of generating calibrations and adding them to a quantum circuit has been - introduced. This pass takes calibrated - :class:`~qiskit.circuit.library.CXGate` objects and creates the - calibrations for :class:`qiskit.circuit.library.RZXGate` objects with an - arbitrary rotation angle. The schedules are created by stretching and - compressing the :class:`~qiskit.pulse.GaussianSquare` pulses of the - echoed-cross resonance gates. - -- New template circuits for using :class:`qiskit.circuit.library.RZXGate` - are added to the :mod:`qiskit.circuit.library` module (eg - :class:`~qiskit.circuit.library.rzx_yz`). This enables pairing - the :class:`~qiskit.transpiler.passes.TemplateOptimization` pass with the - :py:class:`qiskit.transpiler.passes.RZXCalibrationBuilder` pass to - automatically find and replace gate sequences, such as - ``CNOT - P(theta) - CNOT``, with more efficent circuits based on - :class:`qiskit.circuit.library.RZXGate` with a calibration. - -- The matplotlib output type for the - :func:`~qiskit.visualization.circuit_drawer` and - the :meth:`~qiskit.circuit.QuantumCircuit.draw` method for the - :class:`~qiskit.circuit.QuantumCircuit` class now supports configuration - files for setting the visualization style. In previous releases, there was - basic functionality that allowed users to pass in a ``style`` kwarg that - took in a ``dict`` to customize the colors and other display features of - the ``mpl`` drawer. This has now been expanded so that these dictionaries - can be loaded from JSON files directly without needing to pass a dictionary. - This enables users to create new style files and use that style for - visualizations by passing the style filename as a string to the ``style`` - kwarg. - - To leverage this feature you must set the ``circuit_mpl_style_path`` - option in a user config file. This option should be set to the path you - want qiskit to search for style JSON files. If specifying multiple path - entries they should be separated by ``:``. For example, setting - ``circuit_mpl_style_path = ~/.qiskit:~/user_styles`` in a user config - file will look for JSON files in both ``~/.qiskit`` and ``~/user_styles``. - -- A new kwarg, ``format_marginal`` has been added to the function - :func:`~qiskit.result.utils.marginal_counts` which when set to ``True`` - formats the counts output according to the - :attr:`~qiskit.circuit.QuantumCircuit.cregs` in the circuit and missing - indices are represented with a ``_``. For example: - - .. code-block:: python - - from qiskit import QuantumCircuit, execute, BasicAer, result - from qiskit.result.utils import marginal_counts - qc = QuantumCircuit(5, 5) - qc.x(0) - qc.measure(0, 0) - - result = execute(qc, BasicAer.get_backend('qasm_simulator')).result() - print(marginal_counts(result.get_counts(), [0, 2, 4], format_marginal=True)) - -- Improved the performance of - :meth:`qiskit.quantum_info.Statevector.expectation_value` and - :meth:`qiskit.quantum_info.DensityMatrix.expectation_value` when the - argument operator is a :class:`~qiskit.quantum_info.Pauli` or - :class:`~qiskit.quantum_info.SparsePauliOp` operator. - -- The user config file has 2 new configuration options, ``num_processes`` and - ``parallel``, which are used to control the default behavior of - :func:`~qiskit.tools.parallel_map`. The ``parallel`` option is a boolean - that is used to dictate whether :func:`~qiskit.tools.parallel_map` will - run in multiple processes or not. If it set to ``False`` calls to - :func:`~qiskit.tools.parallel_map` will be executed serially, while setting - it to ``True`` will enable parallel execution. The ``num_processes`` option - takes an integer which sets how many CPUs to use when executing in parallel. - By default it will use the number of CPU cores on a system. - -- There are 2 new environment variables, ``QISKIT_PARALLEL`` and - ``QISKIT_NUM_PROCS``, that can be used to control the default behavior of - :func:`~qiskit.tools.parallel_map`. The ``QISKIT_PARALLEL`` option can be - set to the ``TRUE`` (any capitalization) to set the default to run in - multiple processes when :func:`~qiskit.tools.parallel_map` is called. If it - is set to any other - value :func:`~qiskit.tools.parallel_map` will be executed serially. - ``QISKIT_NUM_PROCS`` takes an integer (for example ``QISKIT_NUM_PROCS=5``) - which will be used as the default number of processes to run with. Both - of these will take precedence over the equivalent option set in the user - config file. - -- A new method, :meth:`~qiskit.circuit.ParameterExpression.gradient`, has - been added to the :class:`~qiskit.circuit.ParameterExpression` class. This - method is used to evaluate the gradient of a - :class:`~qiskit.circuit.ParameterExpression` object. - -- The ``__eq__`` method (ie what is called when the ``==`` operator is used) - for the :class:`~qiskit.circuit.ParameterExpression` now allows for the - comparison with a numeric value. Previously, it was only possible - to compare two instances of - :class:`~qiskit.circuit.ParameterExpression` with ``==``. For example:: - - from qiskit.circuit import Parameter - - x = Parameter("x") - y = x + 2 - y = y.assign(x, -1) - - assert y == 1 - -- The :class:`~qiskit.circuit.library.PauliFeatureMap` class in the - :mod:`qiskit.circuit.library` module now supports adjusting the rotational - factor, :math:`\alpha`, by either setting using the kwarg ``alpha`` on - the constructor or setting the - :attr:`~qiskit.circuit.library.PauliFeatureMap.alpha` attribute after - creation. Previously this value was fixed at ``2.0``. Adjusting this - attribute allows for better control of decision boundaries and provides - additional flexibility handling the input features without needing - to explicitly scale them in the data set. - -- A new :class:`~qiskit.circuit.Gate` class, - :class:`~qiskit.circuit.library.PauliGate`, has been added - the :class:`qiskit.circuit.library` module and corresponding method, - :meth:`~qiskit.circuit.QuantumCircuit.pauli`, was added to the - :class:`~qiskit.circuit.QuantumCircuit` class. This new gate class enables - applying several individual pauli gates to different qubits at the - simultaneously. This is primarily useful for simulators which can use this - new gate to more efficiently implement multiple simultaneous Pauli gates. - -- Improve the :class:`qiskit.quantum_info.Pauli` operator. - This class now represents and element from the full N-qubit Pauli group - including complex coefficients. It now supports the Operator API methods - including :meth:`~qiskit.quantum_info.Pauli.compose`, - :meth:`~qiskit.quantum_info.Pauli.dot`, - :meth:`~qiskit.quantum_info.Pauli.tensor` etc, where compose and dot are - defined with respect to the full Pauli group. - - This class also allows conversion to and from the string representation - of Pauli's for convenience. - - For example - - .. code-block:: python - - from qiskit.quantum_info import Pauli - - P1 = Pauli('XYZ') - P2 = Pauli('YZX') - P1.dot(P2) - - Pauli's can also be directly appended to - :class:`~qiskit.circuit.QuantumCircuit` objects - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.quantum_info import Pauli - - circ = QuantumCircuit(3) - circ.append(Pauli('XYZ'), [0, 1, 2]) - circ.draw(output='mpl') - - Additional methods allow computing when two Pauli's commute (using the - :meth:`~qiskit.quantum_info.Pauli.commutes` method) or anticommute - (using the :meth:`~qiskit.quantum_info.Pauli.anticommutes` method), and - computing the Pauli resulting from Clifford conjugation - :math:`P^\prime = C.P.C^\dagger` - using the :meth:`~qiskit.quantum_info.Pauli.evolve` method. - - See the API documentation of the :class:`~qiskit.quantum_info.Pauli` class - for additional information. - -- A new function, :func:`~qiskit.quantum_info.random_pauli`, for generating a - random element of the N-qubit Pauli group has been added to the - :mod:`qiskit.quantum_info` module. - -- A new class, - :class:`~qiskit.circuit.library.PiecewisePolynomialPauliRotations`, has - been added to the :mod:`qiskit.circuit.library` module. This circuit library - element is used for mapping a piecewise polynomial function, :math:`f(x)`, - which is defined through breakpoints and coefficients, on qubit amplitudes. - The breakpoints :math:`(x_0, ..., x_J)` are a subset of :math:`[0, 2^n-1]`, - where :math:`n` is the number of state qubits. The corresponding - coefficients :math:`[a_{j,1},...,a_{j,d}]`, where :math:`d` is the highest - degree among all polynomials. Then :math:`f(x)` is defined as: - - .. math:: - - f(x) = \begin{cases} - 0, x < x_0 \\ - \sum_{i=0}^{i=d}a_{j,i} x^i, x_j \leq x < x_{j+1} - \end{cases} - - where we implicitly assume :math:`x_{J+1} = 2^n`. And the mapping applied - to the amplitudes is given by - - .. math:: - - F|x\rangle |0\rangle = \cos(p_j(x))|x\rangle |0\rangle + \sin(p_j(x))|x\rangle |1\rangle - - This mapping is based on controlled Pauli Y-rotations and constructed using - the :class:`~qiskit.circuit.library.PolynomialPauliRotations`. - -- A new module :mod:`qiskit.algorithms` has been introduced. This module - contains functionality equivalent to what has previously been - provided by the :mod:`qiskit.aqua.algorithms` module (which is now - deprecated) and provides the building blocks for constructing quantum - algorithms. For details on migrating from ``qiskit-aqua`` to this new - module, please refer to the - `migration guide `_. - -- A new module :mod:`qiskit.opflow` has been introduced. This module - contains functionality equivalent to what has previously been - provided by the :mod:`qiskit.aqua.operators` module (which is now - deprecated) and provides the operators and state functions which are - used to build quantum algorithms. For details on migrating from - ``qiskit-aqua`` to this new module, please refer to the - `migration guide `_. - -- This is the first release that includes precompiled binary wheels for - the for Linux aarch64 systems. If you are running a manylinux2014 - compatible aarch64 Linux system there are now precompiled wheels available - on PyPI, you are no longer required to build from source to install - qiskit-terra. - -- The :func:`qiskit.quantum_info.process_fidelity` function is now able to be - used with a non-unitary target channel. In this case the returned value is - equivalent to the :func:`qiskit.quantum_info.state_fidelity` of the - normalized :class:`qiskit.quantum_info.Choi` matrices for the channels. - - Note that the :func:`qiskit.quantum_info.average_gate_fidelity` and - :func:`qiskit.quantum_info.gate_error` functions still require the target - channel to be unitary and will raise an exception if it is not. - -- Added a new pulse builder function, :func:`qiskit.pulse.macro`. - This enables normal Python functions to be decorated as macros. - This enables pulse builder functions to be used within the decorated - function. The builder macro can then be called from within a pulse - building context, enabling code reuse. - - For Example: - - .. code-block:: python - - from qiskit import pulse - - @pulse.macro - def measure(qubit: int): - pulse.play(pulse.GaussianSquare(16384, 256, 15872), - pulse.MeasureChannel(qubit)) - mem_slot = pulse.MemorySlot(0) - pulse.acquire(16384, pulse.AcquireChannel(0), mem_slot) - return mem_slot - - with pulse.build(backend=backend) as sched: - mem_slot = measure(0) - print(f"Qubit measured into {mem_slot}") - - sched.draw() - -- A new class, :class:`~qiskit.circuit.library.PauliTwoDesign`, was added - to the :mod:`qiskit.circuit.library` which implements a particular form - of a 2-design circuit from https://arxiv.org/pdf/1803.11173.pdf - For instance, this circuit can look like: - - .. code-block:: python - - from qiskit.circuit.library import PauliTwoDesign - circuit = PauliTwoDesign(4, reps=2, seed=5, insert_barriers=True) - circuit.decompose().draw(output='mpl') - -- A new pulse drawer :func:`qiskit.visualization.pulse_v2.draw` - (which is aliased as ``qiskit.visualization.pulse_drawer_v2``) is now - available. This new pulse drawer supports multiple new features not - present in the original pulse drawer - (:func:`~qiskit.visualization.pulse_drawer`). - - * Truncation of long pulse instructions. - * Visualization of parametric pulses. - * New stylesheets ``IQXStandard``, ``IQXSimple``, ``IQXDebugging``. - * Visualization of system info (channel frequency, etc...) by specifying - :class:`qiskit.providers.Backend` objects for visualization. - * Specifying ``axis`` objects for plotting to allow further extension of - generated plots, i.e., for publication manipulations. - - New stylesheets can take callback functions that dynamically modify the apperance of - the output image, for example, reassembling a collection of channels, - showing details of instructions, updating appearance of pulse envelopes, etc... - You can create custom callback functions and feed them into a stylesheet instance to - modify the figure appearance without modifying the drawer code. - See pulse drawer module docstrings for details. - - Note that file saving is now delegated to Matplotlib. - To save image files, you need to call ``savefig`` method with returned ``Figure`` object. - -- Adds a :meth:`~qiskit.quantum_info.Statevector.reverse_qargs` method to the - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes. This method reverses - the order of subsystems in the states and is equivalent to the - :meth:`qiskit.circuit.QuantumCircuit.reverse_bits` method for N-qubit - states. For example: - - .. code-block:: python - - from qiskit.circuit.library import QFT - from qiskit.quantum_info import Statevector - - circ = QFT(3) - - state1 = Statevector.from_instruction(circ) - state2 = Statevector.from_instruction(circ.reverse_bits()) - - state1.reverse_qargs() == state2 - -- Adds a :meth:`~qiskit.quantum_info.Operator.reverse_qargs` method to the - :class:`qiskit.quantum_info.Operator` class. This method reverses - the order of subsystems in the operator and is equivalent to the - :meth:`qiskit.circuit.QuantumCircuit.reverse_bits` method for N-qubit - operators. For example: - - .. code-block:: python - - from qiskit.circuit.library import QFT - from qiskit.quantum_info import Operator - - circ = QFT(3) - - op1 = Operator(circ) - op2 = Operator(circ.reverse_bits()) - - op1.reverse_qargs() == op2 - -- The ``latex`` output method for the - :func:`qiskit.visualization.circuit_drawer` function and the - :meth:`~qiskit.circuit.QuantumCircuit.draw` method now will use a - user defined label on gates in the output visualization. For example:: - - import math - - from qiskit.circuit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.h(0) - qc.rx(math.pi/2, 0, label='My Special Rotation') - - qc.draw(output='latex') - -- The ``routing_method`` kwarg for the :func:`~qiskit.compiler.transpile` - function now accepts a new option, ``'none'``. When - ``routing_method='none'`` no routing pass will be run as part of the - transpilation. If the circuit does not fit coupling map a - :class:`~qiskit.transpiler.exceptions.TranspilerError` exception will be - raised. - -- A new gate class, :class:`~qiskit.circuit.library.RVGate`, was added to - the :mod:`qiskit.circuit.library` module along with the corresponding - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.rv`. The - :class:`~qiskit.circuit.library.RVGate` is a general rotation gate, similar - to the :class:`~qiskit.circuit.library.UGate`, but instead of specifying - Euler angles the three components of a rotation vector are specified where - the direction of the vector specifies the rotation axis and the magnitude - specifies the rotation angle about the axis in radians. For example:: - - import math - - import np - - from qiskit.circuit import QuantumCircuit - - qc = QuantumCircuit(1) - theta = math.pi / 5 - phi = math.pi / 3 - # RGate axis: - axis = np.array([math.cos(phi), math.sin(phi)]) - rotation_vector = theta * axis - qc.rv(*rotation_vector, 0) - -- Unbound :class:`~qiskit.circuit.Parameter` objects used in a - :class:`~qiskit.circuit.QuantumCircuit` object will now be sorted - by name. This will take effect for the parameters returned by the - :attr:`~qiskit.circuit.QuantumCircuit.parameters` attribute. Additionally, - the :meth:`qiskit.circuit.QuantumCircuit.bind_parameters` and - :meth:`qiskit.circuit.QuantumCircuit.assign_parameters` methods can now take - in a list of a values which will bind/assign them to the parameters in - name-sorted order. Previously these methods would only take a dictionary of - parameters and values. For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit, Parameter - - circuit = QuantumCircuit(1) - circuit.rx(Parameter('x'), 0) - circuit.ry(Parameter('y'), 0) - - print(circuit.parameters) - - bound = circuit.bind_parameters([1, 2]) - bound.draw(output='mpl') - -- The constructors for the :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes can now take a - :class:`~qiskit.circuit.QuantumCircuit` object in to build a - :class:`~qiskit.quantum_info.Statevector` and - :class:`~qiskit.quantum_info.DensityMatrix` object from that circuit, - assuming that the qubits are initialized in :math:`|0\rangle`. For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.quantum_info import Statevector - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - - statevector = Statevector(qc) - statevector.draw(output='latex') - -- New fake backend classes are available under ``qiskit.test.mock``. These - included mocked versions of ``ibmq_casablanca``, ``ibmq_sydney``, - ``ibmq_mumbai``, ``ibmq_lima``, ``ibmq_belem``, ``ibmq_quito``. As - with the other fake backends, these include snapshots of calibration data - (i.e. ``backend.defaults()``) and error data (i.e. ``backend.properties()``) - taken from the real system, and can be used for local testing, compilation - and simulation. - - -.. _Release Notes_0.17.0_Known Issues: - -Known Issues ------------- - -- Attempting to add an :class:`qiskit.pulse.Instruction` object - with a parameterized ``duration`` (ie the value of ``duration`` is - an unbound :class:`~qiskit.circuit.Parameter` or - :class:`~qiskit.circuit.ParameterExpression` object) to a - :class:`qiskit.pulse.Schedule` is not supported. Attempting to do - so will result in ``UnassignedDurationError`` - :class:`~qiskit.pulse.PulseError` being raised. This is a limitation of - how the :class:`~qiskit.pulse.Instruction` overlap constraints are - evaluated currently. This is supported by :class:`~qiskit.pulse.ScheduleBlock`, - in which the overlap constraints are evaluated just before the execution. - -- On Windows systems when parallel execution is enabled for - :func:`~qiskit.tools.parallel_map` parallelism may not work when called - from a script running outside of a ``if __name__ == '__main__':`` block. - This is due to how Python launches parallel processes on Windows. If a - ``RuntimeError`` or ``AttributeError`` are raised by scripts that call - :func:`~qiskit.tools.parallel_map` (including using functions that use - ``parallel_map()`` internally like :func:`~qiskit.compiler.transpile`) - with Windows and parallelism enabled you can try embedding the script - calls inside ``if __name__ == '__main__':`` to workaround the issue. - For example:: - - from qiskit import QuantumCircuit, QiskitError - from qiskit import execute, Aer - - qc1 = QuantumCircuit(2, 2) - qc1.h(0) - qc1.cx(0, 1) - qc1.measure([0,1], [0,1]) - # making another circuit: superpositions - qc2 = QuantumCircuit(2, 2) - qc2.h([0,1]) - qc2.measure([0,1], [0,1]) - execute([qc1, qc2], Aer.get_backend('qasm_simulator')) - - should be changed to:: - - from qiskit import QuantumCircuit, QiskitError - from qiskit import execute, Aer - - def main(): - qc1 = QuantumCircuit(2, 2) - qc1.h(0) - qc1.cx(0, 1) - qc1.measure([0,1], [0,1]) - # making another circuit: superpositions - qc2 = QuantumCircuit(2, 2) - qc2.h([0,1]) - qc2.measure([0,1], [0,1]) - execute([qc1, qc2], Aer.get_backend('qasm_simulator')) - - if __name__ == '__main__': - main() - - if any errors are encountered with parallelism on Windows. - - -.. _Release Notes_0.17.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The preset pass managers - :class:`~qiskit.transpiler.preset_passmanagers.level_1_pass_manager`, - :class:`~qiskit.transpiler.preset_passmanagers.level_2_pass_manager`, - and :class:`~qiskit.transpiler.preset_passmanagers.level_3_pass_manager` - (which are used for ``optimization_level`` 1, 2, and 3 in the - :func:`~qiskit.compiler.transpile` and - :func:`~qiskit.execute_function.execute` functions) now unconditionally - use the :class:`~qiskit.transpiler.passes.Optimize1qGatesDecomposition` - pass for 1 qubit gate optimization. Previously, these pass managers would - use the :class:`~qiskit.transpiler.passes.Optimize1qGates` pass if the basis - gates contained ``u1``, ``u2``, or ``u3``. If you want to still use - the old :class:`~qiskit.transpiler.passes.Optimize1qGates` you will need - to construct a custom :class:`~qiskit.transpiler.PassManager` with the - pass. - -- Following transpilation of a parameterized - :class:`~qiskit.circuit.QuantumCircuit`, the - :attr:`~qiskit.circuit.QuantumCircuit.global_phase` attribute of output - circuit may no longer be returned in a simplified form, if the global phase - is a :class:`~qiskit.circuit.ParameterExpression`. - - For example:: - - qc = QuantumCircuit(1) - theta = Parameter('theta') - - qc.rz(theta, 0) - qc.rz(-theta, 0) - - print(transpile(qc, basis_gates=['p']).global_phase) - - previously returned ``0``, but will now return ``-0.5*theta + 0.5*theta``. - This change was necessary was to avoid a large runtime performance - penalty as simplifying symbolic expressions can be quite slow, especially - if there are many :class:`~qiskit.circuit.ParameterExpression` objects - in a circuit. - -- The :class:`~qiskit.providers.basicaer.BasicAerJob` job objects returned - from BasicAer backends are now synchronous instances of - :class:`~qiskit.providers.JobV1`. This means that calls to - the :meth:`~qiskit.providers.basicaer.QasmSimulatorPy.run` will block - until the simulation finishes executing. If you want to restore the - previous async behavior you'll need to wrap the - :meth:`~qiskit.providers.basicaer.QasmSimulatorPy.run` with something that - will run in a seperate thread or process like ``futures.ThreadPoolExecutor`` - or ``futures.ProcessPoolExecutor``. - -- The ``allow_sample_measuring`` option for the - BasicAer simulator :class:`~qiskit.providers.basicaer.QasmSimulatorPy` has - changed from a default of ``False`` to ``True``. This was done to better - reflect the actual default behavior of the simulator, which would use - sample measuring if the input circuit supported it (even if it was not - enabled). If you are running a circuit that doesn't support sample - measurement (ie it has :class:`~qiskit.circuit.Reset` operations or if - there are operations after a measurement on a qubit) you should make sure - to explicitly set this option to ``False`` when you call - :meth:`~qiskit.providers.basicaer.QasmSimulatorPy.run`. - -- The :class:`~qiskit.transpiler.passes.CommutativeCancellation` transpiler - pass is now aware of the target basis gates, which means it will only - use gates in the specified basis. Previously, the pass would unconditionally - replace consecutive gates which commute with - :class:`~qiskit.circuit.library.ZGate` with the - :class:`~qiskit.circuit.library.U1Gate`. However, now that the pass is - basis aware and has a kwarg, ``basis_gates``, for specifying the target - basis there is a potential change in behavior if the kwarg is not set. - When the ``basis_gates`` kwarg is not used and there are no variable - z-rotation gates in the circuit then no commutative cancellation will occur. - -- :class:`~qiskit.circuit.Register` (which is the parent class for - :class:`~qiskit.circuit.QuantumRegister` and - :class:`~qiskit.circuit.ClassicalRegister` and - :class:`~qiskit.circuit.Bit` (which is the parent class for - :class:`~qiskit.circuit.Qubit` and :class:`~qiskit.circuit.Clbit`) objects - are now immutable. In previous releases it was possible to adjust the value - of a :attr:`~qiskit.circuit.QuantumRegister.size` or - :attr:`~qiskit.circuit.QuantumRegister.name` attributes of a - :class:`~qiskit.circuit.Register` object and the - :attr:`~qiskit.circuit.Qubit.index` or - :attr:`~qiskit.circuit.Qubit.register` attributes of a - :class:`~qiskit.circuit.Bit` object after it was initially - created. However this would lead to unsound behavior that would corrupt - container structure that rely on a hash (such as a `dict`) since these - attributes are treated as immutable properties of a register or bit (see - `#4705 `__ for more - details). To avoid this unsound behavior this attributes of a - :class:`~qiskit.circuit.Register` and :class:`~qiskit.circuit.Bit` are - no longer settable after initial creation. If you were previously adjusting - the objects at runtime you will now need to create a new ``Register`` - or ``Bit`` object with the new values. - -- The ``DAGCircuit.__eq__`` method (which is used by the ``==`` operator), - which is used to check structural equality of - :class:`~qiskit.dagcircuit.DAGCircuit` and - :class:`~qiskit.circuit.QuantumCircuit` instances, will now - include the :attr:`~qiskit.circuit.QuantumCircuit.global_phase` and - :attr:`~qiskit.circuit.QuantumCircuit.calibrations` attributes in the - fields checked for equality. This means that circuits which would have - evaluated as equal in prior releases may not anymore if the - ``global_phase`` or ``calibrations`` differ between the circuits. For - example, in previous releases this would return ``True``:: - - import math - - from qiskit import QuantumCircuit - - qc1 = QuantumCircuit(1) - qc1.x(0) - - qc2 = QuantumCircuit(1, global_phase=math.pi) - qc2.x(0) - - print(qc2 == qc1) - - However, now because the ``global_phase`` attribute of the circuits differ - this will now return ``False``. - -- The previously deprecated ``qubits()`` and ``clbits()`` methods on the - :class:`~qiskit.dagcircuit.DAGCircuit` class, which were deprecated in the - 0.15.0 Terra release, have been removed. Instead you should use the - :attr:`~qiskit.dagcircuit.DAGCircuit.qubits` and - :attr:`~qiskit.dagcircuit.DAGCircuit.clbits` attributes of the - :class:`~qiskit.dagcircuit.DAGCircuit` class. For example, if you were - running:: - - from qiskit.dagcircuit import DAGCircuit - - dag = DAGCircuit() - qubits = dag.qubits() - - That would be replaced by:: - - from qiskit.dagcircuit import DAGCircuit - - dag = DAGCircuit() - qubits = dag.qubits - -- The :class:`~qiskit.providers.models.PulseDefaults` returned by the fake - pulse backends :py:class:`qiskit.test.mock.FakeOpenPulse2Q` and - :py:class:`qiskit.test.mock.FakeOpenPulse3Q` have been updated to have - more realistic pulse sequence definitions. If you are using these fake - backend classes you may need to update your usage because of these changes. - -- The default synthesis method used by - :func:`~qiskit.quantum_info.decompose_clifford` function in the - :mod:`~qiskit.quantum_info` module (which gets used internally by the - :meth:`qiskit.quantum_info.Clifford.to_circuit` method) for more than - 3 qubits now uses a non-optimal greedy compilation routine for Clifford - elements synthesis, by Bravyi et. al., which typically yields better CX - cost compared to the old default. If you need to revert to the previous - Aaronson-Gottesman method this can be done by setting ``method='AG'``. - -- The previously deprecated module ``qiskit.visualization.interactive``, - which was deprecated in the 0.15.0 release, has now been removed. Instead - you should use the matplotlib based visualizations: - - .. list-table:: - :header-rows: 1 - - * - Removed Interactive function - - Equivalent matplotlib function - * - ``iplot_bloch_multivector`` - - :func:`qiskit.visualization.plot_bloch_multivector` - * - ``iplot_state_city`` - - :func:`qiskit.visualization.plot_state_city` - * - ``iplot_state_qsphere`` - - :func:`qiskit.visualization.plot_state_qsphere` - * - ``iplot_state_hinton`` - - :func:`qiskit.visualization.plot_state_hinton` - * - ``iplot_histogram`` - - :func:`qiskit.visualization.plot_histogram` - * - ``iplot_state_paulivec`` - - :func:`qiskit.visualization.plot_state_paulivec` - -- The ``qiskit.Aer`` and ``qiskit.IBMQ`` top level attributes are now lazy - loaded. This means that the objects will now always exist and warnings will - no longer be raised on import if ``qiskit-aer`` or ``qiskit-ibmq-provider`` - are not installed (or can't be found by Python). If you were checking for - the presence of ``qiskit-aer`` or ``qiskit-ibmq-provider`` using these - module attributes and explicitly comparing to ``None`` or looking for the - absence of the attribute this no longer will work because they are always - defined as an object now. In other words running something like:: - - try: - from qiskit import Aer - except ImportError: - print("Aer not available") - - or:: - - try: - from qiskit import IBMQ - except ImportError: - print("IBMQ not available") - - will no longer work. Instead to determine if those providers are present - you can either explicitly use ``qiskit.providers.aer.Aer`` and - ``qiskit.providers.ibmq.IBMQ``:: - - try: - from qiskit.providers.aer import Aer - except ImportError: - print("Aer not available") - - try: - from qiskit.providers.ibmq import IBMQ - except ImportError: - print("IBMQ not available") - - or check ``bool(qiskit.Aer)`` and ``bool(qiskit.IBMQ)`` instead, for - example:: - - import qiskit - - if not qiskit.Aer: - print("Aer not available") - if not qiskit.IBMQ: - print("IBMQ not available") - - This change was necessary to avoid potential import cycle issues between - the qiskit packages and also to improve the import time when Aer or IBMQ - are not being used. - -- The user config file option ``suppress_packaging_warnings`` option in the - user config file and the ``QISKIT_SUPPRESS_PACKAGING_WARNINGS`` environment - variable no longer has any effect and will be silently ignored. The warnings - this option controlled have been removed and will no longer be emitted at - import time from the ``qiskit`` module. - -- The previously deprecated ``condition`` kwarg for - :class:`qiskit.dagcircuit.DAGNode` constructor has been removed. - It was deprecated in the 0.15.0 release. Instead you should now be setting - the classical condition on the :class:`~qiskit.circuit.Instruction` object - passed into the :class:`~qiskit.dagcircuit.DAGNode` constructor when - creating a new ``op`` node. - -- When creating a new :class:`~qiskit.circuit.Register` (which is the parent - class for :class:`~qiskit.circuit.QuantumRegister` and - :class:`~qiskit.circuit.ClassicalRegister`) or - :class:`~qiskit.circuit.QuantumCircuit` object with a number of bits (eg - ``QuantumCircuit(2)``), it is now required that number of bits are - specified as an integer or another type which is castable to unambiguous - integers(e.g. ``2.0``). Non-integer values will now raise an error as the - intent in those cases was unclear (you can't have fractional bits). For - more information on why this was changed refer to: - `#4855 `__ - -- `networkx `__ is no longer a requirement for - qiskit-terra. All the networkx usage inside qiskit-terra has been removed - with the exception of 3 methods: - - * :class:`qiskit.dagcircuit.DAGCircuit.to_networkx` - * :class:`qiskit.dagcircuit.DAGCircuit.from_networkx` - * :class:`qiskit.dagcircuit.DAGDependency.to_networkx` - - If you are using any of these methods you will need to manually install - networkx in your environment to continue using them. - -- By default on macOS with Python >=3.8 :func:`~qiskit.tools.parallel_map` - will no longer run in multiple processes. This is a change from previous - releases where the default behavior was that - :func:`~qiskit.tools.parallel_map` would launch multiple processes. This - change was made because with newer versions of macOS with Python 3.8 and - 3.9 multiprocessing is either unreliable or adds significant overhead - because of the change in Python 3.8 to launch new processes with ``spawn`` - instead of ``fork``. To re-enable parallel execution on macOS with - Python >= 3.8 you can use the user config file ``parallel`` option or set - the environment variable ``QISKIT_PARALLEL`` to ``True``. - -- The previously deprecated kwarg ``callback`` on the constructor for the - :class:`~qiskit.transpiler.PassManager` class has been removed. This - kwarg has been deprecated since the 0.13.0 release (April, 9th 2020). - Instead you can pass the ``callback`` kwarg to the - :meth:`qiskit.transpiler.PassManager.run` method directly. For example, - if you were using:: - - from qiskit.circuit.random import random_circuit - from qiskit.transpiler import PassManager - - qc = random_circuit(2, 2) - - def callback(**kwargs) - print(kwargs['pass_']) - - pm = PassManager(callback=callback) - pm.run(qc) - - this can be replaced with:: - - from qiskit.circuit.random import random_circuit - from qiskit.transpiler import PassManager - - qc = random_circuit(2, 2) - - def callback(**kwargs) - print(kwargs['pass_']) - - pm = PassManager() - pm.run(qc, callback=callback) - -- It is now no longer possible to instantiate a base channel without - a prefix, such as :class:`qiskit.pulse.Channel` or - :class:`qiskit.pulse.PulseChannel`. These classes are designed to - classify types of different user facing channel classes, such - as :class:`qiskit.pulse.DriveChannel`, but do not have a definition as - a target resource. If you were previously directly instantiating either - :class:`qiskit.pulse.Channel` or - :class:`qiskit.pulse.PulseChannel`, this is no longer allowed. Please use - the appropriate subclass. - -- When the ``require_cp`` and/or ``require_tp`` kwargs of - :func:`qiskit.quantum_info.process_fidelity`, - :func:`qiskit.quantum_info.average_gate_fidelity`, - :func:`qiskit.quantum_info.gate_error` are ``True``, they will now only log a - warning rather than the previous behavior of raising a - :class:`~qiskit.exceptions.QiskitError` exception if the input channel is - non-CP or non-TP respectively. - -- The :class:`~qiskit.circuit.library.QFT` class in the - :mod:`qiskit.circuit.library` module now computes the Fourier transform - using a little-endian representation of tensors, i.e. the state - :math:`|1\rangle` maps to :math:`|0\rangle - |1\rangle + |2\rangle - ..` - assuming the computational basis correspond to little-endian bit ordering - of the integers. :math:`|0\rangle = |000\rangle, |1\rangle = |001\rangle`, - etc. This was done to make it more consistent with the rest of Qiskit, - which uses a little-endian convention for bit order. If you were depending - on the previous bit order you can use the - :meth:`~qiskit.circuit.library.QFT.reverse_bits` method to revert to the - previous behavior. For example:: - - from qiskit.circuit.library import QFT - - qft = QFT(5).reverse_bits() - -- The ``qiskit.__qiskit_version__`` module attribute was previously a ``dict`` - will now return a custom read-only ``Mapping`` object that checks the - version of qiskit elements at runtime instead of at import time. This was - done to speed up the import path of qiskit and eliminate a possible import - cycle by only importing the element packages at runtime if the version - is needed from the package. This should be fully compatible with the - ``dict`` previously return and for most normal use cases there will be no - difference. However, if some applications were relying on either mutating - the contents or explicitly type checking it may require updates to adapt to - this change. - -- The ``qiskit.execute`` module has been renamed to - :mod:`qiskit.execute_function`. This was necessary to avoid a potentical - name conflict between the :func:`~qiskit.execute_function.execute` function - which is re-exported as ``qiskit.execute``. ``qiskit.execute`` the function - in some situations could conflict with ``qiskit.execute`` the module which - would lead to a cryptic error because Python was treating ``qiskit.execute`` - as the module when the intent was to the function or vice versa. The module - rename was necessary to avoid this conflict. If you're importing - ``qiskit.execute`` to get the module (typical usage was - ``from qiskit.execute import execute``) you will need to update this to - use ``qiskit.execute_function`` instead. ``qiskit.execute`` will now always - resolve to the function. - -- The ``qiskit.compiler.transpile``, ``qiskit.compiler.assemble``, - ``qiskit.compiler.schedule``, and ``qiskit.compiler.sequence`` modules have - been renamed to ``qiskit.compiler.transpiler``, - ``qiskit.compiler.assembler``, ``qiskit.compiler.scheduler``, and - ``qiskit.compiler.sequence`` respectively. This was necessary to avoid a - potentical name conflict between the modules and the re-exported function - paths :func:`qiskit.compiler.transpile`, :func:`qiskit.compiler.assemble`, - :func:`qiskit.compiler.schedule`, and :func:`qiskit.compiler.sequence`. - In some situations this name conflict between the module path and - re-exported function path would lead to a cryptic error because Python was - treating an import as the module when the intent was to use the function or - vice versa. The module rename was necessary to avoid this conflict. If - you were using the imports to get the modules before (typical usage would - be like``from qiskit.compiler.transpile import transpile``) you will need - to update this to use the new module paths. - :func:`qiskit.compiler.transpile`, :func:`qiskit.compiler.assemble`, - :func:`qiskit.compiler.schedule`, and :func:`qiskit.compiler.sequence` - will now always resolve to the functions. - -- The :class:`qiskit.quantum_info.Quaternion` class was moved from the - ``qiskit.quantum_info.operator`` submodule to the - ``qiskit.quantum_info.synthesis`` submodule to better reflect it's purpose. - No change is required if you were importing it from the root - :mod:`qiskit.quantum_info` module, but if you were importing from - ``qiskit.quantum_info.operator`` you will need to update your import path. - -- Removed the ``QuantumCircuit.mcmt`` method, which has been - deprecated since the Qiskit Terra 0.14.0 release in April 2020. - Instead of using the method, please use the - :class:`~qiskit.circuit.library.MCMT` class instead to construct - a multi-control multi-target gate and use the - :meth:`qiskit.circuit.QuantumCircuit.append` or - :meth:`qiskit.circuit.QuantumCircuit.compose` to add it to a circuit. - - For example, you can replace:: - - circuit.mcmt(ZGate(), [0, 1, 2], [3, 4]) - - with:: - - from qiskit.circuit.library import MCMT - mcmt = MCMT(ZGate(), 3, 2) - circuit.compose(mcmt, range(5)) - -- Removed the ``QuantumCircuit.diag_gate`` method which has been deprecated since the - Qiskit Terra 0.14.0 release in April 2020. Instead, use the - :meth:`~qiskit.circuit.QuantumCircuit.diagonal` method of :class:`~qiskit.circuit.QuantumCircuit`. - -- Removed the ``QuantumCircuit.ucy`` method which has been deprecated since the - Qiskit Terra 0.14.0 release in April 2020. Instead, use the - :meth:`~qiskit.circuit.QuantumCircuit.ucry` method of :class:`~qiskit.circuit.QuantumCircuit`. - -- The previously deprecated ``mirror()`` method for - :class:`qiskit.circuit.QuantumCircuit` has been removed. It was deprecated - in the 0.15.0 release. The :meth:`qiskit.circuit.QuantumCircuit.reverse_ops` - method should be used instead since mirroring could be confused with - swapping the output qubits of the circuit. The ``reverse_ops()`` method - only reverses the order of gates that are applied instead of mirroring. - -- The previously deprecated support passing a float (for the ``scale`` kwarg - as the first positional argument to the - :meth:`qiskit.circuit.QuantumCircuit.draw` has been removed. It was - deprecated in the 0.12.0 release. The first positional argument to the - :meth:`qiskit.circuit.QuantumCircuit.draw` method is now the ``output`` - kwarg which does not accept a float. Instead you should be using ``scale`` - as a named kwarg instead of using it positionally. - - For example, if you were previously calling ``draw`` with:: - - from qiskit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.draw(0.75, output='mpl') - - this would now need to be:: - - from qiskit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.draw(output='mpl', scale=0.75) - - or:: - - qc.draw('mpl', scale=0.75) - -- Features of Qiskit Pulse (:mod:`qiskit.pulse`) which were deprecated - in the 0.15.0 release (August, 2020) have been removed. The full set - of changes are: - - .. list-table:: - :header-rows: 1 - - * - Module - - Old - - New - * - ``qiskit.pulse.library`` - - ``SamplePulse`` - - :class:`~qiskit.pulse.library.Waveform` - * - ``qiskit.pulse.library`` - - ``ConstantPulse`` - - :class:`~qiskit.pulse.library.Constant` - * - (module rename) - - ``pulse.pulse_lib`` Module - - :mod:`qiskit.pulse.library` - - .. list-table:: - :header-rows: 1 - - * - Class - - Old method - - New method - * - :class:`~qiskit.pulse.library.ParametricPulse` - - ``get_sample_pulse`` - - :class:`~qiskit.pulse.library.ParametricPulse.get_waveform` - * - :class:`~qiskit.pulse.instructions.Instruction` - - ``command`` - - N/A. Commands and Instructions have been unified. - Use :meth:`~qiskit.pulse.instructions.Instruction.operands` - to get information about the instruction data. - * - :class:`~qiskit.pulse.instructions.Acquire` - - ``acquires``, ``mem_slots``, ``reg_slots`` - - :meth:`~qiskit.pulse.instructions.Acquire.acquire`, - :meth:`~qiskit.pulse.instructions.Acquire.mem_slot`, - :meth:`~qiskit.pulse.instructions.Acquire.reg_slot`. (The - :class:`~qiskit.pulse.instructions.Acquire` instruction no - longer broadcasts across multiple qubits.) - -- The dictionary previously held on :class:`~qiskit.dagcircuit.DAGCircuit` - edges has been removed. Instead, edges now hold the - :class:`~qiskit.circuit.Bit` instance which had previously been included in - the dictionary as its ``'wire'`` field. Note that the NetworkX graph - returned by :meth:`~qiskit.dagcircuit.DAGCircuit.to_networkx` will still - have a dictionary for its edge attributes, but the ``'name'`` field will no - longer be populated. - -- The :attr:`~qiskit.circuit.QuantumCircuit.parameters` attribute of the - :class:`~qiskit.circuit.QuantumCircuit` class no longer is returning a - ``set``. Instead it returns a ``ParameterView`` object which implements - all the methods that ``set`` offers (albeit deprecated). This was done - to support a model that preserves name-sorted parameters. It - should be fully compatible with any previous usage of the ``set`` returned - by the :attr:`~qiskit.circuit.QuantumCircuit.parameters` attribute, except - for where explicit type checking of a set was done. - -- When running :func:`~qiskit.compiler.transpile` on a - :class:`~qiskit.circuit.QuantumCircuit` with - :meth:`~qiskit.circuit.QuantumCircuit.delay` instructions, the units will - be converted to dt if the value of dt (sample time) is known to - :func:`~qiskit.compiler.transpile`, either explicitly via the ``dt`` - kwarg or via the :class:`~qiskit.providers.models.BackendConfiguration` for - a ``Backend`` object passed in via the ``backend`` kwarg. - -- The interpretation of ``meas_map`` (which - is an attribute of a - :class:`~qiskit.providers.models.PulseBackendConfiguration` object or - as the corresponding ``meas_map`` kwarg on the - :func:`~qiskit.compiler.schedule`, :func:`~qiskit.compiler.assemble`, - :func:`~qiskit.compiler.sequence`, or - :func:`~qiskit.execute_function.execute` functions) has been updated - to better match the true constraints of the hardware. The format of this - data is a list of lists, where the items in the inner list are integers - specifying qubit labels. For instance:: - - [[A, B, C], [D, E, F, G]] - - Previously, the ``meas_map`` constraint was interpreted such that - if one qubit was acquired (e.g. A), then all other qubits sharing - a subgroup with that qubit (B and C) would have to be acquired - at the same time and for the same duration. This constraint has been - relaxed. One acquisition does not require more acquisitions. (If A is - acquired, B and C do **not** need to be acquired.) Instead, qubits in the - same measurement group cannot be acquired in a partially overlapping way - -- think of the ``meas_map`` as specifying a shared acquisition resource - (If we acquire A from ``t=1000`` to ``t=2000``, we cannot acquire B - starting from ``1000`__ - repository and those should be treated as the canonical versions of the - API schemas. Moving forward only those schemas will recieve updates and - will be used as the source of truth for the schemas. If you were relying - on the schemas bundled in qiskit-terra you should update to - use that repository instead. - -- The :mod:`qiskit.util` module has been deprecated and will be removed - in a future release. It has been replaced by :mod:`qiskit.utils` which - provides the same functionality and will be expanded in the future. Note - that no ``DeprecationWarning`` will be emitted regarding this deprecation - since it was not feasible on Python 3.6. - -- The :class:`~qiskit.transpiler.passes.CXDirection` transpiler pass in the - :mod:`qiskit.transpiler.passes` module has been deprecated and will be - removed in a future release. Instead the - :class:`~qiskit.transpiler.GateDirection` should be used. It behaves - identically to the :class:`~qiskit.transpiler.passes.CXDirection` except - that it now also supports transforming a circuit with - :class:`~qiskit.circuit.library.ECRGate` gates in addition to - :class:`~qiskit.circuit.library.CXGate` gates. - -- The :class:`~qiskit.transpiler.passes.CheckCXDirection` transpiler pass in - the :mod:`qiskit.transpiler.passes` module has been deprecated and will be - removed in a future release. Instead the - :class:`~qiskit.transpiler.CheckGateDirection` pass should be used. - It behaves identically to the - :class:`~qiskit.transpiler.passes.CheckCXDirection` except - that it now also supports checking the direction of all 2-qubit gates, not - just :class:`~qiskit.circuit.library.CXGate` gates. - -- The :class:`~qiskit.circuit.library.WeightedAdder` method - :meth:`~qiskit.circuit.library.WeightedAdder.num_ancilla_qubits` is - deprecated and will be removed in a future release. It has been replaced - with the :attr:`qiskit.circuit.library.WeightedAdder.num_ancillas` attribute - which is consistent with other circuit libraries' APIs. - -- The following legacy methods of the :class:`qiskit.quantum_info.Pauli` class - have been deprecated. See the method documentation for replacement use in - the updated Pauli class. - - * :meth:`~qiskit.quantum_info.Pauli.from_label` - * :meth:`~qiskit.quantum_info.Pauli.sgn_prod` - * :meth:`~qiskit.quantum_info.Pauli.to_spmatrix` - * :meth:`~qiskit.quantum_info.Pauli.kron` - * :meth:`~qiskit.quantum_info.Pauli.update_z` - * :meth:`~qiskit.quantum_info.Pauli.update_x` - * :meth:`~qiskit.quantum_info.Pauli.insert_paulis` - * :meth:`~qiskit.quantum_info.Pauli.append_paulis` - * :meth:`~qiskit.quantum_info.Pauli.delete_qubits` - * :meth:`~qiskit.quantum_info.Pauli.pauli_single` - * :meth:`~qiskit.quantum_info.Pauli.random` - -- Using a ``list`` or ``numpy.ndarray`` as the ``channel`` or ``target`` - argument for the :func:`qiskit.quantum_info.process_fidelity`, - :func:`qiskit.quantum_info.average_gate_fidelity`, - :func:`qiskit.quantum_info.gate_error`, and - :func:`qiskit.quantum_info.diamond_norm` functions has been - deprecated and will not be supported in a future release. The inputs should - instead be a :class:`~qiskit.circuit.Gate` or a ``BaseOperator`` subclass - object (eg. :class:`~qiskit.quantum_info.Operator`, - :class:`~qiskit.quantum_info.Choi`, etc.) - -- Accessing references from :class:`~qiskit.circuit.Qubit` and - :class:`~qiskit.circuit.Clbit` instances to their containing registers - via the :attr:`~qiskit.circuit.Qubit.register` or - :attr:`~qiskit.circuit.Qubit.index` properties has been deprecated and will - be removed in a future release. Instead, :class:`~qiskit.circuit.Register` - objects can be queried to find the :class:`~qiskit.circuit.Bit` objects - they contain. - -- The current functionality of the :func:`qiskit.visualization.pulse_drawer` - function is deprecated and will be replaced by - :func:`qiskit.visualization.pulse_drawer_v2` (which is not backwards - compatible) in a future release. - -- The use of methods inherited from the ``set`` type on the output of the - :attr:`~qiskit.circuit.QuantumCircuit.parameters` attribute (which used to - be a ``set``) of the :class:`~qiskit.circuit.QuantumCircuit` class are - deprecated and will be removed in a future release. This includes the - methods from the ``add()``, ``difference()``, ``difference_update()``, - ``discard()``, ``intersection()``, ``intersection_update()``, - ``issubset()``, ``issuperset()``, ``symmetric_difference()``, - ``symmetric_difference_update()``, ``union()``, ``update()``, - ``__isub__()`` (which is the ``-=`` operator), and ``__ixor__()`` (which is - the ``^=`` operator). - -- The name of the first (and only) positional argument for the - :meth:`qiskit.circuit.QuantumCircuit.bind_parameters` method has changed - from ``value_dict`` to ``values``. The passing an argument in with the - name ``values_dict`` is deprecated and will be removed in future release. - For example, if you were previously calling - :meth:`~qiskit.circuit.QuantumCircuit.bind_parameters` with a call like: - ``bind_parameters(values_dict={})`` this is deprecated and should be - replaced by ``bind_parameters(values={})`` or even better just pass the - argument positionally ``bind_parameters({})``. - -- The name of the first (and only) positional argument for the - :meth:`qiskit.circuit.QuantumCircuit.assign_parameters` method has changed - from ``param_dict`` to ``parameters``. Passing an argument in with the name - ``param_dict`` is deprecated and will be removed in future release. For - example, if you were previously calling - :meth:`~qiskit.circuit.QuantumCircuit.assign_parameters` with a call like: - ``assign_parameters(param_dict={})`` this is deprecated and should be - replaced by ``assign_parameters(values={})`` or even better just pass the - argument positionally ``assign_parameters({})``. - - -.. _Release Notes_0.17.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue where the :func:`~qiskit.execute_function.execute` function - would raise :class:`~qiskit.exceptions.QiskitError` exception when a - :class:`~qiskit.circuit.ParameterVector` object was passed in for the - ``parameter_bind`` kwarg. parameter. For example, it is now possible to - call something like:: - - execute(circuit, backend, parameter_binds=[{pv1: [...], pv2: [...]}]) - - where ``pv1`` and ``pv2`` are :class:`~qiskit.circuit.ParameterVector` - objects. - Fixed `#5467 `__ - -- Fixed an issue with the labels of parametric pulses in the - :class:`~qiskit.qobj.PulseQobjInstruction` class were not being properly - set as they are with sampled pulses. This also means that pulse names - that are imported from the :class:`~qiskit.providers.models.PulseDefaults` - returned by a :class:`~qiskit.providers.Backend`, such as ``x90``, ``x90m``, - etc, will properly be set. - Fixed `#5363 `__ - -- Fixed an issue where unbound parameters only occurring in - the :attr:`~qiskit.circuit.QuantumCircuit.global_phase` attribute of - a :class:`~qiskit.circuit.QuantumCircuit` object would not - show in the :attr:`~qiskit.circuit.QuantumCircuit.parameters` attribute - and could not be bound. - Fixed `#5806 `__ - -- The :attr:`~qiskit.circuit.QuantumCircuit.calibrations` attribute - of :class:`~qiskit.circuit.QuantumCircuit` objects are now preserved when - the ``+=`` (ie the :meth:`~qiskit.circuit.QuantumCircuit.extend` - method) and the ``+`` (ie the :meth:`~qiskit.circuit.QuantumCircuit.combine` - method) are used. - Fixed `#5930 `__ and - `#5908 `__ - -- The :attr:`~qiskit.circuit.Register.name` setter method of class - :class:`~qiskit.circuit.Register` (which is the parent class of - :class:`~qiskit.circuit.QuantumRegister` and - :class:`~qiskit.circuit.ClassicalRegister`) previously did not check if - the assigned string was a valid register name as per the - `OpenQASM specification `__. - This check was previously only performed when the name was specified in the - constructor, this has now been fixed so that setting the ``name`` - attribute directly with an invalid value will now also raise an - exception. - Fixed `#5461 `__ - -- Fixed an issue with the :func:`qiskit.visualization.circuit_drawer` function - and :meth:`qiskit.circuit.QuantumCircuit.draw` method when visualizing a - :class:`~qiskit.circuit.QuantumCircuit` with a - :class:`~qiskit.circuit.Gate` that has a classical condition - after a :class:`~qiskit.circuit.Measure` that used the same - :class:`~qiskit.circuit.ClassicalRegister`, it was possible - for the conditional :class:`~qiskit.circuit.Gate` to be displayed to the - left of the :class:`~qiskit.circuit.Measure`. - Fixed `#5387 `__ - -- In the transpiler pass :class:`qiskit.transpiler.passes.CSPLayout` a bias - towards lower numbered qubits could be observed. This undesireable bias has - been fixed by shuffling the candidates to randomize the results. - Furthermore, the usage of the :class:`~qiskit.transpiler.passes.CSPLayout` - pass in the :mod:`~qiskit.transpiler.preset_passmanagers` (for level 2 and - 3) has been adjusted to use a configured seed if the ``seed_transpiler`` - kwarg is set when :func:`~qiskit.compiler.transpile` is called. - Fixed `#5990 `__ - -- Fixes a bug where the ``channels`` field for a - :class:`~qiskit.providers.models.PulseBackendConfiguration` object was - not being included in the output of the - :class:`qiskit.providers.models.PulseBackendConfiguration.to_dict` method. - Fixed `#5579 `__ - -- Fixed the ``'circular'`` entanglement in the - :class:`qiskit.circuit.library.NLocal` circuit class for the edge - case where the circuit has the same size as the entanglement block (e.g. a two-qubit - circuit and CZ entanglement gates). In this case there should only be one entanglement - gate, but there was accidentially added a second one in the inverse direction as the - first. - Fixed `Qiskit/qiskit-aqua#1452 `__ - -- Fixed the handling of breakpoints in the - :class:`~qiskit.circuit.library.PiecewisePolynomialPauliRotations` class - in the :mod:`qiskit.circuit.library`. Now for ``n`` intervals, - ``n+1`` breakpoints are allowed. This enables specifying another end - interval other than :math:`2^\text{num qubits}`. This is important because - from the end of the last interval to :math:`2^\text{num qubits}` the function - is the identity. - -- Fixed an issue in the :class:`qiskit.circuit.library.Permutation` circuit - class where some permutations would not be properly generated. This issue - could also effect :class:`qiskit.circuit.library.QuantumVolume` if it were - called with `classical_permutation=False``. - Fixed `#5812 `__ - -- Fixed an issue where generating QASM output with the - :meth:`~qiskit.circuit.QuantumCircuit.qasm` method for a - :class:`~qiskit.circuit.QuantumCircuit` object that has a - :class:`~qiskit.circuit.ControlledGate` with an open control the output - would be as if all controls were closed independent of the specified - control state. This would result in a different circuit being created - from :meth:`~qiskit.circuit.QuantumCircuit.from_qasm_str` if - parsing the generated QASM. - - This was fixed by updating the QASM output from - :meth:`~qiskit.circuit.QuantumCircuit.qasm` by defining a composite gate - which uses :class:`~qiskit.circuit.XGate` to implement the open controls. - The composite gate is named like ``_o`` - where ``o`` stands for open control and ``ctrl_state`` is the integer value - of the control state. - Fixed `#5443 `__ - -- Fixed an issue where binding :class:`~qiskit.circuit.Parameter` objects - in a :class:`~qiskit.circuit.QuantumCircuit` with the ``parameter_binds`` - in the :class:`~qiskit.execute_function.execute` function would cause all - the bound :class:`~qiskit.circuit.QuantumCircuit` objects would have the - same :attr:`~qiskit.circuit.QuantumCircuit.name`, which meant the - result names were also not unique. This fix causes - the :meth:`~qiskit.circuit.QuantumCircuit.bind_parameters` and - :meth:`~qiskit.circuit.QuantumCircuit.assign_parameters` to assign a unique - circuit name when ``inplace=False`` as:: - - -[-] - - where ```` is the name supplied by the "name" kwarg, - otherwise it defaults to "circuit". The class instance number gets - incremented every time an instance of the class is generated. ```` - is appended if called outside the main process. - Fixed `#5185 `__ - -- Fixed an issue with the :func:`~qiskit.compiler.scheduler` function where - it would raise an exception if an input circuit contained an unbound - :class:`~qiskit.circuit.QuantumCircuit` object. - Fixed `#5304 `__ - -- Fixed an issue in the :class:`qiskit.transpiler.passes.TemplateOptimization` - transpiler passes where template circuits that contained unbound - :class:`~qiskit.circuit.Parameter` objects would crash under some scenarios - if the parameters could not be bound during the template matching. - Now, if the :class:`~qiskit.circuit.Parameter` objects can not be bound - templates with unbound :class:`~qiskit.circuit.Parameter` are discarded and - ignored by the :class:`~qiskit.transpiler.passes.TemplateOptimization` pass. - Fixed `#5533 `__ - -- Fixed an issue with the :func:`qiskit.visualization.timeline_drawer` - function where classical bits were inproperly handled. - Fixed `#5361 `__ - -- Fixed an issue in the :func:`qiskit.visualization.circuit_drawer` function - and the :meth:`qiskit.circuit.QuantumCircuit.draw` method where - :class:`~qiskit.circuit.Delay` instructions in a - :class:`~qiskit.circuit.QuantumCircuit` object were not being correctly - treated as idle time. So when the ``idle_wires`` kwarg was set to - ``False`` the wires with the :class:`~qiskit.circuit.Delay` objects would - still be shown. This has been fixed so that the idle wires are removed from - the visualization if there are only :class:`~qiskit.circuit.Delay` objects - on a wire. - -- Previously, when the option ``layout_method`` kwarg was provided to - the :func:`~qiskit.compiler.transpile` function and the - ``optimization_level`` kwarg was set to >= 2 so that the pass - :class:`qiskit.transpiler.passes.CSPLayout` would run, if - :class:`~qiskit.transpiler.passes.CSPLayout` found a solution then - the method in ``layout_method`` was not executed. This has been fixed so - that if specified, the ``layout_method`` is always honored. - Fixed `#5409 `__ - -- When the argument ``coupling_map=None`` (either set explicitly, set - implicitly as the default value, or via the ``backend`` kwarg), the - transpiling process was not "embedding" the circuit. That is, even when an - ``initial_layout`` was specified, the virtual qubits were not assigned to - physical qubits. This has been fixed so that now, the - :func:`qiskit.compiler.transpile` function honors the ``initial_layout`` - argument by embedding the circuit: - - .. code-block:: python - - from qiskit import QuantumCircuit, QuantumRegister - from qiskit.compiler import transpile - - qr = QuantumRegister(2, name='qr') - circ = QuantumCircuit(qr) - circ.h(qr[0]) - circ.cx(qr[0], qr[1]) - - transpile(circ, initial_layout=[1, 0]).draw(output='mpl') - - - If the ``initial_layout`` refers to more qubits than in the circuit, the - transpiling process will extended the circuit with ancillas. - - .. code-block:: python - - from qiskit import QuantumCircuit, QuantumRegister - from qiskit.compiler import transpile - - qr = QuantumRegister(2, name='qr') - circ = QuantumCircuit(qr) - circ.h(qr[0]) - circ.cx(qr[0], qr[1]) - - transpile(circ, initial_layout=[4, 2], coupling_map=None).draw() - - Fixed `#5345 `__ - -- A new kwarg, ``user_cost_dict`` has been added to the constructor for the - :class:`qiskit.transpiler.passes.TemplateOptimization` transpiler pass. - This enables users to provide a custom cost dictionary for the gates to - the underlying template matching algorithm. For example:: - - from qiskit.transpiler.passes import TemplateOptimization - - cost_dict = {'id': 0, 'x': 1, 'y': 1, 'z': 1, 'h': 1, 't': 1} - pass = TemplateOptimization(user_cost_dict=cost_dict) - -- An issue when passing the :class:`~qiskit.result.Counts` object - returned by :meth:`~qiskit.result.Result.get_counts` to - :func:`~qiskit.result.marginal_counts` would produce an improperly - formatted :class:`~qiskit.result.Counts` object with certain inputs has - been fixed. Fixes - `#5424 `__ - -- Improved the allocation of helper qubits in - :class:`~qiskit.circuit.library.PolynomialPauliRotations` and - :class:`~qiskit.circuit.library.PiecewiseLinearPauliRotations` which makes - the implementation of these circuit more efficient. - Fixed `#5320 `__ and - `#5322 `__ - -- Fix the usage of the allocated helper qubits in the - :class:`~qiskit.circuit.library.MCXGate` in the - :class:`~qiskit.circuit.library.WeightedAdder` class. These were previously - allocated but not used prior to this fix. - Fixed `#5321 `__ - -- In a number of cases, the ``latex`` output method for the - :func:`qiskit.visualization.circuit_drawer` function and the - :meth:`~qiskit.circuit.QuantumCircuit.draw` method did not display the - gate name correctly, and in other cases, did not include gate parameters - where they should be. Now the gate names will be displayed the same way - as they are displayed with the ``mpl`` output method, and parameters will - display for all the gates that have them. In addition, some of the gates - did not display in the correct form, and these have been fixed. Fixes - `#5605 `__, - `#4938 `__, and - `#3765 `__ - -- Fixed an issue where, if the - :meth:`qiskit.circuit.Instruction.to_instruction` method was used on a subcircuit which - contained classical registers and that - :class:`~qiskit.circuit.Instruction` object was then added to a - :class:`~qiskit.circuit.QuantumCircuit` object, then the output from the - :func:`qiskit.visualization.circuit_drawer` function and the - :meth:`qiskit.circuit.QuantumCircuit.draw` method would in some instances - display the subcircuit to the left of a measure when it should have been - displayed to the right. - Fixed `#5947 `__ - -- Fixed an issue with :class:`~qiskit.circuit.Delay` objects in a - :class:`~qiskit.circuit.QuantumCircuit` where - :func:`qiskit.compiler.transpile` would not be convert the units of - the :class:`~qiskit.circuit.Delay` to the units of the - :class:`~qiskit.providers.Backend`, if the ``backend`` kwarg is set on - :func:`~qiskit.circuit.transpile`. This could result in the wrong behavior - because of a unit mismatch, for example running:: - - from qiskit import transpile, execute - from qiskit.circuit import QuantumCircuit - - qc = QuantumCircuit(1) - qc.delay(100, [0], unit='us') - - qc = transpile(qc, backend) - job = execute(qc, backend) - - would previously have resulted in the backend delay for 100 timesteps (each - of duration dt) rather than expected (100e-6 / dt) timesteps. This has been - corrected so the :func:`qiskit.compiler.transpile` function properly - converts the units. - - -.. _Release Notes_0.17.0_Other Notes: - -Other Notes ------------ - -- The snapshots of all the fake/mock backends in ``qiskit.test.mock`` have - been updated to reflect recent device changes. This includes a change in - the :attr:`~qiskit.providers.models.QasmBackendConfiguration.basis_gates` - attribute for the :class:`~qiskit.providers.models.BackendConfiguration` - to ``['cx', 'rz', 'sx', 'x', 'id']``, the addition of a ``readout_length`` - property to the qubit properties in the - :class:`~qiskit.providers.models.BackendProperties`, and updating the - :class:`~qiskit.providers.models.PulseDefaults` so that all the mock - backends support parametric pulse based - :class:`~qiskit.pulse.InstructionScheduleMap` instances. - -.. _Aer_Release Notes_0.8.0: - -Aer 0.8.0 -============ - -.. _Aer_Release Notes_0.8.0_Prelude: - -Prelude -------- - -The 0.8 release includes several new features and bug fixes. The -highlights for this release are: the introduction of a unified -:class:`~qiskit.providers.aer.AerSimulator` backend for running circuit -simulations using any of the supported simulation methods; a simulator -instruction library (:mod:`qiskit.providers.aer.library`) -which includes custom instructions for saving various kinds of simulator -data; MPI support for running large simulations on a distributed -computing environment. - - -.. _Aer_Release Notes_0.8.0_New Features: - -New Features ------------- - -- Python 3.9 support has been added in this release. You can now run Qiskit - Aer using Python 3.9 without building from source. - -- Add the CMake flag ``DISABLE_CONAN`` (default=``OFF``)s. When installing from source, - setting this to ``ON`` allows bypassing the Conan package manager to find libraries - that are already installed on your system. This is also available as an environment - variable ``DISABLE_CONAN``, which takes precedence over the CMake flag. - This is not the official procedure to build AER. Thus, the user is responsible - of providing all needed libraries and corresponding files to make them findable to CMake. - -- This release includes support for building qiskit-aer with MPI support to - run large simulations on a distributed computing environment. See the - `contributing guide `__ - for instructions on building and running in an MPI environment. - -- It is now possible to build qiskit-aer with CUDA enabled in Windows. - See the - `contributing guide `__ - for instructions on building from source with GPU support. - -- When building the qiskit-aer Python extension from source several build - dependencies need to be pre-installed to enable C++ compilation. As a - user convenience when building the extension any of these build - dependencies which were missing would be automatically installed using - ``pip`` prior to the normal ``setuptools`` installation steps, however it was - previously was not possible to avoid this automatic installation. To solve - this issue a new environment variable ``DISABLE_DEPENDENCY_INSTALL`` - has been added. If it is set to ``1`` or ``ON`` when building the python - extension from source this will disable the automatic installation of these - missing build dependencies. - -- Adds support for optimized N-qubit Pauli gate ( - :class:`qiskit.circuit.library.PauliGate`) to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - statevector and density matrix methods of the - :class:`~qiskit.providers.aer.QasmSimulator` and - :class:`~qiskit.providers.aer.AerSimulator`. - -- The :meth:`~qiskit.providers.aer.AerSimulator.run` method for the - :class:`~qiskit.providers.aer.AerSimulator`, - :class:`~qiskit.providers.aer.QasmSimulator`, - :class:`~qiskit.providers.aer.StatevectorSimulator`, and - :class:`~qiskit.providers.aer.UnitarySimulator` backends now takes a - :class:`~qiskit.circuit.QuantumCircuit` (or a list of - :class:`~qiskit.circuit.QuantumCircuit` objects) as it's input. - The previous :class:`~qiskit.qobj.QasmQobj` object is still supported for - now, but will be deprecated in a future release. - - For an example of how to use this see:: - - from qiskit import transpile, QuantumCircuit - - from qiskit.providers.aer import Aer - - backend = Aer.get_backend('aer_simulator') - - circuit = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - - tqc = transpile(circuit, backend) - result = backend.run(tqc, shots=4096).result() - -- The :meth:`~qiskit.providers.aer.PulseSimulator.run` method for the - :class:`~qiskit.providers.aer.PulseSimulator` backend now takes a - :class:`~qiskit.pulse.Schedule` (or a list of - :class:`~qiskit.pulse.Schedule` objects) as it's input. - The previous :class:`~qiskit.qobj.PulseQobj` object is still supported for - now, but will be deprecated in a future release. - -- Adds the new :class:`~qiskit.provider.aer.AerSimulator` simulator backend - supporting the following simulation methods - - * ``automatic`` - * ``statevector`` - * ``stabilizer`` - * ``density_matrix`` - * ``matrix_product_state`` - * ``unitary`` - * ``superop`` - - The default `automatic` method will automatically choose a simulation - method separately for each run circuit based on the circuit instructions - and noise model (if any). Initializing a simulator with a specific - method can be done using the `method` option. - - .. code::python - - from qiskit.providers.aer import AerSimulator - - # Create a MPS simulator backend - backend = AerSimulator(method='matrix_product_state') - - GPU simulation for the statevector, density matrix and unitary methods - can be enabled by setting the ``device='GPU'`` backend option. - - .. code::python - - from qiskit.providers.aer import AerSimulator - - # Create a GPU statevector backend - backend = AerSimulator(method='statevector', device='GPU') - - Note that the ``unitary`` and ``superop`` methods do not support measurement - as they simulate the unitary matrix or superoperator matrix of the run - circuit so one of the new :func:`~qiskit.providers.aer.library.save_unitary`, - :func:`~qiskit.providers.aer.library.save_superop`, or - :func:`~qiskit.providers.aer.library.save_state` instructions must - be used to save the simulator state to the returned results. Similarly - state of the other simulations methods can be saved using the - appropriate instructions. See the :mod:`qiskit.providers.aer.library` - API documents for more details. - - Note that the :class:`~qiskit.providers.aer.AerSimulator` simulator - superceds the :class:`~qiskit.providers.aer.QasmSimulator`, - :class:`~qiskit.providers.aer.StatevectorSimulator`, and - :class:`~qiskit.providers.aer.UnitarySimulator` backends which will - be deprecated in a future release. - -- Updates the :class:`~qiskit.providers.aer.AerProvider` class to include - multiple :class:`~qiskit.providers.aer.AerSimulator` backends preconfigured - for all available simulation methods and simulation devices. The new - backends can be accessed through the provider interface using the names - - * ``"aer_simulator"`` - * ``"aer_simulator_statevector"`` - * ``"aer_simulator_stabilizer"`` - * ``"aer_simulator_density_matrix"`` - * ``"aer_simulator_matrix_product_state"`` - * ``"aer_simulator_extended_stabilizer"`` - * ``"aer_simulator_unitary"`` - * ``"aer_simulator_superop"`` - - Additional if Aer was installed with GPU support on a compatible system - the following GPU backends will also be available - - * ``"aer_simulator_statevector_gpu"`` - * ``"aer_simulator_density_matrix_gpu"`` - * ``"aer_simulator_unitary_gpu"`` - - For example:: - - from qiskit import Aer - - # Get the GPU statevector simulator backend - backend = Aer.get_backend('aer_simulator_statevector_gpu') - -- Added a new ``norm estimation`` method for performing measurements when using - the ``"extended_stabilizer"`` simulation method. This norm estimation method - can be used by passing the following options to the - :class:`~qiskit.providers.aer.AerSimulator` and - :class:`~qiskit.providers.aer.QasmSimulator` backends - - .. code-block:: python - - simulator = QasmSimulator( - method='extended_stabilizer', - extended_stabilizer_sampling_method='norm_estimation') - - The norm estimation method is slower than the alternative ``metropolis`` - or ``resampled_metropolis`` options, but gives better performance on circuits - with sparse output distributions. See the documentation of the - :class:`~qiskit.providers.aer.QasmSimulator` for more information. - -- Adds instructions for saving the state of the simulator in various - formats. These instructions are - - * :class:`qiskit.providers.aer.library.SaveDensityMatrix` - * :class:`qiskit.providers.aer.library.SaveMatrixProductState` - * :class:`qiskit.providers.aer.library.SaveStabilizer` - * :class:`qiskit.providers.aer.library.SaveState` - * :class:`qiskit.providers.aer.library.SaveStatevector` - * :class:`qiskit.providers.aer.library.SaveStatevectorDict` - * :class:`qiskit.providers.aer.library.SaveUnitary` - - These instructions can be appended to a quantum circuit by using the - :class:`~qiskit.providers.aer.library.save_density_matrix`, - :class:`~qiskit.providers.aer.library.save_matrix_product_state`, - :class:`~qiskit.providers.aer.library.save_stabilizer`, - :class:`~qiskit.providers.aer.library.save_state`, - :class:`~qiskit.providers.aer.library.save_statevector`, - :class:`~qiskit.providers.aer.library.save_statevector_dict`, - :class:`~qiskit.providers.aer.library.save_unitary` - circuit methods which are added to ``QuantumCircuit`` when importing Aer. - - See the :mod:`qiskit.providers.aer.library` API documentation - for details on method compatibility for each instruction. - - Note that the snapshot instructions - :class:`~qiskit.providers.aer.extensions.SnapshotStatevector`, - :class:`~qiskit.providers.aer.extensions.SnapshotDensityMatrix`, - :class:`~qiskit.providers.aer.extensions.SnapshotStabilizer` are - still supported but will be deprecated in a future release. - -- Adds :class:`qiskit.providers.aer.library.SaveExpectationValue` and - :class:`qiskit.providers.aer.library.SaveExpectationValueVariance` - quantum circuit instructions for saving the expectation value - :math:`\langle H\rangle = Tr[H\rho]`, or expectation value and variance - :math:`Var(H) = \langle H^2\rangle - \langle H\rangle^2`, - of a Hermitian operator :math:`H` for the simulator state :math:`\rho`. - These instruction can be appended to a quantum circuit by using the - :class:`~qiskit.providers.aer.library.save_expectation_value` and - :class:`~qiskit.providers.aer.library.save_expectation_value_variance` - circuit methods which is added to ``QuantumCircuit`` when importing Aer. - - Note that the snapshot instruction - :class:`~qiskit.providers.aer.extensions.SnapshotExpectationValue`, - is still supported but will be deprecated in a future release. - -- Adds :class:`qiskit.providers.aer.library.SaveProbabilities` and - :class:`qiskit.providers.aer.library.SaveProbabilitiesDict` quantum - circuit instruction for saving all measurement outcome probabilities for - Z-basis measurements of the simualtor state. These instruction can be - appended to a quantum circuit by using the - :class:`~qiskit.providers.aer.library.save_probabilities` and - :class:`~qiskit.providers.aer.library.save_probabilities_dict` circuit - methods which is added to ``QuantumCircuit`` when importing Aer. - - Note that the snapshot instruction - :class:`~qiskit.providers.aer.extensions.SnapshotProbabilities`, - is still supported but will be deprecated in a future release. - -- Adds :class:`qiskit.providers.aer.library.SaveAmplitudes` and - :class:`qiskit.providers.aer.library.SaveAmplitudesSquared` - circuit instructions for saving select complex statevector amplitudes, - or select probabilities (amplitudes squared) for supported simulation - methods. These instructions can be appended to a quantum circuit by using the - :class:`~qiskit.providers.aer.library.save_amplitudes` and - :class:`~qiskit.providers.aer.library.save_amplitudes_squared` circuit - methods which is added to ``QuantumCircuit`` when importing Aer. - -- Adds instructions for setting the state of the simulators. These - instructions must be defined on the full number of qubits in the circuit. - They can be applied at any point in a circuit and will override the - simulator state with the one specified. Added instructions are - - * :class:`qiskit.providers.aer.library.SetDensityMatrix` - * :class:`qiskit.providers.aer.library.SetStabilizer` - * :class:`qiskit.providers.aer.library.SetStatevector` - * :class:`qiskit.providers.aer.library.SetUnitary` - - These instruction can be appended to a quantum circuit by using the - :class:`~qiskit.providers.aer.library.set_density_matrix`, - :class:`~qiskit.providers.aer.library.set_stabilizer`, - :class:`~qiskit.providers.aer.library.set_statevector`, - :class:`~qiskit.providers.aer.library.set_unitary` - circuit methods which are added to ``QuantumCircuit`` when importing Aer. - - See the :mod:`qiskit.providers.aer.library` API documentation - for details on method compatibility for each instruction. - -- Added support for diagonal gates to the ``"matrix_product_state"`` simulation - method. - -- Added support for the ``initialize`` instruction to the - ``"matrix_product_state"`` simulation method. - - -.. _Aer_Release Notes_0.8.0_Known Issues: - -Known Issues ------------- - -- There is a known issue where the simulation of certain circuits with a Kraus - noise model using the ``"matrix_product_state"`` simulation method can cause - the simulator to crash. Refer to - `#306 `__ for more - information. - - -.. _Aer_Release Notes_0.8.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The minimum version of `Conan `__ has been increased to 1.31.2. - This was necessary to fix a compatibility issue with newer versions of the - `urllib3 `__ (which is a dependency of Conan). - It also adds native support for AppleClang 12 which is useful for users with - new Apple computers. - -- ``pybind11`` minimum version required is 2.6 instead of 2.4. This is needed - in order to support CUDA enabled compilation in Windows. - -- Cython has been removed as a build dependency. - -- Removed x90 gate decomposition from noise models that was deprecated - in qiskit-aer 0.7. This decomposition is now done by using regular - noise model basis gates and the qiskit transpiler. - -- The following options for the ``"extended_stabilizer"`` simulation method - have changed. - - + ``extended_stabilizer_measure_sampling``: This option has been replaced - by the options ``extended_stabilizer_sampling_method``, which controls - how we simulate qubit measurement. - - + ``extended_stabilizer_mixing_time``: This option has been renamed as - ``extended_stabilizer_metropolis_mixing_time`` to clarify it only applies - to the ``metropolis`` and ``resampled_metropolis`` sampling methods. - - + ``extended_stabilizer_norm_estimation_samples``: This option has been renamed - to ``extended_stabilizer_norm_estimation_default_samples``. - - One additional option, ``extended_stabilizer_norm_estimation_repetitions`` has been - added, whih controls part of the behaviour of the norm estimation sampling method. - - -.. _Aer_Release Notes_0.8.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- Python 3.6 support has been deprecated and will be removed in a future - release. When support is removed you will need to upgrade the Python - version you're using to Python 3.7 or above. - - -.. _Aer_Release Notes_0.8.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixes bug with :class:`~qiskit.providers.aer.AerProvider` where options set - on the returned backends using - :meth:`~qiskit.providers.aer.QasmSimulator.set_options` were stored in the - provider and would persist for subsequent calls to - :meth:`~qiskit.providers.aer.AerProvider.get_backend` for the same named - backend. Now every call to - and :meth:`~qiskit.providers.aer.AerProvider.backends` returns a new - instance of the simulator backend that can be configured. - -- Fixes bug in the error message returned when a circuit contains unsupported - simulator instructions. Previously some supported instructions were also - being listed in the error message along with the unsupported instructions. - -- Fixes issue with setting :class:`~qiskit.providers.aer.QasmSimulator` - basis gates when using ``"method"`` and ``"noise_model"`` options - together, and when using them with a simulator constructed using - :meth:`~qiskit.providers.aer.QasmSimulator.from_backend`. Now the - listed basis gates will be the intersection of gates supported by - the backend configuration, simulation method, and noise model basis - gates. If the intersection of the noise model basis gates and - simulator basis gates is empty a warning will be logged. - -- Fix bug where the ``"sx"``` gate :class:`~qiskit.circuit.library.SXGate` was - not listed as a supported gate in the C++ code, in ``StateOpSet`` of - ``matrix_product_state.hp``. - -- Fix bug where ``"csx"``, ``"cu2"``, ``"cu3"`` were incorrectly listed as - supported basis gates for the ``"density_matrix"`` method of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Fix bug where parameters were passed incorrectly between functions in - ``matrix_product_state_internal.cpp``, causing wrong simulation, as well - as reaching invalid states, which in turn caused an infinite loop. - -- Fixes a bug that resulted in ``c_if`` not working when the - width of the conditional register was greater than 64. See - `#1077 `__. - -- Fixes a bug `#1153 `__) - where noise on conditional gates was always being applied regardless of - whether the conditional gate was actually applied based on the classical - register value. Now noise on a conditional gate will only be applied in - the case where the conditional gate is applied. - -- Fixes a bug with nested OpenMP flag was being set to true when it - shouldn't be. - -- Fixes a bug when applying truncation in the matrix product state method of the QasmSimulator. - -- Fixed issue `#1126 `__: - bug in reporting measurement of a single qubit. The bug occured when copying - the measured value to the output data structure. - -- In MPS, apply_kraus was operating directly on the input bits in the - parameter qubits, instead of on the internal qubits. In the MPS algorithm, - the qubits are constantly moving around so all operations should be applied - to the internal qubits. - -- When invoking MPS::sample_measure, we need to first sort the qubits to the - default ordering because this is the assumption in qasm_controller.This is - done by invoking the method move_all_qubits_to_sorted_ordering. It was - correct in sample_measure_using_apply_measure, but missing in - sample_measure_using_probabilities. - -- Fixes bug with the :meth:`~qiskit.providers.aer.QasmSimulator.from_backend` - method of the :class:`~qiskit.provider.aer.QasmSimulator` that would set the - ``local`` attribute of the configuration to the backend value rather than - always being set to ``True``. - -- Fixes bug in - :meth:`~qiskit.providers.aer.noise.NoiseModel.from_backend` and - :meth:`~qiskit.providers.aer.QasmSimulator.from_backend` where - :attr:`~qiskit.providers.aer.noise.NoiseModel.basis_gates` was set - incorrectly for IBMQ devices with basis gate set - ``['id', 'rz', 'sx', 'x', 'cx']``. Now the noise model will always - have the same basis gates as the backend basis gates regardless of - whether those instructions have errors in the noise model or not. - -- Fixes an issue where the Extended `"extended_stabilizer"` simulation method - would give incorrect results on quantum circuits with sparse output - distributions. Refer to - `#306 `__ for more - information and examples. - -Ignis 0.6.0 -=========== - -.. _Ignis_Release Notes_0.6.0_New Features: - -New Features ------------- - -- The :func:`qiskit.ignis.mitigation.expval_meas_mitigator_circuits` function - has been improved so that the number of circuits generated by the function - used for calibration by the CTMP method are reduced from :math:`O(n)` to - :math:`O(\log{n})` (where :math:`n` is the number of qubits). - - -.. _Ignis_Release Notes_0.6.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The :func:`qiskit.ignis.verification.randomized_benchmarking_seq` - function is now using the upgraded CNOTDihedral class, - :class:`qiskit.ignis.verification.CNOTDihedral`, which enables performing - CNOT-Dihedral Randomized Benchmarking on more than two qubits. - -- The python package ``retworkx`` is now a requirement for installing - qiskit-ignis. It replaces the previous usage of ``networkx`` (which is - no longer a requirement) to get better performance. - -- The ``scikit-learn`` dependency is no longer required and is now an optional - requirement. If you're using the IQ measurement discriminators - (:class:`~qiskit.ignis.measurement.IQDiscriminationFitter`, - :class:`~qiskit.ignis.measurement.LinearIQDiscriminationFitter`, - :class:`~qiskit.ignis.measurement.QuadraticIQDiscriminationFitter`, - or :class:`~qiskit.ignis.measurement.SklearnIQDiscriminator`) you will - now need to manually install scikit-learn, either by running - ``pip install scikit-learn`` or when you're also installing - qiskit-ignis with ``pip install qiskit-ignis[iq]``. - - -.. _Ignis_Release Notes_0.6.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue in the expectation value method - :meth:`~qiskit.ignis.mitigation.TensoredExpvalMeasMitigator.expectation_value`, - for the error mitigation classes - :class:`~qiskit.ignis.mitigation.TensoredExpvalMeasMitigator` and - :class:`~qiskit.ignis.mitigation.CTMPExpvalMeasMitigator` if the - ``qubits`` kwarg was not specified it would incorrectly use the - total number of qubits of the mitigator, rather than the number of - classical bits in the count dictionary leading to greatly reduced - performance. - Fixed `#561 `__ - -- Fix the ``"auto"`` method of the - :class:`~qiskit.ignis.verification.tomography.TomographyFitter`, - :class:`~qiskit.ignis.verification.tomography.StateTomographyFitter`, and - :class:`~qiskit.ignis.verification.tomography.ProcessTomographyFitter` to - only use ``"cvx"`` if CVXPY is installed *and* a third-party SDP solver - other than SCS is available. This is because the SCS solver has lower - accuracy than other solver methods and often returns a density matrix or - Choi-matrix that is not completely-positive and fails validation when used - with the :func:`qiskit.quantum_info.state_fidelity` or - :func:`qiskit.quantum_info.process_fidelity` functions. - -Aqua 0.9.0 -========== - -This release officially deprecates the Qiskit Aqua project, in the future -(no sooner than 3 months from this release) the Aqua project will have it's -final release and be archived. All the functionality that qiskit-aqua provides -has been migrated to either new packages or to other qiskit packages. The -application modules that are provided by qiskit-aqua have been split into -several new packages: ``qiskit-optimization``, ``qiskit-nature``, -``qiskit-machine-learning``, and ``qiskit-finance``. These packages can be -installed by themselves (via the standard pip install command, -ie ``pip install qiskit-nature``) or with the rest of the Qiskit metapackage as -optional extras (ie, ``pip install 'qiskit[finance,optimization]'`` or -``pip install 'qiskit[all]'``. The core building blocks for algorithms and the -operator flow now exist as part of qiskit-terra at :mod:`qiskit.algorithms` and -:mod:`qiskit.opflow`. Depending on your existing usage of Aqua you should either -use the application packages or the new modules in Qiskit Terra. - -For more details on how to migrate from using Qiskit Aqua, you can refer to the -`migration guide `_. - -IBM Q Provider 0.12.2 -===================== - -No change - -############# -Qiskit 0.24.1 -############# - -Terra 0.16.4 -============ - -No change - -Aer 0.7.6 -========= - -No change - -Ignis 0.5.2 -=========== - -No change - -Aqua 0.8.2 -========== - -No change - -IBM Q Provider 0.12.2 -===================== - -.. _Release Notes_IBMQ_0.12.2_New Features: - -Upgrade Notes -------------- - -- :meth:`qiskit.providers.ibmq.IBMQBackend.defaults` now returns the pulse defaults for - the backend if the backend supports pulse. However, your provider may not support pulse - even if the backend does. The ``open_pulse`` flag in backend configuration indicates - whether the provider supports it. - -############# -Qiskit 0.24.0 -############# - -Terra 0.16.4 -============ - -No change - -Aer 0.7.6 -========= - -.. _Release Notes_Aer_0.7.6_New Features: - -New Features -------------- - -- This is the first release of qiskit-aer that publishes precompiled binaries - to PyPI for Linux on aarch64 (arm64). From this release onwards Linux aarch64 - packages will be published and supported. - - -.. _Release Notes_Aer_0.7.6_Bug Fixes: - -Bug Fixes ---------- - -- Fixes a bug `#1153 `__ - where noise on conditional gates was always being applied regardless of - whether the conditional gate was actually applied based on the classical - register value. Now noise on a conditional gate will only be applied in - the case where the conditional gate is applied. - -- Fixed issue `#1126 `__: - bug in reporting measurement of a single qubit. The bug occured when - copying the measured value to the output data structure. - -- There was previously a mismatch between the default reported number of qubits - the Aer backend objects would say were supported and the the maximum number - of qubits the simulator would actually run. This was due to a mismatch - between the Python code used for calculating the max number of qubits and - the C++ code used for a runtime check for the max number of qubits based on - the available memory. This has been correct so by default now Aer backends - will allow running circuits that can fit in all the available system memory. - Fixes `#1114 `__ - - -No change - -Ignis 0.5.2 -=========== - -No change - -Aqua 0.8.2 -========== - -No change - -IBM Q Provider 0.12.0 -===================== - -.. _Release Notes_IBMQ_0.12.0_Prelude: - -Prelude -------- - -- :meth:`qiskit.providers.ibmq.IBMQBackend.run` method now takes one or more - :class:`~qiskit.circuit.QuantumCircuit` or :class:`~qiskit.pulse.Schedule`. - Use of :class:`~qiskit.qobj.QasmQobj` and :class:`~qiskit.qobj.PulseQobj` is - now deprecated. Runtime configuration options, such as the number of shots, - can be set via either the :meth:`~qiskit.providers.ibmq.IBMQBackend.run` - method, or the :meth:`qiskit.providers.ibmq.IBMQBackend.set_options` method. - The former is used as a one-time setting for the job, and the latter for all - jobs sent to the backend. If an option is set in both places, the value set - in :meth:`~qiskit.providers.ibmq.IBMQBackend.run` takes precedence. - -- IBM Quantum credentials are now loaded only from sections of the ``qiskitrc`` - file that start with 'ibmq'. - -.. _Release Notes_IBMQ_0.12.0_New Features: - -New Features ------------- - -- Python 3.9 support has been added in this release. You can now run Qiskit - IBMQ provider using Python 3.9. - -- :meth:`qiskit.providers.ibmq.AccountProvider.backends` now has a new - parameter `min_num_qubits` that allows you to filter by the minimum number - of qubits. - -- :meth:`qiskit.providers.ibmq.IBMQBackend.run` method now takes one or more - :class:`~qiskit.circuit.QuantumCircuit` or :class:`~qiskit.pulse.Schedule`. - Runtime configuration options, such as the number of shots, can be set via - either the :meth:`~qiskit.providers.ibmq.IBMQBackend.run` method, or - the :meth:`qiskit.providers.ibmq.IBMQBackend.set_options` method. The former - is used as a one-time setting for the job, and the latter for all jobs - sent to the backend. If an option is set in both places, the value set - in :meth:`~qiskit.providers.ibmq.IBMQBackend.run` takes precedence. For - example: - - .. code-block:: python - - from qiskit import IBMQ, transpile - from qiskit.test.reference_circuits import ReferenceCircuits - - provider = IBMQ.load_account() - backend = provider.get_backend('ibmq_vigo') - circuits = transpile(ReferenceCircuits.bell(), backend=backend) - default_shots = backend.options.shots # Returns the backend default of 1024 shots. - backend.set_options(shots=2048) # All jobs will now have use 2048 shots. - backend.run(circuits) # This runs with 2048 shots. - backend.run(circuits, shots=8192) # This runs with 8192 shots. - backend.run(circuits) # This again runs with 2048 shots. - - -- :class:`qiskit.providers.ibmq.experiment.Experiment` now has three - additional attributes, `hub`, `group`, and `project`, that identify - the provider used to create the experiment. - -- You can now assign an ``experiment_id`` to a job when submitting it using - :meth:`qiskit.providers.ibmq.IBMQBackend.run`. You can use this new field - to group together a collection of jobs that belong to the same experiment. - The :meth:`qiskit.providers.ibmq.IBMQBackendService.jobs` method was also - updated to allow filtering by ``experiment_id``. - -- :class:`qiskit.providers.ibmq.experiment.Experiment` now has two - additional attributes: - - * share_level: The level at which the experiment is shared which determines - who can see it when listing experiments. This can be updated. - * owner: The ID of the user that uploaded the experiment. This is set by - the server and cannot be updated. - -- The method - :meth:`qiskit.providers.ibmq.experimentservice.ExperimentService.experiments` - now accepts ``hub``, ``group``, and ``project`` as filtering keywords. - -- Methods - :meth:`qiskit.providers.ibmq.experiment.ExperimentService.experiments` and - :meth:`qiskit.providers.ibmq.experiment.ExperimentService.analysis_results` - now support a ``limit`` parameter that allows you to limit the number of - experiments and analysis results returned. - -- The method - :meth:`qiskit.providers.ibmq.experimentservice.ExperimentService.experiments` - now accepts ``exclude_mine`` and ``mine_only`` as filtering keywords. - -- The method - :meth:`qiskit.providers.ibmq.experimentservice.ExperimentService.experiments` - now accepts ``exclude_public`` and ``public_only`` as filtering keywords. - -- :meth:`qiskit.providers.ibmq.managed.IBMQJobManager.run` now accepts a - single :class:`~qiskit.circuit.QuantumCircuit` or - :class:`~qiskit.pulse.Schedule` in addition to a list of them. - -- The :func:`~qiskit.providers.ibmq.least_busy` function now skips backends - that are operational but paused, meaning they are accepting but not - processing jobs. - -- You can now pickle an :class:`~qiskit.providers.ibmq.job.IBMQJob` instance, - as long as it doesn't contain custom data that is not picklable (e.g. - in Qobj header). - -- You can now use the two new methods, - :meth:`qiskit.providers.ibmq.AccountProvider.services` and - :meth:`qiskit.providers.ibmq.AccountProvider.service` to find out what - services are available to your account and get an instance of a - particular service. - -- The :meth:`qiskit.providers.ibmq.IBMQBackend.reservations` method - now always returns the reservation scheduling modes even for - reservations that you don't own. - - -.. _Release Notes_IBMQ_0.12.0_Upgrade Notes: - -Upgrade Notes -------------- - -- A number of previously deprecated methods and features have been removed, - including: - - * :meth:`qiskit.providers.ibmq.job.IBMQJob.to_dict` - * :meth:`qiskit.providers.ibmq.job.IBMQJob.from_dict` - * `Qconfig.py` support - * Use of proxy URLs that do not include protocols - -- A new parameter, ``limit`` is now the first parameter for both - :meth:`qiskit.providers.ibmq.experiment.ExperimentService.experiments` and - :meth:`qiskit.providers.ibmq.experiment.ExperimentService.analysis_results` - methods. This ``limit`` has a default value of 10, meaning by deafult only - 10 experiments and analysis results will be returned. - -- IBM Quantum credentials are now loaded only from sections of the ``qiskitrc`` - file that start with 'ibmq'. - This allows the ``qiskitrc`` file to be used for other functionality. - - -.. _Release Notes_IBMQ_0.12.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- Use of :class:`~qiskit.qobj.QasmQobj` and :class:`~qiskit.qobj.PulseQobj` in - the :meth:`qiskit.providers.ibmq.IBMQBackend.run` method is now deprecated. - :class:`~qiskit.circuit.QuantumCircuit` and :class:`~qiskit.pulse.Schedule` - should now be used instead. - -- The ``backends`` attribute of :class:`qiskit.providers.ibmq.AccountProvider` - has been renamed to ``backend`` (sigular). For backward compatibility, you - can continue to use ``backends``, but it is deprecated and will be removed - in a future release. The :meth:`qiskit.providers.ibmq.AccountProvider.backends` - method remains unchanged. For example: - - .. code-block:: python - - backend = provider.backend.ibmq_vigo # This is the new syntax. - backend = provider.backends.ibmq_vigo # This is deprecated. - backends = provider.backends() # This continues to work as before. - -- Setting of the :class:`~qiskit.providers.ibmq.job.IBMQJob` - ``client_version`` attribute has been deprecated. You can, however, continue - to read the value of attribute. - -- "The ``validate_qobj`` keyword in :meth:`qiskit.providers.ibmq.IBMQBackend.run` - is deprecated and will be removed in a future release. - If you're relying on this schema validation you should pull the schemas - from the `Qiskit/ibmq-schemas `_ - and directly validate your payloads with that. - - -.. _Release Notes_IBMQ_0.12.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixes the issue wherein a job could be left in the ``CREATING`` state if - job submit fails half-way through. - -- Fixes the issue wherein using Jupyter backend widget would fail if the - backend's basis gates do not include the traditional u1, u2, and u3. - Fixes `#844 `_ - -- Fixes the infinite loop raised when passing an ``IBMQRandomService`` instance - to a child process. - -- Fixes the issue wherein a ``TypeError`` is raised if the server returns - an error code but the response data is not in the expected format. - -############# -Qiskit 0.23.6 -############# - -Terra 0.16.4 -============ - -No change - -Aer 0.7.5 -========= - -.. _Release Notes_Aer_0.7.5_Prelude: - -Prelude -------- - -This release is a bugfix release that fixes compatibility in the precompiled -binary wheel packages with numpy versions < 1.20.0. The previous release 0.7.4 -was building the binaries in a way that would require numpy 1.20.0 which has -been resolved now, so the precompiled binary wheel packages will work with any -numpy compatible version. - -Ignis 0.5.2 -=========== - -No change - -Aqua 0.8.2 -========== - -No change - -IBM Q Provider 0.11.1 -===================== - -No change - -############# -Qiskit 0.23.5 -############# - -Terra 0.16.4 -============ - -.. _Release Notes_0.16.4_Prelude: - -Prelude -------- - -This release is a bugfix release that primarily fixes compatibility with numpy -1.20.0. This numpy release deprecated their local aliases for Python's numeric -types (``np.int`` -> ``int``, ``np.float`` -> ``float``, etc.) and the usage of -these aliases in Qiskit resulted in a large number of deprecation warnings being -emitted. This release fixes this so you can run Qiskit with numpy 1.20.0 without -those deprecation warnings. - -Aer 0.7.4 -========= - -.. _Release Notes_Aer_0.7.4_Bug Fixes: - -Bug Fixes ----------- - -Fixes compatibility with numpy 1.20.0. This numpy release deprecated their local -aliases for Python's numeric types (``np.int`` -> ``int``, -``np.float`` -> ``float``, etc.) and the usage of these aliases in Qiskit Aer -resulted in a large number of deprecation warnings being emitted. This release -fixes this so you can run Qiskit Aer with numpy 1.20.0 without those deprecation -warnings. - -Ignis 0.5.2 -=========== - -.. _Release Notes_Ignis_0.5.2_Prelude: - -Prelude -------- - -This release is a bugfix release that primarily fixes compatibility with numpy -1.20.0. It is also the first release to include support for Python 3.9. Earlier -releases (including 0.5.0 and 0.5.1) worked with Python 3.9 but did not -indicate this in the package metadata, and there was no upstream testing for -those releases. This release fixes that and was tested on Python 3.9 (in -addition to 3.6, 3.7, and 3.8). - -.. _Release Notes_Ignis_0.5.2_Bug Fixes: - -Bug Fixes ---------- - -- `networkx `__ is explicitly listed as a dependency - now. It previously was an implicit dependency as it was required for the - :mod:`qiskit.ignis.verification.topological_codes` module but was not - correctly listed as a depdendency as qiskit-terra also requires networkx - and is also a depdency of ignis so it would always be installed in practice. - However, it is necessary to list it as a requirement for future releases - of qiskit-terra that will not require networkx. It's also important to - correctly list the dependencies of ignis in case there were a future - incompatibility between version requirements. - -Aqua 0.8.2 -========== - - -IBM Q Provider 0.11.1 -===================== - -No change - -############# -Qiskit 0.23.4 -############# - -Terra 0.16.3 -============ - -.. _Release Notes_0.16.3_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue introduced in 0.16.2 that would cause errors when running - :func:`~qiskit.compiler.transpile` on a circuit with a series of 1 qubit - gates and a non-gate instruction that only operates on a qubit (e.g. - :class:`~qiskit.circuit.Reset`). Fixes - `#5736 `__ - -Aer 0.7.3 -========= - -No change - -Ignis 0.5.1 -=========== - -No change - -Aqua 0.8.1 -========== - -No change - -IBM Q Provider 0.11.1 -===================== - -No change - -############# -Qiskit 0.23.3 -############# - -Terra 0.16.2 -============ - -.. _Release Notes_0.16.2_New Features: - -New Features ------------- - -- Python 3.9 support has been added in this release. You can now run Qiskit - Terra using Python 3.9. - - -.. _Release Notes_0.16.2_Upgrade Notes: - -Upgrade Notes -------------- - -- The class :class:`~qiskit.library.standard_gates.x.MCXGrayCode` will now create - a ``C3XGate`` if ``num_ctrl_qubits`` is 3 and a ``C4XGate`` if ``num_ctrl_qubits`` - is 4. This is in addition to the previous functionality where for any of the - modes of the :class:'qiskit.library.standard_gates.x.MCXGate`, if ``num_ctrl_bits`` - is 1, a ``CXGate`` is created, and if 2, a ``CCXGate`` is created. - - -.. _Release Notes_0.16.2_Bug Fixes: - -Bug Fixes ---------- - -- Pulse :py:class:`~qiskit.pulse.instructions.Delay` instructions are now - explicitly assembled as :class:`~qiskit.qobj.PulseQobjInstruction` objects - included in the :class:`~qiskit.qobj.PulseQobj` output from - :func:`~qiskit.compiler.assemble`. - - Previously, we could ignore :py:class:`~qiskit.pulse.instructions.Delay` - instructions in a :class:`~qiskit.pulse.Schedule` as part of - :func:`~qiskit.compiler.assemble` as the time was explicit in the - :class:`~qiskit.qobj.PulseQobj` objects. But, now with pulse gates, there - are situations where we can schedule ONLY a delay, and not including the - delay itself would remove the delay. - -- Circuits with custom gate calibrations can now be scheduled with the - transpiler without explicitly providing the durations of each circuit - calibration. - -- The :class:`~qiskit.transpiler.passes.BasisTranslator` and - :class:`~qiskit.transpiler.passes.Unroller` passes, in some cases, had not been - preserving the global phase of the circuit under transpilation. This has - been fixed. - -- A bug in :func:`qiskit.pulse.builder.frequency_offset` where when - ``compensate_phase`` was set a factor of :math:`2\pi` - was missing from the appended phase. - -- Fix the global phase of the output of the - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.repeat`. If a circuit with global - phase is appended to another circuit, the global phase is currently not - propagated. Simulators rely on this, since the phase otherwise gets - applied multiple times. This sets the global phase of - :meth:`~qiskit.circuit.QuantumCircuit.repeat` to 0 before appending the - repeated circuit instead of multiplying the existing phase times the - number of repetitions. - -- Fixes bug in :class:`~qiskit.quantum_info.SparsePauliOp` where multiplying - by a certain non Python builtin Numpy scalar types returned incorrect values. - Fixes `#5408 `__ - -- The definition of the Hellinger fidelity from has been corrected from the - previous defition of :math:`1-H(P,Q)` to :math:`[1-H(P,Q)^2]^2` so that it - is equal to the quantum state fidelity of P, Q as diagonal density - matrices. - -- Reduce the number of CX gates in the decomposition of the 3-controlled - X gate, :class:`~qiskit.circuit.library.C3XGate`. Compiled and optimized - in the `U CX` basis, now only 14 CX and 16 U gates are used instead of - 20 and 22, respectively. - -- Fixes the issue wherein using Jupyter backend widget or - :meth:`qiskit.tools.backend_monitor` would fail if the - backend's basis gates do not include the traditional u1, u2, and u3. - -- When running :func:`qiskit.compiler.transpile` on a list of circuits with a - single element, the function used to return a circuit instead of a list. Now, - when :func:`qiskit.compiler.transpile` is called with a list, it will return a - list even if that list has a single element. See - `#5260 `__. - - .. code-block:: python - - from qiskit import * - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - - transpiled = transpile([qc]) - print(type(transpiled), len(transpiled)) - - .. parsed-literal:: - 1 - -Aer 0.7.3 -========== - -.. _Release Notes_Aer_0.7.3_New Features: - -New Features ------------- - -- Python 3.9 support has been added in this release. You can now run Qiskit - Aer using Python 3.9 without building from source. - - -.. _Release Notes_Aer_0.7.3_Bug Fixes: - -Bug Fixes ---------- - -- Fixes issue with setting :class:`~qiskit.providers.aer.QasmSimulator` - basis gates when using ``"method"`` and ``"noise_model"`` options - together, and when using them with a simulator constructed using - :meth:`~qiskit.providers.aer.QasmSimulator.from_backend`. Now the - listed basis gates will be the intersection of gates supported by - the backend configuration, simulation method, and noise model basis - gates. If the intersection of the noise model basis gates and - simulator basis gates is empty a warning will be logged. - -- Fixes a bug that resulted in `c_if` not working when the - width of the conditional register was greater than 64. See - `#1077 `__. - -- Fixes bug in - :meth:`~qiskit.providers.aer.noise.NoiseModel.from_backend` and - :meth:`~qiskit.providers.aer.QasmSimulator.from_backend` where - :attr:`~qiskit.providers.aer.noise.NoiseModel.basis_gates` was set - incorrectly for IBMQ devices with basis gate set - ``['id', 'rz', 'sx', 'x', 'cx']``. Now the noise model will always - have the same basis gates as the backend basis gates regardless of - whether those instructions have errors in the noise model or not. - -- Fixes a bug when applying truncation in the matrix product state method of the QasmSimulator. - -Ignis 0.5.1 -=========== - -No change - -Aqua 0.8.1 -========== - -No change - -IBM Q Provider 0.11.1 -===================== - -No change - -############# -Qiskit 0.23.2 -############# - -Terra 0.16.1 -============ - -No change - -Aer 0.7.2 -========== - -.. _Release Notes_0.7.2_New Features: - -New Features ------------- - -- Add the CMake flag ``DISABLE_CONAN`` (default=``OFF``)s. When installing from source, - setting this to ``ON`` allows bypassing the Conan package manager to find libraries - that are already installed on your system. This is also available as an environment - variable ``DISABLE_CONAN``, which takes precedence over the CMake flag. - This is not the official procedure to build AER. Thus, the user is responsible - of providing all needed libraries and corresponding files to make them findable to CMake. - - -.. _Release Notes_0.7.2_Bug Fixes: - -Bug Fixes ---------- - -- Fixes a bug with nested OpenMP flag was being set to true when it - shouldn't be. - -Ignis 0.5.1 -=========== - -No change - -Aqua 0.8.1 -========== - -No change - -IBM Q Provider 0.11.1 -===================== - -No change - - -############# -Qiskit 0.23.1 -############# - -.. _Release Notes_0.16.1: - -Terra 0.16.1 -============ - -.. _Release Notes_0.16.1_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue where an error was thrown in execute for valid circuits - built with delays. - -- The QASM definition of 'c4x' in qelib1.inc has been corrected to match - the standard library definition for C4XGate. - -- Fixes a bug in subtraction for quantum channels :math:`A - B` where :math:`B` - was an :class:`~qiskit.quantum_info.Operator` object. Negation was being - applied to the matrix in the Operator representation which is not equivalent - to negation in the quantum channel representation. - -- Changes the way - :meth:`~qiskit.quantum_info.states.statevector.Statevector._evolve_instruction` - access qubits to handle the case of an instruction with multiple registers. - -.. _Release Notes_Aer_0.7.1: - -Aer 0.7.1 -========= - -.. _Release Notes_Aer_0.7.1_Upgrade Notes: - -Upgrade Notes -------------- - -- The minimum cmake version to build qiskit-aer has increased from 3.6 to - 3.8. This change was necessary to enable fixing GPU version builds that - support running on x86_64 CPUs lacking AVX2 instructions. - - -.. _Release Notes_Aer_0.7.1_Bug Fixes: - -Bug Fixes ---------- - -- qiskit-aer with GPU support will now work on systems with x86_64 CPUs - lacking AVX2 instructions. Previously, the GPU package would only run if - the AVX2 instructions were available. Fixes - `#1023 `__ - -- Fixes bug with :class:`~qiskit.providers.aer.AerProvider` where options set - on the returned backends using - :meth:`~qiskit.providers.aer.QasmSimulator.set_options` were stored in the - provider and would persist for subsequent calls to - :meth:`~qiskit.providers.aer.AerProvider.get_backend` for the same named - backend. Now every call to - and :meth:`~qiskit.providers.aer.AerProvider.backends` returns a new - instance of the simulator backend that can be configured. - -- Fixes bug in the error message returned when a circuit contains unsupported - simulator instructions. Previously some supported instructions were also - being listed in the error message along with the unsupported instructions. - -- Fix bug where the `"sx"`` gate :class:`~qiskit.circuit.library.SXGate` was - not listed as a supported gate in the C++ code, in `StateOpSet` of - `matrix_product_state.hp`. - -- Fix bug where ``"csx"``, ``"cu2"``, ``"cu3"`` were incorrectly listed as - supported basis gates for the ``"density_matrix"`` method of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- In MPS, apply_kraus was operating directly on the input bits in the - parameter qubits, instead of on the internal qubits. In the MPS algorithm, - the qubits are constantly moving around so all operations should be applied - to the internal qubits. - -- When invoking MPS::sample_measure, we need to first sort the qubits to the - default ordering because this is the assumption in qasm_controller.This is - done by invoking the method move_all_qubits_to_sorted_ordering. It was - correct in sample_measure_using_apply_measure, but missing in - sample_measure_using_probabilities. - - -.. _Release Notes_Ignis_0.5.1: - -Ignis 0.5.1 -=========== - -.. _Release Notes_Ignis_0.5.1_Bug Fixes: - -Bug Fixes ---------- - -- Fix the ``"auto"`` method of the - :class:`~qiskit.ignis.verification.tomography.TomographyFitter`, - :class:`~qiskit.ignis.verification.tomography.StateTomographyFitter`, and - :class:`~qiskit.ignis.verification.tomography.ProcessTomographyFitter` to - only use ``"cvx"`` if CVXPY is installed *and* a third-party SDP solver - other than SCS is available. This is because the SCS solver has lower - accuracy than other solver methods and often returns a density matrix or - Choi-matrix that is not completely-positive and fails validation when used - with the :func:`qiskit.quantum_info.state_fidelity` or - :func:`qiskit.quantum_info.process_fidelity` functions. - -.. _Release Notes_Aqua_0.8.1: - -Aqua 0.8.1 -========== - -0.8.1 -===== - -.. _Release Notes_Aqua_0.8.1_New Features: - -New Features ------------- - -- A new algorithm has been added: the Born Openheimer Potential Energy surface for the - calculation of potential energy surface along different degrees of freedom of the molecule. - The algorithm is called ``BOPESSampler``. It further provides functionalities of fitting the - potential energy surface to an analytic function of predefined potentials.some details. - - -.. _Release Notes_Aqua_0.8.1_Critical Issues: - -Critical Issues ---------------- - -- Be aware that ``initial_state`` parameter in ``QAOA`` has now different implementation - as a result of a bug fix. The previous implementation wrongly mixed the user provided - ``initial_state`` with Hadamard gates. The issue is fixed now. No attention needed if - your code does not make use of the user provided ``initial_state`` parameter. - - -.. _Release Notes_Aqua_0.8.1_Bug Fixes: - -Bug Fixes ---------- - -- optimize_svm method of qp_solver would sometimes fail resulting in an error like this - `ValueError: cannot reshape array of size 1 into shape (200,1)` This addresses the issue - by adding an L2 norm parameter, lambda2, which defaults to 0.001 but can be changed via - the QSVM algorithm, as needed, to facilitate convergence. - -- A method ``one_letter_symbol`` has been removed from the ``VarType`` in the latest - build of DOCplex making Aqua incompatible with this version. So instead of using this method - an explicit type check of variable types has been introduced in the Aqua optimization module. - -- :meth`~qiskit.aqua.operators.state_fns.DictStateFn.sample()` could only handle - real amplitudes, but it is fixed to handle complex amplitudes. - `#1311 ` for more details. - -- Trotter class did not use the reps argument in constructor. - `#1317 ` for more details. - -- Raise an `AquaError` if :class`qiskit.aqua.operators.converters.CircuitSampler` - samples an empty operator. - `#1321 ` for more details. - -- :meth:`~qiskit.aqua.operators.legacy.WeightedPauliOperator.to_opflow()` - returns a correct operator when coefficients are complex numbers. - `#1381 ` for more details. - -- Let backend simulators validate NoiseModel support instead of restricting to Aer only - in QuantumInstance. - -- Correctly handle PassManager on QuantumInstance ``transpile`` method by - calling its ``run`` method if it exists. - -- A bug that mixes custom ``initial_state`` in ``QAOA`` with Hadamard gates has been fixed. - This doesn't change functionality of QAOA if no initial_state is provided by the user. - Attention should be taken if your implementation uses QAOA with cusom ``initial_state`` - parameter as the optimization results might differ. - -- Previously, setting `seed_simulator=0` in the `QuantumInstance` did not set - any seed. This was only affecting the value 0. This has been fixed. - - - .. _Release Notes_IBMQ_0.11.1: - -IBM Q Provider 0.11.1 -===================== - - .. _Release Notes_IBMQ_0.11.1_New Features: - -New Features ------------- - -- :class:`qiskit.providers.ibmq.experiment.Experiment` now has three - additional attributes, `hub`, `group`, and `project`, that identify - the provider used to create the experiment. - -- Methods - :meth:`qiskit.providers.ibmq.experiment.ExperimentService.experiments` and - :meth:`qiskit.providers.ibmq.experiment.ExperimentService.analysis_results` - now support a ``limit`` parameter that allows you to limit the number of - experiments and analysis results returned. - - -.. _Release Notes_IBMQ_0.11.1_Upgrade Notes: - -Upgrade Notes -------------- - -- A new parameter, ``limit`` is now the first parameter for both - :meth:`qiskit.providers.ibmq.experiment.ExperimentService.experiments` and - :meth:`qiskit.providers.ibmq.experiment.ExperimentService.analysis_results` - methods. This ``limit`` has a default value of 10, meaning by deafult only - 10 experiments and analysis results will be returned. - - -.. _Release Notes_IBMQ_0.11.1_Bug Fixes: - -Bug Fixes ---------- - -- Fixes the issue wherein a job could be left in the ``CREATING`` state if - job submit fails half-way through. - -- Fixes the infinite loop raised when passing an ``IBMQRandomService`` instance - to a child process. - - -############# -Qiskit 0.23.0 -############# - -Terra 0.16.0 -============ - -.. _Release Notes_0.16.0_Prelude: - -Prelude -------- - -The 0.16.0 release includes several new features and bug fixes. The -major features in this release are the following: - -* Introduction of scheduled circuits, where delays can be used to control - the timing and alignment of operations in the circuit. -* Compilation of quantum circuits from classical functions, such as - oracles. -* Ability to compile and optimize single qubit rotations over different - Euler basis as well as the phase + square-root(X) basis (i.e. - ``['p', 'sx']``), which will replace the older IBM Quantum basis of - ``['u1', 'u2', 'u3']``. -* Tracking of :meth:`~qiskit.circuit.QuantumCircuit.global_phase` on the - :class:`~qiskit.circuit.QuantumCircuit` class has been extended through - the :mod:`~qiskit.transpiler`, :mod:`~qiskit.quantum_info`, and - :mod:`~qiskit.assembler` modules, as well as the BasicAer and Aer - simulators. Unitary and state vector simulations will now return global - phase-correct unitary matrices and state vectors. - -Also of particular importance for this release is that Python 3.5 is no -longer supported. If you are using Qiskit Terra with Python 3.5, the -0.15.2 release is that last version which will work. - - -.. _Release Notes_0.16.0_New Features: - -New Features ------------- - -- Global R gates have been added to :mod:`qiskit.circuit.library`. This - includes the global R gate (:class:`~qiskit.circuit.library.GR`), - global Rx (:class:`~qiskit.circuit.library.GRX`) and global Ry - (:class:`~qiskit.circuit.library.GRY`) gates which are derived from the - :class:`~qiskit.circuit.library.GR` gate, and global Rz ( - :class:`~qiskit.circuit.library.GRZ`) that is defined in a similar way - to the :class:`~qiskit.circuit.library.GR` gates. The global R gates are - defined on a number of qubits simultaneously, and act as a direct sum of - R gates on each qubit. - - For example: - - .. code-block :: python - - from qiskit import QuantumCircuit, QuantumRegister - import numpy as np - - num_qubits = 3 - qr = QuantumRegister(num_qubits) - qc = QuantumCircuit(qr) - - qc.compose(GR(num_qubits, theta=np.pi/3, phi=2*np.pi/3), inplace=True) - - will create a :class:`~qiskit.circuit.QuantumCircuit` on a - :class:`~qiskit.circuit.QuantumRegister` of 3 qubits and perform a - :class:`~qiskit.circuit.library.RGate` of an angle - :math:`\theta = \frac{\pi}{3}` about an axis in the xy-plane of the Bloch - spheres that makes an angle of :math:`\phi = \frac{2\pi}{3}` with the x-axis - on each qubit. - -- A new color scheme, ``iqx``, has been added to the ``mpl`` backend for the - circuit drawer :func:`qiskit.visualization.circuit_drawer` and - :meth:`qiskit.circuit.QuantumCircuit.draw`. This uses the same color scheme - as the Circuit Composer on the IBM Quantum Experience website. There are - now 3 available color schemes - ``default``, ``iqx``, and ``bw``. - - There are two ways to select a color scheme. The first is to use a user - config file, by default in the ``~/.qiskit`` directory, in the - file ``settings.conf`` under the ``[Default]`` heading, a user can enter - ``circuit_mpl_style = iqx`` to select the ``iqx`` color scheme. - - The second way is to add ``{'name': 'iqx'}`` to the ``style`` kwarg to the - ``QuantumCircuit.draw`` method or to the ``circuit_drawer`` function. The - second way will override the setting in the settings.conf file. For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.cx(0, 1) - circuit.measure_all() - circuit.draw('mpl', style={'name': 'iqx'}) - -- In the ``style`` kwarg for the the circuit drawer - :func:`qiskit.visualization.circuit_drawer` and - :meth:`qiskit.circuit.QuantumCircuit.draw` the ``displaycolor`` field with - the ``mpl`` backend now allows for entering both the gate color and the text - color for each gate type in the form ``(gate_color, text_color)``. This - allows the use of light and dark gate colors with contrasting text colors. - Users can still set only the gate color, in which case the ``gatetextcolor`` - field will be used. Gate colors can be set in the ``style`` dict for any - number of gate types, from one to the entire ``displaycolor`` dict. For - example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - - circuit = QuantumCircuit(1) - circuit.h(0) - - style_dict = {'displaycolor': {'h': ('#FA74A6', '#000000')}} - circuit.draw('mpl', style=style_dict) - - or - - .. code-block:: python - - style_dict = {'displaycolor': {'h': '#FA74A6'}} - circuit.draw('mpl', style=style_dict) - -- Two alignment contexts are added to the pulse builder - (:mod:`qiskit.pulse.builder`) to facilitate writing a repeated pulse - sequence with delays. - - * :func:`qiskit.pulse.builder.align_equispaced` inserts delays with - equivalent length in between pulse schedules within the context. - * :func:`qiskit.pulse.builder.align_func` offers more advanced control of - pulse position. This context takes a callable that calculates a fractional - coordinate of i-th pulse and aligns pulses within the context. This makes - coding of dynamical decoupling easy. - -- A ``rep_delay`` parameter has been added to the - :class:`~qiskit.qobj.QasmQobj` class under the run configuration, - :class:`~qiskit.qobj.QasmQobjConfig`. This parameter is used to denote the - time between program executions. It must be chosen from the backend range - given by the :class:`~qiskit.providers.models.BackendConfiguration` - method - :meth:`~qiskit.providers.models.BackendConfiguration.rep_delay_range`. If a - value is not provided a backend default, - :attr:`qiskit.providers.models.BackendConfiguration.default_rep_delay`, - will be used. ``rep_delay`` will only work on backends which allow for - dynamic repetition time. This is can be checked with the - :class:`~qiskit.providers.models.BackendConfiguration` property - :attr:`~qiskit.providers.models.BackendConfiguration.dynamic_reprate_enabled`. - -- The ``qobj_schema.json`` JSON Schema file in :mod:`qiskit.schemas` has - been updated to include the ``rep_delay`` as an optional configuration - property for QASM Qobjs. - -- The ``backend_configuration_schema.json`` JSON Schema file in - :mod:`qiskit.schemas` has been updated to include ``dynamic_reprate_enabled``, - ``rep_delay_range`` and ``default_rep_delay`` as optional properties for a QASM - backend configuration payload. - -- A new optimization pass, - :class:`qiskit.transpiler.passes.TemplateOptimization` has been added to - the transpiler. This pass applies a template matching algorithm described - in `arXiv:1909.05270 `__ that - replaces all compatible maximal matches in the circuit. - - To implement this new transpiler pass a new module, ``template_circuits``, - was added to the circuit library (:mod:`qiskit.circuit.library`). This new - module contains all the Toffoli circuit templates used in the - :class:`~qiskit.transpiler.passes.TemplateOptimization`. - - This new pass is **not** currently included in the preset pass managers - (:mod:`qiskit.transpiler.preset_passmanagers`), to use it you will need - to create a custom :class:`~qiskit.transpiler.PassManager`. - -- A new version of the providers interface has been added. This new interface, - which can be found in :mod:`qiskit.providers`, provides a new versioning - mechanism that will enable changes to the interface to happen in a - compatible manner over time. The new interface should be simple to migrate - existing providers, as it is mostly identical except for the explicit - versioning. - - Besides having explicitly versioned abstract classes the key changes for - the new interface are that the :class:`~qiskit.providers.BackendV1` - method :meth:`~qiskit.providers.BackendV1.run` can now - take a :class:`~qiskit.circuits.QuantumCircuit` or - :class:`~qiskit.pulse.Schedule` object as inputs instead of ``Qobj`` - objects. To go along with that options are now part of a backend class - so that users can configure run time options when running with a circuit. - The final change is that :class:`qiskit.providers.JobV1` can now be - synchronous or asynchronous, the exact configuration and method for - configuring this is up to the provider, but there are interface hook - points to make it explicit which execution model a job is running under - in the ``JobV1`` abstract class. - -- A new kwarg, ``inplace``, has been added to the function - :func:`qiskit.result.marginal_counts`. This kwarg is used to control whether - the contents are marginalized in place or a new copy is returned, for - :class:`~qiskit.result.Result` object input. This parameter does not have - any effect for an input ``dict`` or :class:`~qiskit.result.Counts` object. - -- An initial version of a classical function compiler, - :mod:`qiskit.circuit.classicalfunction`, has been added. This - enables compiling typed python functions (operating only on bits of type - ``Int1`` at the moment) into :class:`~qiskit.circuit.QuantumCircuit` - objects. For example: - - .. code-block:: python - - from qiskit.circuit import classical_function, Int1 - - @classical_function - def grover_oracle(a: Int1, b: Int1, c: Int1, d: Int1) -> Int1: - x = not a and b - y = d and not c - z = not x or y - return z - - quantum_circuit = grover_oracle.synth() - quantum_circuit.draw() - - The parameter ``registerless=False`` in the - :class:`qiskit.circuit.classicalfunction.ClassicalFunction` method - :meth:`~qiskit.circuit.classicalfunction.ClassicalFunction.synth` creates a - circuit with registers refering to the parameter names. For example: - - .. code-block:: python - - quantum_circuit = grover_oracle.synth(registerless=False) - quantum_circuit.draw() - - A decorated classical function can be used the same way as any other - quantum gate when appending it to a circuit. - - .. code-block:: python - - circuit = QuantumCircuit(5) - circuit.append(grover_oracle, range(5)) - circuit.draw() - - The ``GROVER_ORACLE`` gate is synthesized when its decomposition is required. - - .. code-block:: python - - circuit.decompose().draw() - - The feature requires ``tweedledum``, a library for synthesizing quantum - circuits, that can be installed via pip with ``pip install tweedledum``. - -- A new class :class:`qiskit.circuit.Delay` for representing a delay - instruction in a circuit has been added. A new method - :meth:`~qiskit.circuit.QuantumCircuit.delay` is now available for easily - appending delays to circuits. This makes it possible to describe - timing-sensitive experiments (e.g. T1/T2 experiment) in the circuit level. - - .. code-block:: python - - from qiskit import QuantumCircuit - - qc = QuantumCircuit(1, 1) - qc.delay(500, 0, unit='ns') - qc.measure(0, 0) - - qc.draw() - -- A new argument ``scheduling_method`` for - :func:`qiskit.compiler.transpile` has been added. It is required when - transpiling circuits with delays. If ``scheduling_method`` is specified, - the transpiler returns a scheduled circuit such that all idle times in it - are padded with delays (i.e. start time of each instruction is uniquely - determined). This makes it possible to see how scheduled instructions - (gates) look in the circuit level. - - .. code-block:: python - - from qiskit import QuantumCircuit, transpile - from qiskit.test.mock.backends import FakeAthens - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - - scheduled_circuit = transpile(qc, backend=FakeAthens(), scheduling_method="alap") - print("Duration in dt:", scheduled_circuit.duration) - scheduled_circuit.draw(idle_wires=False) - - See also :func:`~qiskit.visualization.timeline_drawer` for the best visualization - of scheduled circuits. - -- A new fuction :func:`qiskit.compiler.sequence` has been also added so that - we can convert a scheduled circuit into a :class:`~qiskit.pulse.Schedule` - to make it executable on a pulse-enabled backend. - - .. code-block:: python - - from qiskit.compiler import sequence - - sched = sequence(scheduled_circuit, pulse_enabled_backend) - -- The :func:`~qiskit.compiler.schedule` has been updated so that it can - schedule circuits with delays. Now there are two paths to schedule a - circuit with delay: - - .. code-block:: python - - qc = QuantumCircuit(1, 1) - qc.h(0) - qc.delay(500, 0, unit='ns') - qc.h(0) - qc.measure(0, 0) - - sched_path1 = schedule(qc.decompose(), backend) - sched_path2 = sequence(transpile(qc, backend, scheduling_method='alap'), backend) - assert pad(sched_path1) == sched_path2 - - Refer to the release notes and documentation for - :func:`~qiskit.compiler.transpile` and :func:`~qiskit.compiler.sequence` - for the details on the other path. - -- Added the :class:`~qiskit.circuit.library.GroverOperator` to the circuit - library (:mod:`qiskit.circuit.library`) to construct the Grover operator - used in Grover's search algorithm and Quantum Amplitude - Amplification/Estimation. Provided with an oracle in form of a circuit, - ``GroverOperator`` creates the textbook Grover operator. To generalize - this for amplitude amplification and use a generic operator instead of - Hadamard gates as state preparation, the ``state_in`` argument can be - used. - -- The :class:`~qiskit.pulse.InstructionScheduleMap` methods - :meth:`~qiskit.pulse.InstructionScheduleMap.get` and - :meth:`~qiskit.pulse.InstructionScheduleMap.pop` methods now take - :class:`~qiskit.circuit.ParameterExpression` instances - in addition to numerical values for schedule generator parameters. If the - generator is a function, expressions may be bound before or within the - function call. If the generator is a - :class:`~qiskit.pulse.ParametrizedSchedule`, expressions must be - bound before the schedule itself is bound/called. - -- A new class :class:`~qiskit.circuit.library.LinearAmplitudeFunction` was - added to the circuit library (:mod:`qiskit.circuit.library`) for mapping - (piecewise) linear functions on qubit amplitudes, - - .. math:: - - F|x\rangle |0\rangle = \sqrt{1 - f(x)}|x\rangle |0\rangle + \sqrt{f(x)}|x\rangle |1\rangle - - - The mapping is based on a controlled Pauli Y-rotations and - a Taylor approximation, as described in https://arxiv.org/abs/1806.06893. - This circuit can be used to compute expectation values of linear - functions using the quantum amplitude estimation algorithm. - -- The new jupyter magic ``monospaced_output`` has been added to the - :mod:`qiskit.tools.jupyter` module. This magic sets the Jupyter notebook - output font to "Courier New", when possible. When used this fonts returns - text circuit drawings that are better aligned. - - .. code-block:: python - - import qiskit.tools.jupyter - %monospaced_output - -- A new transpiler pass, - :class:`~qiskit.transpiler.passes.Optimize1qGatesDecomposition`, - has been added. This transpiler pass is an alternative to the existing - :class:`~qiskit.transpiler.passes.Optimize1qGates` that uses the - :class:`~qiskit.quantum_info.OneQubitEulerDecomposer` class to decompose - and simplify a chain of single qubit gates. This method is compatible with - any basis set, while :class:`~qiskit.transpiler.passes.Optimize1qGates` - only works for u1, u2, and u3. The default pass managers for - ``optimization_level`` 1, 2, and 3 have been updated to use this new pass - if the basis set doesn't include u1, u2, or u3. - -- The :class:`~qiskit.quantum_info.OneQubitEulerDecomposer` now supports - two new basis, ``'PSX'`` and ``'U'``. These can be specified with the - ``basis`` kwarg on the constructor. This will decompose the matrix into a - circuit using :class:`~qiskit.circuit.library.PGate` and - :class:`~qiskit.circuit.library.SXGate` for ``'PSX'``, and - :class:`~qiskit.circuit.library.UGate` for ``'U'``. - -- A new method :meth:`~qiskit.transpiler.PassManager.remove` has been added - to the :class:`qiskit.transpiler.PassManager` class. This method enables - removing a pass from a :class:`~qiskit.transpiler.PassManager` instance. - It works on indexes, similar to - :meth:`~qiskit.transpiler.PassManager.replace`. For example, to - remove the :class:`~qiskit.transpiler.passes.RemoveResetInZeroState` pass - from the pass manager used at optimization level 1: - - .. code-block:: python - - from qiskit.transpiler.preset_passmanagers import level_1_pass_manager - from qiskit.transpiler.passmanager_config import PassManagerConfig - - pm = level_1_pass_manager(PassManagerConfig()) - pm.draw() - - .. code-block:: - - [0] FlowLinear: UnrollCustomDefinitions, BasisTranslator - [1] FlowLinear: RemoveResetInZeroState - [2] DoWhile: Depth, FixedPoint, Optimize1qGates, CXCancellation - - The stage ``[1]`` with ``RemoveResetInZeroState`` can be removed like this: - - .. code-block:: python - - pass_manager.remove(1) - pass_manager.draw() - - .. code-block:: - - [0] FlowLinear: UnrollCustomDefinitions, BasisTranslator - [1] DoWhile: Depth, FixedPoint, Optimize1qGates, CXCancellation - -- Several classes to load probability distributions into qubit amplitudes; - :class:`~qiskit.circuit.library.UniformDistribution`, - :class:`~qiskit.circuit.library.NormalDistribution`, and - :class:`~qiskit.circuit.library.LogNormalDistribution` were added to the - circuit library (:mod:`qiskit.circuit.library`). The normal and - log-normal distribution support both univariate and multivariate - distributions. These circuits are central to applications in finance - where quantum amplitude estimation is used. - -- Support for pulse gates has been added to the - :class:`~qiskit.circuit.QuantumCircuit` class. This enables a - :class:`~qiskit.circuit.QuantumCircuit` to override (for basis gates) or - specify (for standard and custom gates) a definition of a - :class:`~qiskit.circuit.Gate` operation in terms of time-ordered signals - across hardware channels. In other words, it enables the option to provide - pulse-level custom gate calibrations. - - The circuits are built exactly as before. For example:: - - from qiskit import pulse - from qiskit.circuit import QuantumCircuit, Gate - - class RxGate(Gate): - def __init__(self, theta): - super().__init__('rxtheta', 1, [theta]) - - circ = QuantumCircuit(1) - circ.h(0) - circ.append(RxGate(3.14), [0]) - - Then, the calibration for the gate can be registered using the - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.add_calibration` which takes a - :class:`~qiskit.pulse.Schedule` definition as well as the qubits and - parameters that it is defined for:: - - # Define the gate implementation as a schedule - with pulse.build() as custom_h_schedule: - pulse.play(pulse.library.Drag(...), pulse.DriveChannel(0)) - - with pulse.build() as q1_x180: - pulse.play(pulse.library.Gaussian(...), pulse.DriveChannel(1)) - - # Register the schedule to the gate - circ.add_calibration('h', [0], custom_h_schedule) # or gate.name string to register - circ.add_calibration(RxGate(3.14), [0], q1_x180) # Can accept gate - - Previously, this functionality could only be used through complete Pulse - Schedules. Additionally, circuits can now be submitted to backends with - your custom definitions (dependent on backend support). - - Circuits with pulse gates can still be lowered to a - :class:`~qiskit.pulse.Schedule` by using the - :func:`~qiskit.compiler.schedule` function. - - The calibrated gate can also be transpiled using the regular transpilation - process:: - - transpiled_circuit = transpile(circ, backend) - - The transpiled circuit will leave the calibrated gates on the same qubit as - the original circuit and will not unroll them to the basis gates. - -- Support for disassembly of :class:`~qiskit.qobj.PulseQobj` objects has - been added to the :func:`qiskit.assembler.disassemble` function. - For example: - - .. code-block:: - - from qiskit import pulse - from qiskit.assembler.disassemble import disassemble - from qiskit.compiler.assemble import assemble - from qiskit.test.mock import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - d0 = pulse.DriveChannel(0) - d1 = pulse.DriveChannel(1) - with pulse.build(backend) as sched: - with pulse.align_right(): - pulse.play(pulse.library.Constant(10, 1.0), d0) - pulse.shift_phase(3.11, d0) - pulse.measure_all() - - qobj = assemble(sched, backend=backend, shots=512) - scheds, run_config, header = disassemble(qobj) - -- A new kwarg, ``coord_type`` has been added to - :func:`qiskit.visualization.plot_bloch_vector`. This kwarg enables - changing the coordinate system used for the input parameter that - describes the positioning of the vector on the Bloch sphere in the - generated visualization. There are 2 supported values for this new kwarg, - ``'cartesian'`` (the default value) and ``'spherical'``. If the - ``coord_type`` kwarg is set to ``'spherical'`` the list of parameters - taken in are of the form ``[r, theta, phi]`` where ``r`` is the - radius, ``theta`` is the inclination from +z direction, and ``phi`` is - the azimuth from +x direction. For example: - - .. code-block:: python - - from numpy import pi - - from qiskit.visualization import plot_bloch_vector - - x = 0 - y = 0 - z = 1 - r = 1 - theta = pi - phi = 0 - - - # Cartesian coordinates, where (x,y,z) are cartesian coordinates - # for bloch vector - plot_bloch_vector([x,y,z]) - - .. code-block:: python - - plot_bloch_vector([x,y,z], coord_type="cartesian") # Same as line above - - .. code-block:: python - - # Spherical coordinates, where (r,theta,phi) are spherical coordinates - # for bloch vector - plot_bloch_vector([r, theta, phi], coord_type="spherical") - -- Pulse :py:class:`~qiskit.pulse.Schedule` objects now support - using :py:class:`~qiskit.circuit.ParameterExpression` objects - for parameters. - - For example:: - - from qiskit.circuit import Parameter - from qiskit import pulse - - alpha = Parameter('⍺') - phi = Parameter('ϕ') - qubit = Parameter('q') - amp = Parameter('amp') - - schedule = pulse.Schedule() - schedule += SetFrequency(alpha, DriveChannel(qubit)) - schedule += ShiftPhase(phi, DriveChannel(qubit)) - schedule += Play(Gaussian(duration=128, sigma=4, amp=amp), - DriveChannel(qubit)) - schedule += ShiftPhase(-phi, DriveChannel(qubit)) - - Parameter assignment is done via the - :meth:`~qiskit.pulse.Schedule.assign_parameters` method:: - - schedule.assign_parameters({alpha: 4.5e9, phi: 1.57, - qubit: 0, amp: 0.2}) - - Expressions and partial assignment also work, such as:: - - beta = Parameter('b') - schedule += SetFrequency(alpha + beta, DriveChannel(0)) - schedule.assign_parameters({alpha: 4.5e9}) - schedule.assign_parameters({beta: phi / 6.28}) - -- A new visualization function :func:`~qiskit.visualization.timeline_drawer` - was added to the :mod:`qiskit.visualization` module. - - For example: - - .. code-block:: python - - from qiskit.visualization import timeline_drawer - from qiskit import QuantumCircuit, transpile - from qiskit.test.mock import FakeAthens - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0,1) - timeline_drawer(transpile(qc, FakeAthens(), scheduling_method='alap')) - - -.. _Release Notes_0.16.0_Upgrade Notes: - -Upgrade Notes -------------- - -- Type checking for the ``params`` kwarg of the constructor for the - :class:`~qiskit.circuit.Gate` class and its subclasses has been changed. - Previously all :class:`~qiskit.circuit.Gate` parameters had to be - in a set of allowed types defined in the - :class:`~qiskit.circuit.Instruction` class. Now a new method, - :meth:`~qiskit.circuit.Gate.validate_parameter` is used to determine - if a parameter type is valid or not. The definition of this method in - a subclass will take priority over its parent. For example, - :class:`~qiskit.extensions.UnitaryGate` accepts a parameter of the type - ``numpy.ndarray`` and defines a custom - :meth:`~qiskit.extensionst.UnitaryGate.validate_parameter` method that - returns the parameter if it's an ``numpy.ndarray``. This takes priority - over the function defined in its parent class :class:`~qiskit.circuit.Gate`. - If :class:`~qiskit.extensions.UnitaryGate` were to be used as parent - for a new class, this ``validate_parameter`` method would be used unless - the new child class defines its own method. - -- The previously deprecated methods, arguments, and properties named - ``n_qubits`` and ``numberofqubits`` have been removed. These were - deprecated in the 0.13.0 release. The full set of changes are: - - .. list-table:: - :header-rows: 1 - - * - Class - - Old - - New - * - :class:`~qiskit.circuit.QuantumCircuit` - - ``n_qubits`` - - :class:`~qiskit.circuit.QuantumCircuit.num_qubits` - * - :class:`~qiskit.quantum_info.Pauli` - - ``numberofqubits`` - - :attr:`~qiskit.quantum_info.Pauli.num_qubits` - - .. list-table:: - :header-rows: 1 - - * - Function - - Old Argument - - New Argument - * - :func:`qiskit.circuit.random.random_circuit` - - ``n_qubits`` - - ``num_qubits`` - * - :class:`qiskit.circuit.library.MSGate` - - ``n_qubits`` - - ``num_qubits`` - -- Inserting a parameterized :class:`~qiskit.circuit.Gate` instance into - a :class:`~qiskit.circuit.QuantumCircuit` now creates a copy of that - gate which is used in the circuit. If changes are made to the instance - inserted into the circuit it will no longer be reflected in the gate in - the circuit. This change was made to fix an issue when inserting a single - parameterized :class:`~qiskit.circuit.Gate` object into multiple circuits. - -- The function :func:`qiskit.result.marginal_counts` now, by default, - does not modify the :class:`qiskit.result.Result` instance - parameter. Previously, the ``Result`` object was always modified in place. - A new kwarg ``inplace`` has been added - :func:`~qiskit.result.marginal_counts` which enables using the previous - behavior when ``inplace=True`` is set. - -- The :class:`~qiskit.circuit.library.U3Gate` definition has been changed to - be in terms of the :class:`~qiskit.circuit.library.UGate` class. The - :class:`~qiskit.circuit.library.UGate` class has no definition. It is - therefore not possible to unroll **every** circuit in terms of U3 - and CX anymore. Instead, U and CX can be used for **every** circuit. - -- The deprecated support for running Qiskit Terra with Python 3.5 has been - removed. To use Qiskit Terra from this release onward you will now need to - use at least Python 3.6. If you are using Python 3.5 the last version which - will work is Qiskit Terra 0.15.2. - -- In the :class:`~qiskit.providers.models.PulseBackendConfiguration` - in the ``hamiltonian`` attributes the ``vars`` field is now returned - in a unit of Hz instead of the previously used GHz. This change was made - to be consistent with the units used with the other attributes in the - class. - -- The previously deprecated support for passing in a dictionary as the - first positional argument to :class:`~qiskit.dagcircuit.DAGNode` constructor - has been removed. Using a dictonary for the first positional argument - was deprecated in the 0.13.0 release. To create a - :class:`~qiskit.dagcircuit.DAGNode` object now you should directly - pass the attributes as kwargs on the constructor. - -- The keyword arguments for the circuit gate methods (for example: - :class:`qiskit.circuit.QuantumCircuit.cx`) ``q``, ``ctl*``, and - ``tgt*``, which were deprecated in the 0.12.0 release, have been removed. - Instead, only ``qubit``, ``control_qubit*`` and ``target_qubit*`` can be - used as named arguments for these methods. - -- The previously deprecated module ``qiskit.extensions.standard`` has been - removed. This module has been deprecated since the 0.14.0 release. - The :mod:`qiskit.circuit.library` can be used instead. - Additionally, all the gate classes previously in - ``qiskit.extensions.standard`` are still importable from - :mod:`qiskit.extensions`. - -- The previously deprecated gates in the module - ``qiskit.extensions.quantum_initializer``: - ``DiagGate``, `UCG``, ``UCPauliRotGate``, ``UCRot``, ``UCRXGate``, ``UCX``, - ``UCRYGate``, ``UCY``, ``UCRZGate``, ``UCZ`` have been removed. These were - all deprecated in the 0.14.0 release and have alternatives available in - the circuit library (:mod:`qiskit.circuit.library`). - -- The previously deprecated :class:`qiskit.circuit.QuantumCircuit` gate method - :meth:`~qiskit.circuit.QuantumCircuit.iden` has been removed. This was - deprecated in the 0.13.0 release and - :meth:`~qiskit.circuit.QuantumCircuit.i` or - :meth:`~qiskit.circuit.QuantumCircuit.id` can be used instead. - - -Deprecation Notes ------------------ - -- The use of a ``numpy.ndarray`` for a parameter in the ``params`` kwarg - for the constructor of the :class:`~qiskit.circuit.Gate` class and - subclasses has been deprecated and will be removed in future releases. This - was done as part of the refactoring of how ``parms`` type checking is - handled for the :class:`~qiskit.circuit.Gate` class. If you have a custom - gate class which is a subclass of :class:`~qiskit.circuit.Gate` directly - (or via a different parent in the hierarchy) that accepts an ``ndarray`` - parameter, you should define a custom - :meth:`~qiskit.circuit.Gate.validate_parameter` method for your class - that will return the allowed parameter type. For example:: - - def validate_parameter(self, parameter): - """Custom gate parameter has to be an ndarray.""" - if isinstance(parameter, numpy.ndarray): - return parameter - else: - raise CircuitError("invalid param type {0} in gate " - "{1}".format(type(parameter), self.name)) - -- The - :attr:`~qiskit.circuit.library.PiecewiseLinearPauliRotations.num_ancilla_qubits` - property of the :class:`~qiskit.circuit.library.PiecewiseLinearPauliRotations` - and :class:`~qiskit.circuit.library.PolynomialPauliRotations` classes has been - deprecated and will be removed in a future release. Instead the property - :attr:`~qiskit.circuit.library.PolynomialPauliRotations.num_ancillas` should - be used instead. This was done to make it consistent with the - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.num_ancillas`. - -- The :class:`qiskit.circuit.library.MSGate` class has been - deprecated, but will remain in place to allow loading of old jobs. It has been replaced - with the :class:`qiskit.circuit.library.GMS` class which should be used - instead. - -- The :class:`~qiskit.transpiler.passes.MSBasisDecomposer` transpiler pass - has been deprecated and will be removed in a future release. - The :class:`qiskit.transpiler.passes.BasisTranslator` pass can be used - instead. - -- The :class:`~qiskit.circuit.QuantumCircuit` methods ``u1``, ``u2`` and - ``u3`` are now deprecated. Instead the following replacements can be - used. - - .. code-block:: - - u1(theta) = p(theta) = u(0, 0, theta) - u2(phi, lam) = u(pi/2, phi, lam) = p(pi/2 + phi) sx p(pi/2 lam) - u3(theta, phi, lam) = u(theta, phi, lam) = p(phi + pi) sx p(theta + pi) sx p(lam) - - The gate classes themselves, :class:`~qiskit.circuit.library.U1Gate`, - :class:`~qiskit.circuit.library.U2Gate` and :class:`~qiskit.circuit.library.U3Gate` - remain, to allow loading of old jobs. - - -.. _Release Notes_0.16.0_Bug Fixes: - -Bug Fixes ---------- - -- The :class:`~qiskit.result.Result` class's methods - :meth:`~qiskit.result.Result.data`, :meth:`~qiskit.result.Result.get_memory`, - :meth:`~qiskit.result.Result.get_counts`, :meth:`~qiskit.result.Result.get_unitary`, - and :meth:`~qiskit.result.Result.get_statevector ` will now emit a warning - when the ``experiment`` kwarg is specified for attempting to fetch - results using either a :class:`~qiskit.circuit.QuantumCircuit` or - :class:`~qiskit.pulse.Schedule` instance, when more than one entry matching - the instance name is present in the ``Result`` object. Note that only the - first entry matching this name will be returned. Fixes - `#3207 `__ - -- The :class:`qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.append` can now be used to insert one - parameterized gate instance into multiple circuits. This fixes a previous - issue where inserting a single parameterized - :class:`~qiskit.circuit.Gate` object into multiple circuits would - cause failures when one circuit had a parameter assigned. - Fixes `#4697 `__ - -- Previously the :func:`qiskit.execute.execute` function would incorrectly - disallow both the ``backend`` and ``pass_manager`` kwargs to be - specified at the same time. This has been fixed so that both - ``backend`` and ``pass_manager`` can be used together on calls to - :func:`~qiskit.execute.execute`. - Fixes `#5037 `__ - -- The :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.unitary` method has been fixed - to accept a single integer for the ``qarg`` argument (when adding a - 1-qubit unitary). The allowed types for the ``qargs`` argument are now - ``int``, :class:`~qiskit.circuit.Qubit`, or a list of integers. - Fixes `#4944 `__ - -- Previously, calling :meth:`~qiskit.circuit.library.BlueprintCircuit.inverse` - on a :class:`~qiskit.circuit.library.BlueprintCircuit` object - could fail if its internal data property was not yet populated. This has - been fixed so that the calling - :meth:`~qiskit.circuit.library.BlueprintCircuit.inverse` will populate - the internal data before generating the inverse of the circuit. - Fixes `#5140 `__ - -- Fixed an issue when creating a :class:`qiskit.result.Counts` object from an - empty data dictionary. Now this will create an empty - :class:`~qiskit.result.Counts` object. The - :meth:`~qiskit.result.Counts.most_frequent` method is also updated to raise - a more descriptive exception when the object is empty. Fixes - `#5017 `__ - -- Fixes a bug where setting ``ctrl_state`` of a - :class:`~qiskit.extensions.UnitaryGate` would be applied twice; once - in the creation of the matrix for the controlled unitary and again - when calling the :meth:`~qiskit.circuit.ControlledGate.definition` method of - the :class:`qiskit.circuit.ControlledGate` class. This would give the - appearence that setting ``ctrl_state`` had no effect. - -- Previously the :class:`~qiskit.circuit.ControlledGate` method - :meth:`~qiskit.circuit.ControlledGate.inverse` would not preserve the - ``ctrl_state`` parameter in some cases. This has been fixed so that - calling :meth:`~qiskit.circuit.ControlledGate.inverse` will preserve - the value ``ctrl_state`` in its output. - -- Fixed a bug in the ``mpl`` output backend of the circuit drawer - :meth:`qiskit.circuit.QuantumCircuit.draw` and - :func:`qiskit.visualization.circuit_drawer` that would - cause the drawer to fail if the ``style`` kwarg was set to a string. - The correct behavior would be to treat that string as a path to - a JSON file containing the style sheet for the visualization. This has - been fixed, and warnings are raised if the JSON file for the style - sheet can't be loaded. - -- Fixed an error where loading a QASM file via - :meth:`~qiskit.circuit.QuantumCircuit.from_qasm_file` or - :meth:`~qiskit.circuit.QuantumCircuit.from_qasm_str` would fail - if a ``u``, ``phase(p)``, ``sx``, or ``sxdg`` gate were present in - the QASM file. - Fixes `#5156 `__ - -- Fixed a bug that would potentially cause registers to be mismapped when - unrolling/decomposing a gate defined with only one 2-qubit operation. - -Aer 0.7.0 -========= - -.. _Release Notes_Aer_0.7.0_Prelude: - -Prelude -------- - -This 0.7.0 release includes numerous performance improvements and significant -enhancements to the simulator interface, and drops support for Python 3.5. The -main interface changes are configurable simulator backends, and constructing -preconfigured simulators from IBMQ backends. Noise model an basis gate support -has also been extended for most of the Qiskit circuit library standard gates, -including new support for 1 and 2-qubit rotation gates. Performance -improvements include adding SIMD support to the density matrix and unitary -simulation methods, reducing the used memory and improving the performance of -circuits using statevector and density matrix snapshots, and adding support -for Kraus instructions to the gate fusion circuit optimization for greatly -improving the performance of noisy statevector simulations. - -.. _Release Notes_Aer_0.7.0_New Features: - -New Features ------------- - -- Adds basis gate support for the :class:`qiskit.circuit.Delay` - instruction to the :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and - :class:`~qiskit.providers.aer.QasmSimulator`. - Note that this gate is treated as an identity gate during simulation - and the delay length parameter is ignored. - -- Adds basis gate support for the single-qubit gate - :class:`qiskit.circuit.library.UGate` to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"``, ``"density_matrix"``, ``"matrix_product_state"``, - and ``"extended_stabilizer"`` methods of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Adds basis gate support for the phase gate - :class:`qiskit.circuit.library.PhaseGate` to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"``, ``"density_matrix"``, ``"matrix_product_state"``, - and ``"extended_stabilizer"`` methods of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Adds basis gate support for the controlled-phase gate - :class:`qiskit.circuit.library.CPhaseGate` to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"``, ``"density_matrix"``, and - ``"matrix_product_state"`` methods of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Adds support for the multi-controlled phase gate - :class:`qiskit.circuit.library.MCPhaseGate` to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"`` method of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Adds support for the :math:`\sqrt(X)` gate - :class:`qiskit.circuit.library.SXGate` to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Adds support for 1 and 2-qubit Qiskit circuit library rotation gates - :class:`~qiskit.circuit.library.RXGate`, :class:`~qiskit.circuit.library.RYGate`, - :class:`~qiskit.circuit.library.RZGate`, :class:`~qiskit.circuit.library.RGate`, - :class:`~qiskit.circuit.library.RXXGate`, :class:`~qiskit.circuit.library.RYYGate`, - :class:`~qiskit.circuit.library.RZZGate`, :class:`~qiskit.circuit.library.RZXGate` - to the :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"`` and ``"density_matrix"`` methods of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Adds support for multi-controlled rotation gates ``"mcr"``, ``"mcrx"``, - ``"mcry"``, ``"mcrz"`` - to the :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"`` method of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Make simulator backends configurable. This allows setting persistant options - such as simulation method and noise model for each simulator backend object. - - The :class:`~qiskit.providers.aer.QasmSimulator` and - :class:`~qiskit.providers.aer.PulseSimulator` can also be configured from - an :class:`~qiskit.providers.ibmq.IBMQBackend` backend object using the - `:meth:`~qiskit.providers.aer.QasmSimulator.from_backend` method. - For the :class:`~qiskit.providers.aer.QasmSimulator` this will configure the coupling map, - basis gates, and basic device noise model based on the backend configuration and - properties. For the :class:`~qiskit.providers.aer.PulseSimulator` the system model - and defaults will be configured automatically from the backend configuration, properties and - defaults. - - For example a noisy density matrix simulator backend can be constructed as - ``QasmSimulator(method='density_matrix', noise_model=noise_model)``, or an ideal - matrix product state simulator as ``QasmSimulator(method='matrix_product_state')``. - - A benefit is that a :class:`~qiskit.providers.aer.PulseSimulator` instance configured from - a backend better serves as a drop-in replacement to the original backend, making it easier to - swap in and out a simulator and real backend, e.g. when testing code on a simulator before - using a real backend. - For example, in the following code-block, the :class:`~qiskit.providers.aer.PulseSimulator` is - instantiated from the ``FakeArmonk()`` backend. All configuration and default data is copied - into the simulator instance, and so when it is passed as an argument to ``assemble``, - it behaves as if the original backend was supplied (e.g. defaults from ``FakeArmonk`` will be - present and used by ``assemble``). - - .. code-block:: python - - armonk_sim = qiskit.providers.aer.PulseSimulator.from_backend(FakeArmonk()) - pulse_qobj = assemble(schedules, backend=armonk_sim) - armonk_sim.run(pulse_qobj) - - While the above example is small, the demonstrated 'drop-in replacement' behavior should - greatly improve the usability in more complicated work-flows, e.g. when calibration experiments - are constructed using backend attributes. - -- Adds support for qobj global phase to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and statevector - methods of the :class:`~qiskit.providers.aer.QasmSimulator`. - -- Improves general noisy statevector simulation performance by adding a Kraus - method to the gate fusion circuit optimization that allows applying gate - fusion to noisy statevector simulations with general Kraus noise. - -- Use move semantics for statevector and density matrix snapshots for the - `"statevector"` and `"density_matrix"` methods of the - :class:`~qiskit.providers.aer.QasmSimulator` if they are the final - instruction in a circuit. This reduces the memory usage of the - simulator improves the performance by avoiding copying a large array in - the results. - -- Adds support for general Kraus - :class:`~qiskit.providers.aer.noise.QauntumError` gate errors in the - :class:`~qiskit.providers.aer.noise.NoiseModel` to the - ``"matrix_product_state"`` method of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Adds support for density matrix snapshot instruction - :class:`qiskit.providers.aer.extensions.SnapshotDensityMatrix` to the - ``"matrix_product_state"`` method of the - :class:`~qiskit.providers.aer.QasmSimulator`. - -- Extends the SIMD vectorization of the statevector simulation method to the - unitary matrix, superoperator matrix, and density matrix simulation methods. - This gives roughtly a 2x performance increase general simulation using the - :class:`~qiskit.providers.aer.UnitarySimulator`, the ``"density_matrix"`` - method of the :class:`~qiskit.providers.aer.QasmSimulator`, gate - fusion, and noise simulation. - -- Adds a custom vector class to C++ code that has better integration with - Pybind11. This haves the memory requirement of the - :class:`~qiskit.providers.aer.StatevectorSimulator` by avoiding an - memory copy during Python binding of the final simulator state. - - -.. _Release Notes_Aer_0.7.0_Upgrade Notes: - -Upgrade Notes -------------- - -- AER now uses Lapack to perform some matrix related computations. - It uses the Lapack library bundled with OpenBlas (already available - in Linux and Macos typical OpenBlas dsitributions; Windows version - distributed with AER) or with the accelerate framework in MacOS. - -- The deprecated support for running qiskit-aer with Python 3.5 has - been removed. To use qiskit-aer >=0.7.0 you will now need at - least Python 3.6. If you are using Python 3.5 the last version which will - work is qiskit-aer 0.6.x. - -- Updates gate fusion default thresholds so that gate fusion will be applied - to circuits with of more than 14 qubits for statevector simulations on the - :class:`~qiskit.providers.aer.StatevectorSimulator` and - :class:`~qiskit.providers.aer.QasmSimulator`. - - For the ``"density_matrix"`` - method of the :class:`~qiskit.providers.aer.QasmSimulator` and for the - :class:`~qiskit.providers.aer.UnitarySimulator` gate fusion will be applied - to circuits with more than 7 qubits. - - Custom qubit threshold values can be set using the ``fusion_threshold`` - backend option ie ``backend.set_options(fusion_threshold=10)`` - -- Changes ``fusion_threshold`` backend option to apply fusion when the - number of qubits is above the threshold, not equal or above the threshold, - to match the behavior of the OpenMP qubit threshold parameter. - - -.. _Release Notes_Aer_0.7.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- :meth:`qiskit.providers.aer.noise.NoiseModel.set_x90_single_qubit_gates` has - been deprecated as unrolling to custom basis gates has been added to the - qiskit transpiler. The correct way to use an X90 based noise model is to - define noise on the Sqrt(X) ``"sx"`` or ``"rx"`` gate and one of the single-qubit - phase gates ``"u1"``, ``"rx"``, or ``"p"`` in the noise model. - -- The ``variance`` kwarg of Snapshot instructions has been deprecated. This - function computed the sample variance in the snapshot due to noise model - sampling, not the variance due to measurement statistics so was often - being used incorrectly. If noise modeling variance is required single shot - snapshots should be used so variance can be computed manually in - post-processing. - - -.. _Release Notes_Aer_0.7.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixes bug in the :class:`~qiskit.providers.aer.StatevectorSimulator` that - caused it to always run as CPU with double-precision without SIMD/AVX2 - support even on systems with AVX2, or when single-precision or the GPU - method was specified in the backend options. - -- Fixes some for-loops in C++ code that were iterating over copies - rather than references of container elements. - -- Fixes a bug where snapshot data was always copied from C++ to Python rather - than moved where possible. This will halve memory usage and improve simulation - time when using large statevector or density matrix snapshots. - -- Fix `State::snapshot_pauli_expval` to return correct Y - expectation value in stabilizer simulator. Refer to - `#895 ` - for more details. - -- The controller_execute wrappers have been adjusted to be functors (objects) - rather than free functions. Among other things, this allows them to be used - in multiprocessing.pool.map calls. - -- Add missing available memory checks for the - :class:`~qiskit.providers.aer.StatevectorSimulator` and - :class:`~qiskit.providers.aer.UnitarySimulator`. This throws an exception if - the memory required to simulate the number of qubits in a circuit exceeds the - available memory of the system. - - -.. _Release Notes_Ignis_0.5.0: - -Ignis 0.5.0 -=========== - -.. _Release Notes_Ignis_0.5.0_Prelude: - -Prelude -------- - -This release includes a new module for expectation value measurement error -mitigation, improved plotting functionality for quantum volume experiments, -several bug fixes, and drops support for Python 3.5. - - -.. _Release Notes_Ignis_0.5.0_New Features: - -New Features ------------- - -- The :func:`qiskit.ignis.verification.randomized_benchmarking.randomized_benchmarking_seq` - function allows an optional input of gate objects as `interleaved_elem`. - In addition, the CNOT-Dihedral class - :class:`qiskit.ignis.verification.randomized_benchmarking.CNOTDihedral` - has a new method `to_instruction`, and the existing `from_circuit` method has - an optional input of an `Instruction` (in addition to `QuantumCircuit`). - -- The :class:`qiskit.ignis.verification.randomized_benchmarking.CNOTDihedral` - now contains the following new features. - Initialization from various types of objects: - `CNOTDihedral`, `ScalarOp`, `QuantumCircuit`, `Instruction` and `Pauli`. - Converting to a matrix using `to_matrix` and to an operator using `to_operator`. - Tensor product methods `tensor` and `expand`. - Calculation of the adjoint, conjugate and transpose using `conjugate`, `adjoint` - and `transpose` methods. - Verify that an element is CNOTDihedral using `is_cnotdihedral` method. - Decomposition method `to_circuit` of a CNOTDihedral element into a circuit - was extended to allow any number of qubits, based on the function - `decompose_cnotdihedral_general`. - -- Adds expectation value measurement error mitigation to the mitigation module. - This supports using *complete* N-qubit assignment matrix, single-qubit - *tensored* assignment matrix, or *continuous time Markov process (CTMP)* [1] - measurement error mitigation when computing expectation values of diagonal - operators from counts dictionaries. Expectation values are computed using - the using the :func:`qiskit.ignis.mitigation.expectation_value` function. - - Calibration circuits for calibrating a measurement error mitigator are - generated using the :func:`qiskit.ignis.mitigation.expval_meas_mitigator_circuits` - function, and the result fitted using the - :class:`qiskit.ignis.mitigation.ExpvalMeasMitigatorFitter` class. The - fitter returns a mitigator object can the be supplied as an argument to the - :func:`~qiskit.ignis.mitigation.expectation_value` function to apply mitigation. - - [1] S Bravyi, S Sheldon, A Kandala, DC Mckay, JM Gambetta, - *Mitigating measurement errors in multi-qubit experiments*, - arXiv:2006.14044 [quant-ph]. - - Example: - - The following example shows calibrating a 5-qubit expectation value - measurement error mitigator using the ``'tensored'`` method. - - .. code-block:: - - from qiskit import execute - from qiskit.test.mock import FakeVigo - import qiskit.ignis.mitigation as mit - - backend = FakeVigo() - num_qubits = backend.configuration().num_qubits - - # Generate calibration circuits - circuits, metadata = mit.expval_meas_mitigator_circuits( - num_qubits, method='tensored') - result = execute(circuits, backend, shots=8192).result() - - # Fit mitigator - mitigator = mit.ExpvalMeasMitigatorFitter(result, metadata).fit() - - # Plot fitted N-qubit assignment matrix - mitigator.plot_assignment_matrix() - - The following shows how to use the above mitigator to apply measurement - error mitigation to expectation value computations - - .. code-block:: - - from qiskit import QuantumCircuit - - # Test Circuit with expectation value -1. - qc = QuantumCircuit(num_qubits) - qc.x(range(num_qubits)) - qc.measure_all() - - # Execute - shots = 8192 - seed_simulator = 1999 - result = execute(qc, backend, shots=8192, seed_simulator=1999).result() - counts = result.get_counts(0) - - # Expectation value of Z^N without mitigation - expval_nomit, error_nomit = mit.expectation_value(counts) - print('Expval (no mitigation): {:.2f} \u00B1 {:.2f}'.format( - expval_nomit, error_nomit)) - - # Expectation value of Z^N with mitigation - expval_mit, error_mit = mit.expectation_value(counts, - meas_mitigator=mitigator) - print('Expval (with mitigation): {:.2f} \u00B1 {:.2f}'.format( - expval_mit, error_mit)) - - -- Adds Numba as an optional dependency. Numba is used to significantly increase - the performance of the :class:`qiskit.ignis.mitigation.CTMPExpvalMeasMitigator` - class used for expectation value measurement error mitigation with the CTMP - method. - - -- Add two methods to :class:`qiskit.ignis.verification.quantum_volume.QVFitter`. - - * :meth:`qiskit.ignis.verification.quantum_volume.QVFitter.calc_z_value` to - calculate z value in standard normal distribution using mean and standard - deviation sigma. If sigma = 0, it raises a warning and assigns a small - value (1e-10) for sigma so that the code still runs. - * :meth:`qiskit.ignis.verification.quantum_volume.QVFitter.calc_confidence_level` - to calculate confidence level using z value. - - -- Store confidence level even when hmean < 2/3 in - :meth:`qiskit.ignis.verification.quantum_volume.QVFitter.qv_success`. - -- Add explanations for how to calculate statistics based on binomial - distribution in - :meth:`qiskit.ignis.verification.quantum_volume.QVFitter.calc_statistics`. - -- The :class:`qiskit.ignis.verification.QVFitter` method - :meth:`~qiskit.ignis.verification.QVFitter.plot_qv_data` has been updated to return a - ``matplotlib.Figure`` object. Previously, it would not return anything. By returning a figure - this makes it easier to integrate the visualizations into a larger ``matplotlib`` workflow. - -- The error bars in the figure produced by the - :class:`qiskit.ignis.verification.QVFitter` method - :meth:`qiskit.ignis.verification.QVFitter.plot_qv_data` has been updated to represent - two-sigma confidence intervals. Previously, the error bars represent one-sigma confidence - intervals. The success criteria of Quantum Volume benchmarking requires heavy output - probability > 2/3 with one-sided two-sigma confidence (~97.7%). Changing error bars to - represent two-sigma confidence intervals allows easily identification of success in the - figure. - -- A new kwarg, ``figsize`` has been added to the - :class:`qiskit.ignis.verification.QVFitter` method - :meth:`qiskit.ignis.verification.QVFitter.plot_qv_data`. This kwarg takes in a tuple of the - form ``(x, y)`` where ``x`` and ``y`` are the dimension in inches to make the generated - plot. - -- The :meth:`qiskit.ignis.verification.quantum_volume.QVFitter.plot_hop_accumulative` method - has been added to plot heavy output probability (HOP) vs number of trials similar to - Figure 2a of Quantum Volume 64 paper (`arXiv:2008.08571 `_). - HOP of individual trials are plotted as scatters and cummulative HOP are plotted in red line. - Two-sigma confidence intervals are plotted as shaded area and 2/3 success threshold is plotted - as dashed line. - -- The :meth:`qiskit.ignis.verification.quantum_volume.QVFitter.plot_qv_trial` method - has been added to plot individual trials, leveraging on the - :meth:`qiskit.visualization.plot_histogram` method from Qiskit Terra. - Bitstring counts are plotted as overlapping histograms for ideal (hollow) and experimental - (filled) values. - Experimental heavy output probability are shown on the legend. - Median probability is plotted as red dashed line. - - -.. _Release Notes_Ignis_0.5.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The deprecated support for running qiskit-ignis with Python 3.5 has - been removed. To use qiskit-ignis >=0.5.0 you will now need at - least Python 3.6. If you are using Python 3.5 the last version which will - work is qiskit-ignis 0.4.x. - - -.. _Release Notes_Ignis_0.5.0_Bug Fixes: - -Bug Fixes ---------- - - -- Fixing a bug in the class - :class:`qiskit.ignis.verification.randomized_benchmarking.CNOTDihedral` - for elements with more than 5 quits. - -- Fix the confidence level threshold for - :meth:`qiskit.ignis.verification.quantum_volume.QVFitter.qv_success` to 0.977 - corresponding to z = 2 as defined by the QV paper Algorithm 1. - -- Fix a bug at - :func:`qiskit.ignis.verification.randomized_benchmarking.randomized_benchmarking_seq` - which caused all the subsystems with the same size in the given rb_pattern to - have the same gates when a 'rand_seed' parameter was given to the function. - -Aqua 0.8.0 -========== - -.. _Release Notes_Aqua_0.8.0_Prelude: - -Prelude -------- - -This release introduces an interface for running the available methods for -Bosonic problems. In particular we introduced a full interface for running -vibronic structure calculations. - -This release introduces an interface for excited states calculations. It is -now easier for the user to create a general excited states calculation. -This calculation is based on a Driver which provides the relevant information -about the molecule, a Transformation which provides the information about the -mapping of the problem into a qubit Hamiltonian, and finally a Solver. -The Solver is the specific way which the excited states calculation is done -(the algorithm). This structure follows the one of the ground state -calculations. The results are modified to take lists of expectation values -instead of a single one. The QEOM and NumpyEigensolver are adapted to the new -structure. A factory is introduced to run a numpy eigensolver with a specific -filter (to target states of specific symmetries). - -VQE expectation computation with Aer qasm_simulator now defaults to a -computation that has the expected shot noise behavior. - - -.. _Release Notes_Aqua_0.8.0_New Features: - -New Features ------------- - -- Introduced an option `warm_start` that should be used when tuning other options does not help. - When this option is enabled, a relaxed problem (all variables are continuous) is solved first - and the solution is used to initialize the state of the optimizer before it starts the - iterative process in the `solve` method. - -- The amplitude estimation algorithms now use ``QuantumCircuit`` objects as - inputs to specify the A- and Q operators. This change goes along with the - introduction of the ``GroverOperator`` in the circuit library, which allows - an intuitive and fast construction of different Q operators. - For example, a Bernoulli-experiment can now be constructed as - - .. code-block:: python - - import numpy as np - from qiskit import QuantumCircuit - from qiskit.aqua.algorithms import AmplitudeEstimation - - probability = 0.5 - angle = 2 * np.sqrt(np.arcsin(probability)) - a_operator = QuantumCircuit(1) - a_operator.ry(angle, 0) - - # construct directly - q_operator = QuantumCircuit(1) - q_operator.ry(2 * angle, 0) - - # construct via Grover operator - from qiskit.circuit.library import GroverOperator - oracle = QuantumCircuit(1) - oracle.z(0) # good state = the qubit is in state |1> - q_operator = GroverOperator(oracle, state_preparation=a_operator) - - # use default construction in QAE - q_operator = None - - ae = AmplitudeEstimation(a_operator, q_operator) - -- Add the possibility to compute Conditional Value at Risk (CVaR) expectation - values. - - Given a diagonal observable H, often corresponding to the objective function - of an optimization problem, we are often not as interested in minimizing the - average energy of our observed measurements. In this context, we are - satisfied if at least some of our measurements achieve low energy. (Note that - this is emphatically not the case for chemistry problems). - - To this end, one might consider using the best observed sample as a cost - function during variational optimization. The issue here, is that this can - result in a non-smooth optimization surface. To resolve this issue, we can - smooth the optimization surface by using not just the best observed sample, - but instead average over some fraction of best observed samples. This is - exactly what the CVaR estimator accomplishes [1]. - - Let :math:`\alpha` be a real number in :math:`[0,1]` which specifies the - fraction of best observed samples which are used to compute the objective - function. Observe that if :math:`\alpha = 1`, CVaR is equivalent to a - standard expectation value. Similarly, if :math:`\alpha = 0`, then CVaR - corresponds to using the best observed sample. Intermediate values of - :math:`\alpha` interpolate between these two objective functions. - - The functionality to use CVaR is included into the operator flow through a - new subclass of OperatorStateFn called CVaRMeasurement. This new StateFn - object is instantied in the same way as an OperatorMeasurement with the - exception that it also accepts an `alpha` parameter and that it automatically - enforces the `is_measurement` attribute to be True. Observe that it is - unclear what a CVaRStateFn would represent were it not a measurement. - - Examples:: - - qc = QuantumCircuit(1) - qc.h(0) - op = CVaRMeasurement(Z, alpha=0.5) @ CircuitStateFn(primitive=qc, coeff=1.0) - result = op.eval() - - - Similarly, an operator corresponding to a standard expectation value can be - converted into a CVaR expectation using the CVaRExpectation converter. - - Examples:: - - qc = QuantumCircuit(1) - qc.h(0) - op = ~StateFn(Z) @ CircuitStateFn(primitive=qc, coeff=1.0) - cvar_expecation = CVaRExpectation(alpha=0.1).convert(op) - result = cvar_expecation.eval() - - See [1] for additional details regarding this technique and it's empircal - performance. - - References: - - [1]: Barkoutsos, P. K., Nannicini, G., Robert, A., Tavernelli, I., and Woerner, S., - "Improving Variational Quantum Optimization using CVaR" - `arXiv:1907.04769 `_ - -- New interface ``Eigensolver`` for Eigensolver algorithms. - -- An interface for excited states calculation has been added to the chemistry module. - It is now easier for the user to create a general excited states calculation. - This calculation is based on a ``Driver`` which provides the relevant information - about the molecule, a ``Transformation`` which provides the information about the - mapping of the problem into a qubit Hamiltonian, and finally a Solver. - The Solver is the specific way which the excited states calculation is done - (the algorithm). This structure follows the one of the ground state calculations. - The results are modified to take lists of expectation values instead of a single one. - The ``QEOM`` and ``NumpyEigensolver`` are adapted to the new structure. - A factory is introduced to run a numpy eigensolver with a specific filter - (to target states of specific symmetries). - -- In addition to the workflows for solving Fermionic problems, interfaces for calculating - Bosonic ground and excited states have been added. In particular we introduced a full - interface for running vibronic structure calculations. - -- The ``OrbitalOptimizationVQE`` has been added as new ground state solver in the chemistry - module. This solver allows for the simulatneous optimization of the variational parameters - and the orbitals of the molecule. The algorithm is introduced in Sokolov et al., - The Journal of Chemical Physics 152 (12). - -- A new algorithm has been added: the Born Openheimer Potential Energy surface for the calculation - of potential energy surface along different degrees of freedom of the molecule. The algorithm - is called ``BOPESSampler``. It further provides functionalities of fitting the potential energy - surface to an analytic function of predefined potentials. - -- A feasibility check of the obtained solution has been added to all optimizers in the - optimization stack. This has been implemented by adding two new methods to ``QuadraticProgram``: - * ``get_feasibility_info(self, x: Union[List[float], np.ndarray])`` accepts an array and returns - whether this solution is feasible and a list of violated variables(violated bounds) and - a list of violated constraints. - * ``is_feasible(self, x: Union[List[float], np.ndarray])`` accepts an array and returns whether - this solution is feasible or not. - -- Add circuit-based versions of ``FixedIncomeExpectedValue``, ``EuropeanCallDelta``, - ``GaussianConditionalIndependenceModel`` and ``EuropeanCallExpectedValue`` to - ``qiskit.finance.applications``. - -- Gradient Framework. - :class:`qiskit.operators.gradients` - Given an operator that represents either a quantum state resp. an expectation - value, the gradient framework enables the evaluation of gradients, natural - gradients, Hessians, as well as the Quantum Fisher Information. - - Suppose a parameterized quantum state `|ψ(θ)〉 = V(θ)|ψ〉` with input state - `|ψ〉` and parametrized Ansatz `V(θ)`, and an Operator `O(ω)`. - - Gradients: We want to compute :math:`d⟨ψ(θ)|O(ω)|ψ(θ)〉/ dω` - resp. :math:`d⟨ψ(θ)|O(ω)|ψ(θ)〉/ dθ` - resp. :math:`d⟨ψ(θ)|i〉⟨i|ψ(θ)〉/ dθ`. - - The last case corresponds to the gradient w.r.t. the sampling probabilities - of `|ψ(θ)`. These gradients can be computed with different methods, i.e. a - parameter shift, a linear combination of unitaries and a finite difference - method. - - Examples:: - - x = Parameter('x') - ham = x * X - a = Parameter('a') - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.p(params[0], q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) - - value_dict = {x: 0.1, a: np.pi / 4} - - ham_grad = Gradient(grad_method='param_shift').convert(operator=op, params=[x]) - ham_grad.assign_parameters(value_dict).eval() - - state_grad = Gradient(grad_method='lin_comb').convert(operator=op, params=[a]) - state_grad.assign_parameters(value_dict).eval() - - prob_grad = Gradient(grad_method='fin_diff').convert(operator=CircuitStateFn(primitive=qc, coeff=1.), - params=[a]) - prob_grad.assign_parameters(value_dict).eval() - - Hessians: We want to compute :math:`d^2⟨ψ(θ)|O(ω)|ψ(θ)〉/ dω^2` - resp. :math:`d^2⟨ψ(θ)|O(ω)|ψ(θ)〉/ dθ^2` - resp. :math:`d^2⟨ψ(θ)|O(ω)|ψ(θ)〉/ dθdω` - resp. :math:`d^2⟨ψ(θ)|i〉⟨i|ψ(θ)〉/ dθ^2`. - - The last case corresponds to the Hessian w.r.t. the sampling probabilities of `|ψ(θ)`. - Just as the first order gradients, the Hessians can be evaluated with - different methods, i.e. a parameter shift, a linear combination of unitaries - and a finite difference method. Given a tuple of parameters - ``Hessian().convert(op, param_tuple)`` returns the value for the second order - derivative. If a list of parameters is given ``Hessian().convert(op, param_list)`` - returns the full Hessian for all the given parameters according to the given - parameter order. - - QFI: The Quantum Fisher Information `QFI` is a metric tensor which is - representative for the representation capacity of a parameterized quantum - state `|ψ(θ)〉 = V(θ)|ψ〉` generated by an input state `|ψ〉` and a - parametrized Ansatz `V(θ)`. The entries of the `QFI` for a pure state read - :math:`[QFI]kl= Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] * 4`. - - Just as for the previous derivative types, the QFI can be computed using - different methods: a full representation based on a linear combination of - unitaries implementation, a block-diagonal and a diagonal representation - based on an overlap method. - - Examples:: - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.p(params[0], q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) - - value_dict = {x: 0.1, a: np.pi / 4} - qfi = QFI('lin_comb_full').convert(operator=CircuitStateFn(primitive=qc, coeff=1.), params=[a]) - qfi.assign_parameters(value_dict).eval() - - - The combination of the QFI and the gradient lead to a special form of a - gradient, namely - - NaturalGradients: The natural gradient is a special gradient method which - rescales a gradient w.r.t. a state parameter with the inverse of the - corresponding Quantum Fisher Information (QFI) - :math:`QFI^-1 d⟨ψ(θ)|O(ω)|ψ(θ)〉/ dθ`. - Hereby, we can choose a gradient as well as a QFI method and a - regularization method which is used together with a least square solver - instead of exact invertion of the QFI: - - Examples:: - - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) - nat_grad = NaturalGradient(grad_method='lin_comb, qfi_method='lin_comb_full', \ - regularization='ridge').convert(operator=op, params=params) - - The gradient framework is also compatible with the optimizers from - `qiskit.aqua.components.optimizers`. The derivative classes come with a - `gradient_wrapper()` function which returns the corresponding callable. - -- Introduces ``transformations`` for the fermionic and bosonic transformation of a problem - instance. Transforms the fermionic operator to qubit operator. Respective class for the - transformation is ``fermionic_transformation`` - Introduces in algorithms ``ground_state_solvers`` for the calculation of ground state - properties. The calculation can be done either using an ``MinimumEigensolver`` or using - ``AdaptVQE`` - Introduces ``chemistry/results`` where the eigenstate_result and the - electronic_structure_result are also used for the algorithms. - Introduces Minimum Eigensolver factories ``minimum_eigensolver_factories`` where chemistry - specific minimum eigensolvers can be initialized Introduces orbital optimization vqe - ``oovqe`` as a ground state solver for chemistry applications - -- New Algorithm result classes: - - :class:`~qiskit.aqua.algorithms.Grover` method - :meth:`~qiskit.aqua.algorithms.Grover._run` - returns class :class:`~qiskit.aqua.algorithms.GroverResult`. - :class:`~qiskit.aqua.algorithms.AmplitudeEstimation` method - :meth:`~qiskit.aqua.algorithms.AmplitudeEstimation._run` - returns class :class:`~qiskit.aqua.algorithms.AmplitudeEstimationResult`. - :class:`~qiskit.aqua.algorithms.IterativeAmplitudeEstimation` method - :meth:`~qiskit.aqua.algorithms.IterativeAmplitudeEstimation._run` - returns class :class:`~qiskit.aqua.algorithms.IterativeAmplitudeEstimationResult`. - :class:`~qiskit.aqua.algorithms.MaximumLikelihoodAmplitudeEstimation` method - :meth:`~qiskit.aqua.algorithms.MaximumLikelihoodAmplitudeEstimation._run` - returns class :class:`~qiskit.aqua.algorithms.MaximumLikelihoodAmplitudeEstimationResult`. - - All new result classes are backwards compatible with previous result dictionary. - -- New Linear Solver result classes: - - :class:`~qiskit.aqua.algorithms.HHL` method - :meth:`~qiskit.aqua.algorithms.HHL._run` - returns class :class:`~qiskit.aqua.algorithms.HHLResult`. - :class:`~qiskit.aqua.algorithms.NumPyLSsolver` method - :meth:`~qiskit.aqua.algorithms.NumPyLSsolver._run` - returns class :class:`~qiskit.aqua.algorithms.NumPyLSsolverResult`. - - All new result classes are backwards compatible with previous result dictionary. - -- ``MinimumEigenOptimizationResult`` now exposes properties: ``samples`` and - ``eigensolver_result``. The latter is obtained from the underlying algorithm used by the - optimizer and specific to the algorithm. - ``RecursiveMinimumEigenOptimizer`` now returns an instance of the result class - ``RecursiveMinimumEigenOptimizationResult`` which in turn may contains intermediate results - obtained from the underlying algorithms. The dedicated result class exposes properties - ``replacements`` and ``history`` that are specific to this optimizer. The depth of the history - is managed by the ``history`` parameter of the optimizer. - -- ``GroverOptimizer`` now returns an instance of ``GroverOptimizationResult`` and this result - class exposes properties ``operation_counts``, ``n_input_qubits``, and ``n_output_qubits`` - directly. These properties are not available in the ``raw_results`` dictionary anymore. - -- ``SlsqpOptimizer`` now returns an instance of ``SlsqpOptimizationResult`` and this result class - exposes additional properties specific to the SLSQP implementation. - -- Support passing ``QuantumCircuit`` objects as generator circuits into - the ``QuantumGenerator``. - -- Removes the restriction to real input vectors in CircuitStateFn.from_vector. - The method calls extensions.Initialize. The latter explicitly supports (in API - and documentation) complex input vectors. So this restriction seems unnecessary. - -- Simplified `AbelianGrouper` using a graph coloring algorithm of retworkx. - It is faster than the numpy-based coloring algorithm. - -- Allow calling ``eval`` on state function objects with no argument, which returns the - ``VectorStateFn`` representation of the state function. - This is consistent behavior with ``OperatorBase.eval``, which returns the - ``MatrixOp`` representation, if no argument is passed. - -- Adds ``max_iterations`` to the ``VQEAdapt`` class in order to allow - limiting the maximum number of iterations performed by the algorithm. - -- VQE expectation computation with Aer qasm_simulator now defaults to a - computation that has the expected shot noise behavior. The special Aer - snapshot based computation, that is much faster, with the ideal output - similar to state vector simulator, may still be chosen but like before - Aqua 0.7 it now no longer defaults to this but can be chosen. - - -.. _Release Notes_Aqua_0.8.0_Upgrade Notes: - -Upgrade Notes -------------- - -- Extension of the previous Analytic Quantum Gradient Descent (AQGD) classical - optimizer with the AQGD with Epochs. Now AQGD performs the gradient descent - optimization with a momentum term, analytic gradients, and an added customized - step length schedule for parametrized quantum gates. Gradients are computed - "analytically" using the quantum circuit when evaluating the objective function. - - -- The deprecated support for running qiskit-aqua with Python 3.5 has - been removed. To use qiskit-aqua >=0.8.0 you will now need at - least Python 3.6. If you are using Python 3.5 the last version which will - work is qiskit-aqua 0.7.x. - -- Added retworkx as a new dependency. - - -.. _Release Notes_Aqua_0.8.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The ``i_objective`` argument of the amplitude estimation algorithms has been - renamed to ``objective_qubits``. - -- TransformationType - -- QubitMappingType - -- Deprecate the ``CircuitFactory`` and derived types. The ``CircuitFactory`` has - been introduced as temporary class when the ``QuantumCircuit`` missed some - features necessary for applications in Aqua. Now that the circuit has all required - functionality, the circuit factory can be removed. - The replacements are shown in the following table. - - .. code-block:: - - Circuit factory class | Replacement - ------------------------------------+----------------------------------------------- - CircuitFactory | use QuantumCircuit - | - UncertaintyModel | - - UnivariateDistribution | - - MultivariateDistribution | - - NormalDistribution | qiskit.circuit.library.NormalDistribution - MultivariateNormalDistribution | qiskit.circuit.library.NormalDistribution - LogNormalDistribution | qiskit.circuit.library.LogNormalDistribution - MultivariateLogNormalDistribution | qiskit.circuit.library.LogNormalDistribution - UniformDistribution | qiskit.circuit.library.UniformDistribution - MultivariateUniformDistribution | qiskit.circuit.library.UniformDistribution - UnivariateVariationalDistribution | use parameterized QuantumCircuit - MultivariateVariationalDistribution | use parameterized QuantumCircuit - | - UncertaintyProblem | - - UnivariateProblem | - - MultivariateProblem | - - UnivariatePiecewiseLinearObjective | qiskit.circuit.library.LinearAmplitudeFunction - -- The ising convert classes - :class:`qiskit.optimization.converters.QuadraticProgramToIsing` and - :class:`qiskit.optimization.converters.IsingToQuadraticProgram` have - been deprecated and will be removed in a future release. Instead the - :class:`qiskit.optimization.QuadraticProgram` methods - :meth:`~qiskit.optimization.QuadraticProgram.to_ising` and - :meth:`~qiskit.optimization.QuadraticPrgraom.from_ising` should be used - instead. - -- Deprecate the ``WeightedSumOperator`` which has been ported to the circuit library as - ``WeightedAdder`` in ``qiskit.circuit.library``. - -- ``Core Hamiltonian`` class is deprecated in favor of the ``FermionicTransformation`` - ``Chemistry Operator`` class is deprecated in favor of the ``tranformations`` - ``minimum_eigen_solvers/vqe_adapt`` is also deprecated and moved as an implementation - of the ground_state_solver interface - ``applications/molecular_ground_state_energy`` is deprecated in favor of ``ground_state_solver`` - -- ``Optimizer.SupportLevel`` nested enum is replaced by ``OptimizerSupportLevel`` - and ``Optimizer.SupportLevel`` was removed. Use, for example, - ``OptimizerSupportLevel.required`` instead of ``Optimizer.SupportLevel.required``. - -- Deprecate the ``UnivariateVariationalDistribution`` and - ``MultivariateVariationalDistribution`` as input - to the ``QuantumGenerator``. Instead, plain ``QuantumCircuit`` objects can - be used. - -- Ignored `fast` and `use_nx` options of `AbelianGrouper.group_subops` to be removed in the - future release. - -- GSLS optimizer class deprecated ``__init__`` parameter ``max_iter`` in favor of ``maxiter``. - SPSA optimizer class deprecated ``__init__`` parameter ``max_trials`` in favor of ``maxiter``. - optimize_svm function deprecated ``max_iters`` parameter in favor of ``maxiter``. - ADMMParameters class deprecated ``__init__`` parameter ``max_iter`` in favor of ``maxiter``. - - -.. _Release Notes_Aqua_0.8.0_Bug Fixes: - -Bug Fixes ---------- - - -- The UCCSD excitation list, comprising single and double excitations, was not being - generated correctly when an active space was explicitly provided to UCSSD via the - active_(un)occupied parameters. - -- For the amplitude estimation algorithms, we define the number of oracle queries - as number of times the Q operator/Grover operator is applied. This includes - the number of shots. That factor has been included in MLAE and IQAE but - was missing in the 'standard' QAE. - -- Fix CircuitSampler.convert, so that the ``is_measurement`` property is - propagated to converted StateFns. - -- Fix double calculation of coefficients in - :meth`~qiskit.aqua.operators.VectorStateFn.to_circuit_op`. - -- Calling PauliTrotterEvolution.convert on an operator including a term that - is a scalar multiple of the identity gave an incorrect circuit, one that - ignored the scalar coefficient. This fix includes the effect of the - coefficient in the global_phase property of the circuit. - -- Make ListOp.num_qubits check that all ops in list have the same num_qubits - Previously, the number of qubits in the first operator in the ListOp - was returned. With this change, an additional check is made that all - other operators also have the same number of qubits. - -- Make PauliOp.exp_i() generate the correct matrix with the following changes. - 1) There was previously an error in the phase of a factor of 2. - 2) The global phase was ignored when converting the circuit - to a matrix. We now use qiskit.quantum_info.Operator, which is - generally useful for converting a circuit to a unitary matrix, - when possible. - -- Fixes the cyclicity detection as reported buggy in - https://github.com/Qiskit/qiskit-aqua/issues/1184. - - -IBM Q Provider 0.11.0 -===================== - -.. _Release Notes_0.11.0_IBMQ_Upgrade Notes: - -Upgrade Notes -------------- - -- The deprecated support for running qiskit-ibmq-provider with Python 3.5 has - been removed. To use qiskit-ibmq-provider >=0.11.0 you will now need at - least Python 3.6. If you are using Python 3.5 the last version which will - work is qiskit-ibmq-provider 0.10.x. - -- Prior to this release, ``websockets`` 7.0 was used for Python 3.6. - With this release, ``websockets`` 8.0 or above is required for all Python versions. - The package requirements have been updated to reflect this. - - -############# -Qiskit 0.22.0 -############# - -Terra 0.15.2 -============ - -No change - -Aer 0.6.1 -========= - -No change - -Ignis 0.4.0 -=========== - -No change - -Aqua 0.7.5 -========== - -No change - -IBM Q Provider 0.10.0 -===================== - -.. _Release Notes_IBMQ_provider_0.10.0_New Features: - -New Features ------------- - -- CQC randomness extractors can now be invoked asynchronously, using methods - :meth:`~qiskit.providers.ibmq.random.CQCExtractor.run_async_ext1` and - :meth:`~qiskit.providers.ibmq.random.CQCExtractor.run_async_ext2`. Each of - these methods returns a :class:`~qiskit.providers.ibmq.random.CQCExtractorJob` - instance that allows you to check on the job status (using - :meth:`~qiskit.providers.ibmq.random.CQCExtractorJob.status`) and wait for - its result (using - :meth:`~qiskit.providers.ibmq.random.CQCExtractorJob.block_until_ready`). - The :meth:`qiskit.provider.ibmq.random.CQCExtractor.run` method remains - synchronous. - -- You can now use the new IBMQ experiment service to query, retrieve, and - download experiment related data. Interface to this service is located - in the new :mod:`qiskit.providers.ibmq.experiment` package. - Note that this feature is still in - beta, and not all accounts have access to it. It is also subject to heavy - modification in both functionality and API without backward compatibility. - -- Two Jupyter magic functions, the IQX dashboard and the backend widget, are - updated to display backend reservations. If a backend has reservations - scheduled in the next 24 hours, time to the next one and its duration - are displayed (e.g. ``Reservation: in 6 hrs 30 min (60m)``). If there is - a reservation and the backend is active, the backend status is displayed - as ``active [R]``. - - -.. _Release Notes_IBMQ_provider_0.10.0_Upgrade Notes: - -Upgrade Notes -------------- - -- Starting from this release, the `basis_gates` returned by - :meth:`qiskit.providers.ibmq.IBMQBackend.configuration` may differ for each backend. - You should update your program if it relies on the basis gates being - ``['id','u1','u2','u3','cx']``. We recommend always using the - :meth:`~qiskit.providers.ibmq.IBMQBackend.configuration` method to find backend - configuration values instead of hard coding them. - -- ``qiskit-ibmq-provider`` release 0.10 requires ``qiskit-terra`` - release 0.15 or above. The package metadata has been updated to reflect - the new dependency. - -############# -Qiskit 0.21.0 -############# - -Terra 0.15.2 -============ - -No change - -Aer 0.6.1 -========= - -No change - -Ignis 0.4.0 -=========== - -No change - -Aqua 0.7.5 -========== - -No change - -IBM Q Provider 0.9.0 -==================== - -.. _Release Notes_IBMQ_provider_0.9.0_New Features: - -New Features ------------- - -- You can now access the IBMQ random number services, such as the CQC - randomness extractor, using the new package - :mod:`qiskit.providers.ibmq.random`. Note that this feature is still in - beta, and not all accounts have access to it. It is also subject to heavy - modification in both functionality and API without backward compatibility. - - -.. _Release Notes_IBMQ_provider_0.9.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixes an issue that may raise a ``ValueError`` if - :meth:`~qiskit.providers.ibmq.IBMQBackend.retrieve_job` is used to retrieve - a job submitted via the IBM Quantum Experience Composer. - -- :class:`~qiskit.providers.ibmq.managed.IBMQJobManager` has been updated so - that if a time out happens while waiting for an old job to finish, the - time out error doesn't prevent a new job to be submitted. Fixes - `#737 `_ - - -############# -Qiskit 0.20.1 -############# - -Terra 0.15.2 -============ - -.. _Release Notes_0.15.2_Bug Fixes: - -Bug Fixes ---------- - -- When accessing the ``definition`` attribute of a parameterized ``Gate`` - instance, the generated ``QuantumCircuit`` had been generated with an invalid - ``ParameterTable``, such that reading from ``QuantumCircuit.parameters`` or - calling ``QuantumCircuit.bind_parameters`` would incorrectly report the - unbound parameters. This has been resolved. - -- ``SXGate().inverse()`` had previously returned an 'sx_dg' gate with a correct - ``definition`` but incorrect ``to_matrix``. This has been updated such that - ``SXGate().inverse()`` returns an ``SXdgGate()`` and vice versa. - -- ``Instruction.inverse()``, when not overridden by a subclass, would in some - cases return a ``Gate`` instance with an incorrect ``to_matrix`` method. The - instances of incorrect ``to_matrix`` methods have been removed. - -- For ``C3XGate`` with a non-zero ``angle``, inverting the gate via - ``C3XGate.inverse()`` had previously generated an incorrect inverse gate. - This has been corrected. - -- The ``MCXGate`` modes have been updated to return a gate of the same mode - when calling ``.inverse()``. This resolves an issue where in some cases, - transpiling a circuit containing the inverse of an ``MCXVChain`` gate would - raise an error. - -- Previously, when creating a multiply controlled phase gate via - ``PhaseGate.control``, an ``MCU1Gate`` gate had been returned. This has been - had corrected so that an ``MCPhaseGate`` is returned. - -- Previously, attempting to decompose a circuit containing an - ``MCPhaseGate`` would raise an error due to an inconsistency in the - definition of the ``MCPhaseGate``. This has been corrected. - -- ``QuantumCircuit.compose`` and ``DAGCircuit.compose`` had, in some cases, - incorrectly translated conditional gates if the input circuit contained - more than one ``ClassicalRegister``. This has been resolved. - -- Fixed an issue when creating a :class:`qiskit.result.Counts` object from an - empty data dictionary. Now this will create an empty - :class:`~qiskit.result.Counts` object. The - :meth:`~qiskit.result.Counts.most_frequent` method is also updated to raise - a more descriptive exception when the object is empty. Fixes - `#5017 `__ - -- Extending circuits with differing registers updated the ``qregs`` and - ``cregs`` properties accordingly, but not the ``qubits`` and ``clbits`` - lists. As these are no longer generated from the registers but are cached - lists, this lead to a discrepancy of registers and bits. This has been - fixed and the ``extend`` method explicitly updates the cached bit lists. - -- Fix bugs of the concrete implementations of - meth:`~qiskit.circuit.ControlledGate.inverse` method which do not preserve - the ``ctrl_state`` parameter. - -- A bug was fixed that caused long pulse schedules to throw a recursion error. - -Aer 0.6.1 -========= - -No change - -Ignis 0.4.0 -=========== - -No change - -Aqua 0.7.5 -========== - -No change - -IBM Q Provider 0.8.0 -==================== - -No change - - -############# -Qiskit 0.20.0 -############# - -Terra 0.15.1 -============ - -.. _Release Notes_0.15.0_Prelude: - -Prelude -------- - - -The 0.15.0 release includes several new features and bug fixes. Some -highlights for this release are: - -This release includes the introduction of arbitrary -basis translation to the transpiler. This includes support for directly -targeting a broader range of device basis sets, e.g. backends -implementing RZ, RY, RZ, CZ or iSwap gates. - -The :class:`~qiskit.circuit.QuantumCircuit` class now tracks global -phase. This means controlling a circuit which has global phase now -correctly adds a relative phase, and gate matrix definitions are now -exact rather than equal up to a global phase. - - -.. _Release Notes_0.15.0_New Features: - -New Features ------------- - - -- A new DAG class :class:`qiskit.dagcircuit.DAGDependency` for representing - the dependency form of circuit, In this DAG, the nodes are - operations (gates, measure, barrier, etc...) and the edges corresponds to - non-commutation between two operations. - -- Four new functions are added to :mod:`qiskit.converters` for converting back and - forth to :class:`~qiskit.dagcircuit.DAGDependency`. These functions are: - - * :func:`~qiskit.converters.circuit_to_dagdependency` to convert - from a :class:`~qiskit.circuit.QuantumCircuit` object to a - :class:`~qiskit.dagcircuit.DAGDependency` object. - * :func:`~qiskit.converters.dagdependency_to_circuit` to convert from a - :class:`~qiskit.dagcircuit.DAGDependency` object to a - :class:`~qiskit.circuit.QuantumCircuit` object. - * :func:`~qiskit.converters.dag_to_dagdependency` to convert from - a :class:`~qiskit.dagcircuit.DAGCircuit` object to a - :class:`~qiskit.dagcircuit.DAGDependency` object. - * :func:`~qiskit.converters.dagdependency_to_dag` to convert from - a :class:`~qiskit.dagcircuit.DAGDependency` object to a - :class:`~qiskit.dagcircuit.DAGCircuit` object. - - For example:: - - from qiskit.converters.dagdependency_to_circuit import dagdependency_to_circuit - from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit - - circuit_in = QuantumCircuit(2) - circuit_in.h(qr[0]) - circuit_in.h(qr[1]) - - dag_dependency = circuit_to_dagdependency(circuit_in) - circuit_out = dagdepency_to_circuit(dag_dependency) - -- Two new transpiler passes have been added to :mod:`qiskit.transpiler.passes` - The first, :class:`~qiskit.transpiler.passes.UnrollCustomDefinitions`, - unrolls all instructions in the - circuit according to their :attr:`~qiskit.circuit.Instruction.definition` - property, stopping when reaching either the specified ``basis_gates`` - or a set of gates in the provided - :class:`~qiskit.circuit.EquivalenceLibrary`. The second, - :class:`~qiskit.transpiler.passes.BasisTranslator`, uses the set of - translations in the provided :class:`~qiskit.circuit.EquivalenceLibrary` to - re-write circuit instructions in a specified basis. - -- A new ``translation_method`` keyword argument has been added to - :func:`~qiskit.compiler.transpile` to allow selection of the method to be - used for translating circuits to the available device gates. For example, - ``transpile(circ, backend, translation_method='translator')``. Valid - choices are: - - * ``'unroller'``: to use the :class:`~qiskit.transpiler.passes.Unroller` - pass - * ``'translator'``: to use the - :class:`~qiskit.transpiler.passes.BasisTranslator` pass. - * ``'synthesis'``: to use the - :class:`~qiskit.transpiler.passes.UnitarySynthesis` pass. - - The default value is ``'translator'``. - -- A new class for handling counts result data, :class:`qiskit.result.Counts`, - has been added. This class is a subclass of ``dict`` and can be interacted - with like any other dictionary. But, it includes helper methods and - attributes for dealing with counts results from experiments and also - handles post processing and formatting of binary strings at object - initialization. A :class:`~qiskit.result.Counts` object can be created by - passing a dictionary of counts with the keys being either integers, - hexadecimal strings of the form ``'0x4a'``, binary strings of the form - ``'0b1101'``, a bit string formatted across register and memory slots - (ie ``'00 10'``), or a dit string. For example:: - - from qiskit.result import Counts - - counts = Counts({"0x0': 1, '0x1', 3, '0x2': 1020}) - -- A new method for constructing :class:`qiskit.dagcircuit.DAGCircuit` objects - has been added, :meth:`~qiskit.dagcircuit.DAGCircuit.from_networkx`. This - method takes in a networkx ``MultiDiGraph`` object (in the format returned - by :meth:`~qiskit.dagcircuit.DAGCircuit.to_networkx`) and will return a - new :class:`~qiskit.dagcircuit.DAGCircuit` object. The intent behind this - function is to enable transpiler pass authors to leverage networkx's - `graph algorithm library - `__ - if a function is missing from the - `retworkx API `_. - Although, hopefully in such casses an issue will be opened with - `retworkx issue tracker `__ (or - even better a pull request submitted). - -- A new kwarg for ``init_qubits`` has been added to - :func:`~qiskit.compiler.assemble` and :func:`~qiskit.execute.execute`. - For backends that support this feature ``init_qubits`` can be used to - control whether the backend executing the circuits inserts any - initialization sequences at the start of each shot. By default this is set - to ``True`` meaning that all qubits can assumed to be in the ground state - at the start of each shot. However, when ``init_qubits`` is set to - ``False`` qubits will be uninitialized at the start of each - experiment and between shots. Note, that the backend running the circuits - has to support this feature for this flag to have any effect. - -- A new kwarg ``rep_delay`` has been added to - :func:`qiskit.compiler.assemble`, :func:`qiskit.execute.execute`, and the - constructor for :class:`~qiskit.qobj.PulseQobjtConfig`.qiskit - This new kwarg is used to denotes the time between program executions. It - must be chosen from the list of valid values set as the - ``rep_delays`` from a backend's - :class:`~qiskit.providers.models.PulseBackendConfiguration` object which - can be accessed as ``backend.configuration().rep_delays``). - - The ``rep_delay`` kwarg will only work on backends which allow for dynamic - repetition time. This will also be indicated in the - :class:`~qiskit.providers.models.PulseBackendConfiguration` object for a - backend as the ``dynamic_reprate_enabled`` attribute. If - ``dynamic_reprate_enabled`` is ``False`` then the ``rep_time`` value - specified for :func:`qiskit.compiler.assemble`, - :func:`qiskit.execute.execute`, or the constructor for - :class:`~qiskit.qobj.PulseQobjtConfig` will be used rather than - ``rep_delay``. ``rep_time`` only allows users to specify the duration of a - program, rather than the delay between programs. - -- The ``qobj_schema.json`` JSON Schema file in :mod:`qiskit.schemas` has - been updated to include the ``rep_delay`` as an optional configuration - property for pulse qobjs. - -- The ``backend_configuration_schema.json`` JSON Schema file in - mod:`qiskit.schemas` has been updated to include ``rep_delay_range`` and - ``default_rep_delay`` as optional properties for a pulse backend - configuration. - -- A new attribute, :attr:`~qiskit.circuit.QuantumCircuit.global_phase`, - which is is used for tracking the global phase has been added to the - :class:`qiskit.circuit.QuantumCircuit` class. For example:: - - import math - - from qiskit import QuantumCircuit - - circ = QuantumCircuit(1, global_phase=math.pi) - circ.u1(0) - - The global phase may also be changed or queried with - ``circ.global_phase`` in the above example. In either case the setting is - in radians. If the circuit is converted to an instruction or gate the - global phase is represented by two single qubit rotations on the first - qubit. - - This allows for other methods and functions which consume a - :class:`~qiskit.circuit.QuantumCircuit` object to take global phase into - account. For example. with the - :attr:`~qiskit.circuit.QuantumCircuit.global_phase` - attribute the :meth:`~qiskit.circuit.Gate.to_matrix` method for a gate - can now exactly correspond to its decompositions instead of - just up to a global phase. - - The same attribute has also been added to the - :class:`~qiskit.dagcircuit.DAGCircuit` class so that global phase - can be tracked when converting between - :class:`~qiskit.circuit.QuantumCircuit` and - :class:`~qiskit.dagcircuit.DAGCircuit`. - -- Two new classes, :class:`~qiskit.circuit.AncillaRegister` and - :class:`~qiskit.circuit.AncillaQubit` have been added to the - :mod:`qiskit.circuit` module. These are subclasses of - :class:`~qiskit.circuit.QuantumRegister` and :class:`~qiskit.circuit.Qubit` - respectively and enable marking qubits being ancillas. This will allow - these qubits to be re-used in larger circuits and algorithms. - -- A new method, :meth:`~qiskit.circuit.QuantumCircuit.control`, has been - added to the :class:`~qiskit.circuit.QuantumCircuit`. This method will - return a controlled version of the :class:`~qiskit.circuit.QuantumCircuit` - object, with both open and closed controls. This functionality had - previously only been accessible via the :class:`~qiskit.circuit.Gate` - class. - -- A new method :meth:`~qiskit.circuit.QuantumCircuit.repeat` has been added - to the :class:`~qiskit.circuit.QuantumCircuit` class. It returns a new - circuit object containing a specified number of repetitions of the original - circuit. For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - repeated_qc = qc.repeat(3) - repeated_qc.decompose().draw(output='mpl') - - The parameters are copied by reference, meaning that if you update - the parameters in one instance of the circuit all repetitions will be - updated. - -- A new method :meth:`~qiskit.circuit.QuantumCircuit.reverse_bits` has been - added to the :class:`~qiskit.circuit.QuantumCircuit` class. This method - will reverse the order of bits in a circuit (both quantum and classical - bits). This can be used to switch a circuit from little-endian to big-endian - and vice-versa. - -- A new method, :meth:`~qiskit.transpiler.Layout.combine_into_edge_map()`, - was added to the :class:`qiskit.transpiler.Layout` class. This method - enables converting converting two :class:`~qiskit.transpiler.Layout` objects - into a qubit map for composing two circuits. - -- A new class, :class:`~qiskit.test.mock.utils.ConfigurableFakeBackend`, has - been added to the :mod:`qiskit.test.mock.utils` module. This new class - enables the creation of configurable mock backends for use in testing. - For example:: - - from qiskit.test.mock.utils import ConfigurableFakeBackend - - backend = ConfigurableFakeBackend("Tashkent", - n_qubits=100, - version="0.0.1", - basis_gates=['u1'], - qubit_t1=99., - qubit_t2=146., - qubit_frequency=5., - qubit_readout_error=0.01, - single_qubit_gates=['u1']) - - will create a backend object with 100 qubits and all the other parameters - specified in the constructor. - -- A new method :meth:`~qiskit.circuit.EquivalenceLibrary.draw` has been - added to the :class:`qiskit.circuit.EquivalenceLibrary` class. This - method can be used for drawing the contents of an equivalence library, - which can be useful for debugging. For example: - - .. code-block:: python - - from numpy import pi - - from qiskit.circuit import EquivalenceLibrary - from qiskit.circuit import QuantumCircuit - from qiskit.circuit import QuantumRegister - from qiskit.circuit import Parameter - from qiskit.circuit.library import HGate - from qiskit.circuit.library import U2Gate - from qiskit.circuit.library import U3Gate - - my_equiv_library = EquivalenceLibrary() - - q = QuantumRegister(1, 'q') - def_h = QuantumCircuit(q) - def_h.append(U2Gate(0, pi), [q[0]], []) - my_equiv_library.add_equivalence(HGate(), def_h) - - theta = Parameter('theta') - phi = Parameter('phi') - lam = Parameter('lam') - def_u2 = QuantumCircuit(q) - def_u2.append(U3Gate(pi / 2, phi, lam), [q[0]], []) - my_equiv_library.add_equivalence(U2Gate(phi, lam), def_u2) - - my_equiv_library.draw() - -- A new Phase instruction, :class:`~qiskit.pulse.SetPhase`, has been added - to :mod:`qiskit.pulse`. This instruction sets the phase of the - subsequent pulses to the specified phase (in radians. For example:: - - import numpy as np - - from qiskit.pulse import DriveChannel - from qiskit.pulse import Schedule - from qiskit.pulse import SetPhase - - sched = Schedule() - sched += SetPhase(np.pi, DriveChannel(0)) - - In this example, the phase of the pulses applied to ``DriveChannel(0)`` - after the :class:`~qiskit.pulse.SetPhase` instruction will be set to - :math:`\pi` radians. - -- A new pulse instruction :class:`~qiskit.pulse.ShiftFrequency` has been - added to :mod:`qiskit.pulse.instructions`. This instruction enables - shifting the frequency of a channel from its set frequency. For example:: - - from qiskit.pulse import DriveChannel - from qiskit.pulse import Schedule - from qiskit.pulse import ShiftFrequency - - sched = Schedule() - sched += ShiftFrequency(-340e6, DriveChannel(0)) - - In this example all the pulses applied to ``DriveChannel(0)`` after the - :class:`~qiskit.pulse.ShiftFrequency` command will have the envelope a - frequency decremented by 340MHz. - -- A new method :meth:`~qiskit.circuit.ParameterExpression.conjugate` has - been added to the :class:`~qiskit.circuit.ParameterExpression` class. - This enables calling ``numpy.conj()`` without raising an error. Since a - :class:`~qiskit.circuit.ParameterExpression` object is real, it will - return itself. This behaviour is analogous to Python floats/ints. - -- A new class :class:`~qiskit.circuit.library.PhaseEstimation` has been - added to :mod:`qiskit.circuit.library`. This circuit library class is - the circuit used in the original formulation of the phase estimation - algorithm in - `arXiv:quant-ph/9511026 `__. - Phase estimation is the task to to estimate the phase :math:`\phi` of an - eigenvalue :math:`e^{2\pi i\phi}` of a unitary operator :math:`U`, provided - with the corresponding eigenstate :math:`|psi\rangle`. That is - - .. math:: - - U|\psi\rangle = e^{2\pi i\phi} |\psi\rangle - - This estimation (and thereby this circuit) is a central routine to several - well-known algorithms, such as Shor's algorithm or Quantum Amplitude - Estimation. - -- The :mod:`qiskit.visualization` function - :func:`~qiskit.visualization.plot_state_qsphere` has a new kwarg - ``show_state_labels`` which is used to control whether each blob in the - qsphere visualization is labeled. By default this kwarg is set to ``True`` - and shows the basis states next to each blob by default. This feature can be - disabled, reverting to the previous behavior, by setting the - ``show_state_labels`` kwarg to ``False``. - -- The :mod:`qiskit.visualization` function - :func:`~qiskit.visualization.plot_state_qsphere` has a new kwarg - ``show_state_phases`` which is set to ``False`` by default. When set to - ``True`` it displays the phase of each basis state. - -- The :mod:`qiskit.visualization` function - :func:`~qiskit.visualization.plot_state_qsphere` has a new kwarg - ``use_degrees`` which is set to ``False`` by default. When set to ``True`` - it displays the phase of each basis state in degrees, along with the phase - circle at the bottom right. - -- A new class, :class:`~qiskit.circuit.library.QuadraticForm` to the - :mod:`qiskit.circuit.library` module for implementing a a quadratic form on - binary variables. The circuit library element implements the operation - - .. math:: - - |x\rangle |0\rangle \mapsto |x\rangle |Q(x) \mod 2^m\rangle - - for the quadratic form :math:`Q` and :math:`m` output qubits. - The result is in the :math:`m` output qubits is encoded in two's - complement. If :math:`m` is not specified, the circuit will choose - the minimal number of qubits required to represent the result - without applying a modulo operation. - The quadratic form is specified using a matrix for the quadratic - terms, a vector for the linear terms and a constant offset. - If all terms are integers, the circuit implements the quadratic form - exactly, otherwise it is only an approximation. - - For example:: - - import numpy as np - - from qiskit.circuit.library import QuadraticForm - - A = np.array([[1, 2], [-1, 0]]) - b = np.array([3, -3]) - c = -2 - m = 4 - quad_form_circuit = QuadraticForm(m, A, b, c) - -- Add :meth:`qiskit.quantum_info.Statevector.expectation_value` and - :meth:`qiskit.quantum_info.DensityMatrix.expectation_value` methods for - computing the expectation value of an :class:`qiskit.quantum_info.Operator`. - -- For the ``seed`` kwarg in the constructor for - :class:`qiskit.circuit.library.QuantumVolume` `numpy random Generator - objects `__ - can now be used. Previously, only integers were a valid input. This is - useful when integrating :class:`~qiskit.circuit.library.QuantumVolume` as - part of a larger function with its own random number generation, e.g. - generating a sequence of - :class:`~qiskit.circuit.library.QuantumVolume` circuits. - -- The :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.compose` has a new kwarg ``front`` - which can be used for prepending the other circuit before the origin - circuit instead of appending. For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - - circ1 = QuantumCircuit(2) - circ2 = QuantumCircuit(2) - - circ2.h(0) - circ1.cx(0, 1) - - circ1.compose(circ2, front=True).draw(output='mpl') - -- Two new passes, :class:`~qiskit.transpiler.passes.SabreLayout` and - :class:`~qiskit.transpiler.passes.SabreSwap` for layout and routing have - been added to :mod:`qiskit.transpiler.passes`. These new passes are based - on the algorithm presented in Li et al., "Tackling the Qubit Mapping - Problem for NISQ-Era Quantum Devices", ASPLOS 2019. They can also be - selected when using the :func:`~qiskit.compiler.transpile` function by - setting the ``layout_method`` kwarg to ``'sabre'`` and/or the - ``routing_method`` to ``'sabre'`` to use - :class:`~qiskit.transpiler.passes.SabreLayout` and - :class:`~qiskit.transpiler.passes.SabreSwap` respectively. - -- Added the method :meth:`~qiskit.pulse.Schedule.replace` to the - :class:`qiskit.pulse.Schedule` class which allows a - pulse instruction to be replaced with another. For example:: - - .. code-block:: python - - from qiskit import pulse - - d0 = pulse.DriveChannel(0) - - sched = pulse.Schedule() - - old = pulse.Play(pulse.Constant(100, 1.0), d0) - new = pulse.Play(pulse.Constant(100, 0.1), d0) - - sched += old - - sched = sched.replace(old, new) - - assert sched == pulse.Schedule(new) - -- Added new gate classes to :mod:`qiskit.circuit.library` for the - :math:`\sqrt{X}`, its adjoint :math:`\sqrt{X}^\dagger`, and - controlled :math:`\sqrt{X}` gates as - :class:`~qiskit.circuit.library.SXGate`, - :class:`~qiskit.circuit.library.SXdgGate`, and - :class:`~qiskit.circuit.library.CSXGate`. They can also be added to - a :class:`~qiskit.circuit.QuantumCircuit` object using the - :meth:`~qiskit.circuit.QuantumCircuit.sx`, - :meth:`~qiskit.circuit.QuantumCircuit.sxdg`, and - :meth:`~qiskit.circuit.QuantumCircuit.csx` respectively. - -- Add support for :class:`~qiskit.circuit.Reset` instructions to - :meth:`qiskit.quantum_info.Statevector.from_instruction`. Note that this - involves RNG sampling in choosing the projection to the zero state in the - case where the qubit is in a superposition state. The seed for sampling - can be set using the :meth:`~qiskit.quantum_info.Statevector.seed` method. - -- The methods :meth:`qiskit.circuit.ParameterExpression.subs` and - :meth:`qiskit.circuit.QuantumCircuit.assign_parameters` now - accept :class:`~qiskit.circuit.ParameterExpression` as the target value - to be substituted. - - For example, - - .. code-block:: - - from qiskit.circuit import QuantumCircuit, Parameter - - p = Parameter('p') - source = QuantumCircuit(1) - source.rz(p, 0) - - x = Parameter('x') - source.assign_parameters({p: x*x}) - - .. parsed-literal:: - - ┌──────────┐ - q_0: ┤ Rz(x**2) ├ - └──────────┘ - -- The :meth:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.to_gate` has a new kwarg - ``label`` which can be used to set a label for for the output - :class:`~qiskit.circuit.Gate` object. For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - - circuit_gate = QuantumCircuit(2) - circuit_gate.h(0) - circuit_gate.cx(0, 1) - custom_gate = circuit_gate.to_gate(label='My Special Bell') - new_circ = QuantumCircuit(2) - new_circ.append(custom_gate, [0, 1], []) - new_circ.draw(output='mpl') - -- Added the :class:`~qiskit.circuit.library.UGate`, - :class:`~qiskit.circuit.library.CUGate`, - :class:`~qiskit.circuit.library.PhaseGate`, and - :class:`~qiskit.circuit.library.CPhaseGate` with the corresponding - :class:`~qiskit.circuit.QuantumCircuit` methods - :meth:`~qiskit.circuit.QuantumCircuit.u`, - :meth:`~qiskit.circuit.QuantumCircuit.cu`, - :meth:`~qiskit.circuit.QuantumCircuit.p`, and - :meth:`~qiskit.circuit.QuantumCircuit.cp`. - The :class:`~qiskit.circuit.library.UGate` gate is the generic single qubit - rotation gate with 3 Euler angles and the - :class:`~qiskit.circuit.library.CUGate` gate its controlled version. - :class:`~qiskit.circuit.library.CUGate` has 4 parameters to account for a - possible global phase of the U gate. The - :class:`~qiskit.circuit.library.PhaseGate` and - :class:`~qiskit.circuit.library.CPhaseGate` gates are the general Phase - gate at an arbitrary angle and it's controlled version. - -- A new kwarg, ``cregbundle`` has been added to the - :func:`qiskit.visualization.circuit_drawer` function and the - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.draw`. When set to ``True`` the - cregs will be bundled into a single line in circuit visualizations for the - ``text`` and ``mpl`` drawers. The default value is ``True``. - Addresses issue `#4290 `_. - - For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - circuit = QuantumCircuit(2) - circuit.measure_all() - circuit.draw(output='mpl', cregbundle=True) - -- A new kwarg, ``initial_state`` has been added to the - :func:`qiskit.visualization.circuit_drawer` function and the - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.draw`. When set to ``True`` the - initial state will now be included in circuit visualizations for all drawers. - Addresses issue `#4293 `_. - - For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - circuit = QuantumCircuit(2) - circuit.measure_all() - circuit.draw(output='mpl', initial_state=True) - -- Labels will now be displayed when using the 'mpl' drawer. There are 2 - types of labels - gate labels and control labels. Gate labels will - replace the gate name in the display. Control labels will display - above or below the controls for a gate. - Fixes issues #3766, #4580 - Addresses issues `#3766 `_ - and `#4580 `_. - - For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.circuit.library.standard_gates import YGate - circuit = QuantumCircuit(2) - circuit.append(YGate(label='A Y Gate').control(label='Y Control'), [0, 1]) - circuit.draw(output='mpl') - - -.. _Release Notes_0.15.0_Upgrade Notes: - -Upgrade Notes -------------- - -- Implementations of the multi-controlled X Gate ( - :class:`~qiskit.circuit.library.MCXGrayCode`, - :class:`~qiskit.circuit.library.MCXRecursive`, and - :class:`~qiskit.circuit.library.MCXVChain`) have had their ``name`` - properties changed to more accurately describe their - implementation: ``mcx_gray``, ``mcx_recursive``, and - ``mcx_vchain`` respectively. Previously, these gates shared the - name ``mcx`` with :class:`~qiskit.circuit.library.MCXGate`, which caused - these gates to be incorrectly transpiled and simulated. - -- By default the preset passmanagers in - :mod:`qiskit.transpiler.preset_passmanagers` are using - :class:`~qiskit.transpiler.passes.UnrollCustomDefinitions` and - :class:`~qiskit.transpiler.passes.BasisTranslator` to handle basis changing - instead of the previous default :class:`~qiskit.transpiler.passes.Unroller`. - This was done because the new passes are more flexible and allow targeting - any basis set, however the output may differ. To use the previous default - you can set the ``translation_method`` kwarg on - :func:`~qiskit.compiler.transpile` to ``'unroller'``. - -- The :func:`qiskit.converters.circuit_to_gate` and - :func`qiskit.converters.circuit_to_instruction` converter functions - had previously automatically included the generated gate or instruction - in the active ``SessionEquivalenceLibrary``. These converters now accept - an optional ``equivalence_library`` keyword argument to specify if and - where the converted instances should be registered. The default behavior - has changed to not register the converted instance. - -- The default value of the ``cregbundle`` kwarg for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function has been changed - to ``True``. This means that by default the classical bits in the - circuit diagram will now be bundled by default, for example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - - circ = QuantumCircuit(4) - circ.x(0) - circ.h(1) - circ.measure_all() - circ.draw(output='mpl') - - If you want to have your circuit drawing retain the previous behavior - and show each classical bit in the diagram you can set the ``cregbundle`` - kwarg to ``False``. For example: - - .. code-block:: python - - from qiskit.circuit import QuantumCircuit - - circ = QuantumCircuit(4) - circ.x(0) - circ.h(1) - circ.measure_all() - circ.draw(output='mpl', cregbundle=False) - -- :class:`~qiskit.pulse.Schedule` plotting with - :py:meth:`qiskit.pulse.Schedule.draw` and - :func:`qiskit.visualization.pulse_drawer` will no - longer display the event table by default. This can be reenabled by setting - the ``table`` kwarg to ``True``. - -- The pass :class:`~qiskit.transpiler.passes.RemoveResetInZeroState` was - previously included in the preset pass manager - :func:`~qiskit.transpiler.preset_passmanagers.level_0_pass_manager` which - was used with the ``optimization_level=0`` for - :func:`~qiskit.compiler.transpile` and :func:`~qiskit.execute.execute` - functions. However, - :class:`~qiskit.transpiler.passes.RemoveResetInZeroState` is an - optimization pass and should not have been included in optimization level - 0 and was removed. If you need to run :func:`~qiskit.compiler.transpile` - with :class:`~qiskit.transpiler.passes.RemoveResetInZeroState` either use - a custom pass manager or ``optimization_level`` 1, 2, or 3. - -- The deprecated kwarg ``line_length`` for the - :func:`qiskit.visualization.circuit_drawer` function and - :meth:`qiskit.circuit.QuantumCircuit.draw` method has been removed. It - had been deprecated since the 0.10.0 release. Instead you can use the - ``fold`` kwarg to adjust the width of the circuit diagram. - -- The ``'mpl'`` output mode for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`~qiskit.visualization.circuit_drawer` now requires the - `pylatexenc `__ - library to be installed. This was already an optional dependency for - visualization, but was only required for the ``'latex'`` output mode - before. It is now also required for the matplotlib drawer because it is - needed to handle correctly sizing gates with matplotlib's - `mathtext `__ - labels for gates. - -- The deprecated ``get_tokens`` methods for the :class:`qiskit.qasm.Qasm` - and :class:`qiskit.qasm.QasmParser` has been removed. These methods have - been deprecated since the 0.9.0 release. The - :meth:`qiskit.qasm.Qasm.generate_tokens` and - :meth:`qiskit.qasm.QasmParser.generate_tokens` methods should be used - instead. - -- The deprecated kwarg ``channels_to_plot`` for - :meth:`qiskit.pulse.Schedule.draw`, - :meth:`qiskit.pulse.Instruction.draw`, - ``qiskit.visualization.pulse.matplotlib.ScheduleDrawer.draw`` and - :func:`~qiskit.visualization.pulse_drawer` has been removed. The kwarg - has been deprecated since the 0.11.0 release and was replaced by - the ``channels`` kwarg, which functions identically and should be used - instead. - -- The deprecated ``circuit_instruction_map`` attribute of the - :class:`qiskit.providers.models.PulseDefaults` class has been removed. - This attribute has been deprecated since the 0.12.0 release and was - replaced by the ``instruction_schedule_map`` attribute which can be used - instead. - -- The ``union`` method of :py:class:`~qiskit.pulse.Schedule` and - :py:class:`~qiskit.pulse.Instruction` have been deprecated since - the 0.12.0 release and have now been removed. Use - :meth:`qiskit.pulse.Schedule.insert` and - :meth:`qiskit.pulse.Instruction.meth` methods instead with the - kwarg``time=0``. - -- The deprecated ``scaling`` argument to the ``draw`` method of - :py:class:`~qiskit.pulse.Schedule` and :py:class:`~qiskit.pulse.Instruction` - has been replaced with ``scale`` since the 0.12.0 release and now has been - removed. Use the ``scale`` kwarg instead. - -- The deprecated ``period`` argument to :py:mod:`qiskit.pulse.library` functions - have been replaced by ``freq`` since the 0.13.0 release and now removed. Use the - ``freq`` kwarg instead of ``period``. - -- The ``qiskit.pulse.commands`` module containing ``Commands`` classes - was deprecated in the 0.13.0 release and has now been removed. You will - have to upgrade your Pulse code if you were still using commands. For - example: - - .. list-table:: - :header-rows: 2 - - * - Old - - New - * - ``Command(args)(channel)`` - - ``Instruction(args, channel)`` - * - .. code-block:: python - - Acquire(duration)(AcquireChannel(0)) - - .. code-block:: python - - Acquire(duration, AcquireChannel(0)) - * - .. code-block:: python - - Delay(duration)(channel) - - .. code-block:: python - - Delay(duration, channel) - * - .. code-block:: python - - FrameChange(angle)(DriveChannel(0)) - - .. code-block:: python - - # FrameChange was also renamed - ShiftPhase(angle, DriveChannel(0)) - * - .. code-block:: python - - Gaussian(...)(DriveChannel(0)) - - .. code-block:: python - - # Pulses need to be `Play`d - Play(Gaussian(...), DriveChannel(0)) - -- All classes and function in the ``qiskit.tool.qi`` module were deprecated - in the 0.12.0 release and have now been removed. Instead use the - :mod:`qiskit.quantum_info` module and the new methods and classes that - it has for working with quantum states and operators. - -- The ``qiskit.quantum_info.basis_state`` and - ``qiskit.quantum_info.projector`` functions are deprecated as of - Qiskit Terra 0.12.0 as are now removed. Use the - :class:`qiskit.quantum_info.QuantumState` and its derivatives - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` to work with states. - -- The interactive plotting functions from :mod:`qiskit.visualization`, - ``iplot_bloch_multivector``, ``iplot_state_city``, ``iplot_state_qsphere``, - ``iplot_state_hinton``, ``iplot_histogram``, ``iplot_state_paulivec`` now - are just deprecated aliases for the matplotlib based equivalents and are - no longer interactive. The hosted static JS code that these functions - relied on has been removed and they no longer could work. A normal - deprecation wasn't possible because the site they depended on no longer - exists. - -- The validation components using marshmallow from :mod:`qiskit.validation` - have been removed from terra. Since they are no longer used to build - any objects in terra. - -- The marshmallow schema classes in :mod:`qiskit.result` have been removed - since they are no longer used by the :class:`qiskit.result.Result` class. - -- The output of the :meth:`~qiskit.result.Result.to_dict` method for the - :class:`qiskit.result.Result` class is no longer in a format for direct - JSON serialization. Depending on the content contained in instances of - these classes there may be types that the default JSON encoder doesn't - know how to handle, for example complex numbers or numpy arrays. If you're - JSON serializing the output of the ``to_dict()`` method directly you should - ensure that your JSON encoder can handle these types. - -- The option to acquire multiple qubits at once was deprecated in the 0.12.0 - release and is now removed. Specifically, the init args ``mem_slots`` and - ``reg_slots`` have been removed from - :class:`qiskit.pulse.instructions.Acquire`, and ``channel``, ``mem_slot`` - and ``reg_slot`` will raise an error if a list is provided as input. - -- Support for the use of the ``USE_RETWORKX`` environment variable which was - introduced in the 0.13.0 release to provide an optional fallback to the - legacy `networkx `__ based - :class:`qiskit.dagcircuit.DAGCircuit` implementation - has been removed. This flag was only intended as provide a relief valve - for any users that encountered a problem with the new implementation for - one release during the transition to retworkx. - -- The module within :mod:`qiskit.pulse` responsible for schedule->schedule transformations - has been renamed from ``reschedule.py`` to ``transforms.py``. The previous import - path has been deprecated. To upgrade your code:: - - from qiskit.pulse.rescheduler import - - should be replaced by:: - - from qiskit.pulse.transforms import - -- In previous releases a :class:`~qiskit.transpiler.PassManager` - did not allow ``TransformationPass`` classes to modify the - :class:`~qiskit.transpiler.PropertySet`. This restriction has been lifted - so a ``TransformationPass`` class now has read and write access to both - the :class:`~qiskit.transpiler.PropertySet` and - :class:`~qiskit.transpiler.DAGCircuit` during - :meth:`~qiskit.transpiler.PassManager.run`. This change was made to - more efficiently facilitate ``TransformationPass`` classes that have an - internal state which may be necessary for later passes in the - :class:`~qiskit.transpiler.PassManager`. Without this change a second - redundant ``AnalysisPass`` would have been necessary to recreate the - internal state, which could add significant overhead. - -.. _Release Notes_0.15.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The name of the first positional parameter for the - :mod:`qiskit.visualization` functions - :func:`~qiskit.visualization.plot_state_hinton`, - :func:`~qiskit.visualization.plot_bloch_multivector`, - :func:`~qiskit.visualization.plot_state_city`, - :func:`~qiskit.visualization.plot_state_paulivec`, and - :func:`~qiskit.visualization.plot_state_qsphere` has been renamed from - ``rho`` to ``state``. Passing in the value by name to ``rho`` is deprecated - and will be removed in a future release. Instead you should either pass - the argument positionally or use the new parameter name ``state``. - -- The ``qiskit.pulse.pulse_lib`` module has been deprecated and will be - removed in a future release. It has been renamed to - :py:mod:`qiskit.pulse.library` which should be used instead. - -- The :class:`qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.mirror` has been deprecated and will - be removed in a future release. The method - :meth:`qiskit.circuit.QuantumCircuit.reverse_ops` should be used instead, - since mirroring could be confused with swapping the output qubits of the - circuit. The :meth:`~qiskit.circuit.QuantumCircuit.reverse_ops` method - only reverses the order of gates that are applied instead of mirroring. - -- The :meth:`~qiskit.dagcircuit.DAGCircuit.qubits` and - :meth:`~qiskit.dagcircuit.DAGCircuit.clbits` methods of - :class:`qiskit.dagcircuit.DAGCircuit` have been deprecated and will be - removed in a future release. They have been replaced with properties of - the same name, :attr:`qiskit.dagcircuit.DAGCircuit.qubits` and - :attr:`qiskit.dagcircuit.DAGCircuit.clbits`, and are cached so - accessing them is much faster. - -- The ``get_sample_pulse`` method for - ``qiskit.pulse.library.ParametricPulse`` derived classes (for example - :class:`~qiskit.pulse.library.GaussianSquare`) has been deprecated and - will be removed in a future release. It has been replaced by the - ``get_waveform`` method (for example - :meth:`~qiskit.pulse.library.GaussianSquare.get_waveform`) which should - behave identically. - -- The use of the optional ``condition`` argument on - :class:`qiskit.dagcircuit.DAGNode`, - :meth:`qiskit.dagcircuit.DAGCircuit.apply_operation_back`, and - :meth:`qiskit.dagcircuit.DAGCircuit.apply_operation_front` has been - deprecated and will be removed in a future release. Instead the - ``control`` set in :class:`qiskit.circuit.Instruction` instances being - added to a :class:`~qiskit.dagcircuit.DAGCircuit` should be used. - -- The ``set_atol`` and ``set_rtol`` class methods of the - :class:`qiskit.quantum_info.BaseOperator` and - :class:`qiskit.quantum_info.QuantumState` classes (and - their subclasses such as :class:`~qiskit.quantum_info.Operator` - and :class:`qiskit.quantum_info.DensityMatrix`) are deprecated and will - be removed in a future release. Instead the value for the attributes - ``.atol`` and ``.rtol`` should be set on the class instead. For example:: - - from qiskit.quantum_info import ScalarOp - - ScalarOp.atol = 3e-5 - op = ScalarOp(2) - -- The interactive plotting functions from :mod:`qiskit.visualization`, - ``iplot_bloch_multivector``, ``iplot_state_city``, ``iplot_state_qsphere``, - ``iplot_state_hinton``, ``iplot_histogram``, ``iplot_state_paulivec`` have - been deprecated and will be removed in a future release. The matplotlib - based equivalent functions from :mod:`qiskit.visualization`, - :func:`~qiskit.visualization.plot_bloch_multivector`, - :func:`~qiskit.visualization.plot_state_city`, - :func:`~qiskit.visualization.plot_state_qsphere`, - :func:`~qiskit.visualization.plot_state_hinton`, - :func:`~qiskit.visualization.plot_state_histogram`, and - :func:`~qiskit.visualization.plot_state_paulivec` should be used instead. - -- The properties ``acquires``, ``mem_slots``, and ``reg_slots`` of the - :class:`qiskit.pulse.instructions.Acquire` pulse instruction have been - deprecated and will be removed in a future release. They are just - duplicates of :attr:`~qiskit.pulse.instructions.Acquire.channel`, - :attr:`~qiskit.pulse.instructions.Acquire.mem_slot`, - and :attr:`~qiskit.pulse.instructions.Acquire.reg_slot` respectively - now that previously deprecated support for using multiple qubits in a - single :class:`~qiskit.pulse.instructions.Acquire` instruction has been - removed. - -- The ``SamplePulse`` class from :mod:`qiskit.pulse` has been renamed to - :py:class:`~qiskit.pulse.library.Waveform`. ``SamplePulse`` is deprecated - and will be removed in a future release. - -- The style dictionary key ``cregbundle`` has been deprecated and will be - removed in a future release. This has been replaced by the - kwarg ``cregbundle`` added to the - :func:`qiskit.visualization.circuit_drawer` function and the - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.draw`. - - -.. _Release Notes_0.15.0_Bug Fixes: - -Bug Fixes ---------- - -- The :class:`qiskit.circuit.QuantumCircuit` method - :attr:`~qiskit.circuit.QuantumCircuit.num_nonlocal_gates` previously - included multi-qubit :class:`qiskit.circuit.Instruction` objects - (for example, :class:`~qiskit.circuit.library.Barrier`) in its count of - non-local gates. This has been corrected so that only non-local - :class:`~qiskit.circuit.Gate` objects are counted. - Fixes `#4500 `__ - -- :class:`~qiskit.circuit.ControlledGate` instances with a set - ``ctrl_state`` were in some cases not being evaluated as equal, even if the - compared gates were equivalent. This has been resolved so that - Fixes `#4573 `__ - -- When accessing a bit from a - :class:`qiskit.circuit.QuantumRegister` or - :class:`qiskit.circuit.ClassicalRegister` by index when using numpy - `integer types` `__ - would previously raise a ``CircuitError`` exception. This has been - resolved so numpy types can be used in addition to Python's built-in - ``int`` type. - Fixes `#3929 `__. - -- A bug was fixed where only the first :class:`qiskit.pulse.configuration.Kernel` - or :class:`qiskit.pulse.configuration.Discriminator` for an - :class:`qiskit.pulse.Acquire` was used when there were multiple Acquires - at the same time in a :class:`qiskit.pulse.Schedule`. - -- The SI unit use for constructing :py:class:`qiskit.pulse.SetFrequency` - objects is in Hz, but when a :class:`~qiskit.qobj.PulseQobjInstruction` - object is created from a :py:class:`~qiskit.pulse.SetFrequency` instance - it needs to be converted to GHz. This conversion was missing from previous - releases and has been fixed. - -- Previously it was possible to set the number of control qubits to zero in - which case the the original, potentially non-controlled, operation would be - returned. This could cause an ``AttributeError`` to be raised if the caller - attempted to access an attribute which only - :class:`~qiskit.circuit.ControlledGate` object have. This has been fixed - by adding a getter and setter for - :attr:`~qiskit.circuit.ControlledGate.num_ctrl_qubits` to validate - that a valid value is being used. - Fixes `#4576 `__ - -- Open controls were implemented by modifying a :class:`~qiskit.circuit.Gate` - objects :attr:`~qiskit.circuit.Gate.definition`. However, when the gate - already exists in the basis set, this definition was not used, which - resulted in incorrect circuits being sent to a backend after transpilation. - This has been fixed by modifying the :class:`~qiskit.transpiler.Unroller` - pass to use the definition if it encounters a controlled gate with open - controls. - Fixes `#4437 `__ - -- The ``insert_barriers`` keyword argument in the - :class:`~qiskit.circuit.library.ZZFeatureMap` class didn't actually insert - barriers in between the Hadamard layers and evolution layers. This has been - fixed so that barriers are now properly inserted. - -- Fixed issue where some gates with three or more qubits would fail to compile - in certain instances. Refer to - `#4577 `_. - -- Fixes issue where initializing or evolving - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes by circuits by - circuit containing :class:`~qiskit.circuit.Barrier` instructions would - raise an exception. Fixes - `#4461 `__ - -- Previously when a :class:`~qiskit.circuit.QuantumCircuit` contained a - :class:`~qiskit.circuit.Gate` with a classical condition the transpiler - would sometimes fail when using ``optimization_level=3`` on - :func:`~qiskit.compiler.transpile` or - :func:`~qiskit.execute.execute` raising an ``UnboundLocalError``. This has - been fixed by updating the - :class:`~qiskit.transpiler.passes.ConsolidateBlocks` pass to account for - the classical condition. - Fixes `#4672 `_. - -- In some situations long gate and register names would overflow, or leave - excessive empty space around them when using the ``'mpl'`` output backend - for the :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function. This has been fixed - by using correct text widths for a proportional font. Fixes - `#4611 `__, - `#4605 `__, - `#4545 `__, - `#4497 `__, - `#4449 `__, and - `#3641 `__. - -- When using the ``style` kwarg on the - :meth:`qiskit.circuit.QuantumCircuit.draw` or - :func:`qiskit.visualization.circuit_drawer` with the ``'mpl'`` output - backend the dictionary key ``'showindex'`` set to ``True``, the index - numbers at the top of the column did not line up properly. This has been - fixed. - -- When using ``cregbunde=True`` with the ``'mpl'`` output backend for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function and measuring onto - a second fold, the measure arrow would overwrite the creg count. The count - was moved to the left to prevent this. Fixes - `#4148 `__. - -- When using the ``'mpl'`` output backend for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function - :class:`~qiskit.circuit.library.CSwapGate` gates and a controlled - :class:`~qiskit.circuit.library.RZZGate` gates now display with their - appropriate symbols instead of in a box. - -- When using the ``'mpl'`` output backend for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function controlled gates - created using the :meth:`~qiskit.circuit.QuantumCircuit.to_gate` method - were not properly spaced and could overlap with other gates in the circuit - diagram. This issue has been fixed. - -- When using the ``'mpl'`` output backend for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function - gates with arrays as parameters, such as - :class:`~qiskit.extensions.HamiltonianGate`, no longer display with - excessive space around them. Fixes - `#4352 `__. - -- When using the ``'mpl'`` output backend for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function - generic gates created by directly instantiating :class:`qiskit.circuit.Gate` - method now display the proper background color for the gate. Fixes - `#4496 `__. - -- When using the ``'mpl'`` output backend for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function - an ``AttributeError`` that occurred when using - :class:`~qiskit.extensions.Isometry` or :class:`~qiskit.extensions.Initialize` - has been fixed. Fixes - `#4439 `__. - -- When using the ``'mpl'`` output backend for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function - some open-controlled gates did not properly display the open controls. - This has been corrected so that open controls are properly displayed - as open circles. Fixes - `#4248 `__. - -- When using the ``'mpl'`` output backend for the - :meth:`qiskit.circuit.QuantumCircuit.draw` method and - :func:`qiskit.visualization.circuit_drawer` function - setting the ``fold`` kwarg to -1 will now properly display the circuit - without folding. Fixes - `#4506 `__. - -- Parametric pulses from :mod:`qiskit.pulse.library.discrete` - now have zero ends of parametric pulses by default. The endpoints are - defined such that for a function :math:`f(x)` then - :math:`f(-1) = f(duration + 1) = 0`. - Fixes `#4317 `__ - - -.. _Release Notes_0.15.0_Other Notes: - -Other Notes ------------ - -- The :class:`qiskit.result.Result` class which was previously constructed - using the marshmallow library has been refactored to not depend on - marshmallow anymore. This new implementation should be a seamless transition - but some specific behavior that was previously inherited from marshmallow - may not work. Please file issues for any incompatibilities found. - -Aer 0.6.1 -========= - -.. _Release Notes_0.6.0_Prelude: - -Prelude -------- - -This 0.6.0 release includes numerous performance improvements for all -simulators in the Aer provider and significant changes to the build system -when building from source. The main changes are support for SIMD -vectorization, approximation in the matrix product state method via -bond-dimension truncation, more efficient Pauli expectation value -computation, and greatly improved efficiency in Python conversion of -C++ result objects. The build system was upgraded to use the -`Conan `__ to manage common C++ dependencies when -building from source. - -.. _Release Notes_0.6.0_New Features: - -New Features ------------- - -- Add density matrix snapshot support to "statevector" and "statevector_gpu" - methods of the QasmSimulator. - -- Allow density matrix snapshots on specific qubits, not just all qubits. - This computes the partial trace of the state over the remaining qubits. - -- Adds Pauli expectation value snapshot support to the `"density_matrix"` - simulation method of the :class:`qiskit.providers.aer.QasmSimulator`. - Add snapshots to circuits using the - :class:`qiskit.providers.aer.extensions.SnapshotExpectationValue` - extension. - -- Greatly improves performance of the Pauli expectation value snapshot - algorithm for the `"statevector"`, `"statevector_gpu`, `"density_matrix"`, - and `"density_matrix_gpu"` simulation methods of the - :class:`qiskit.providers.aer.QasmSimulator`. - -- Enable the gate-fusion circuit optimization from the - :class:`qiskit.providers.aer.QasmSimulator` in both the - :class:`qiskit.providers.aer.StatevectorSimulator` and - :class:`qiskit.providers.aer.UnitarySimulator` backends. - -- Improve the performance of average snapshot data in simulator results. - This effects probability, Pauli expectation value, and density matrix snapshots - using the following extensions: - - * :class:`qiskit.providers.aer.extensions.SnapshotExpectationValue` - * :class:`qiskit.providers.aer.extensions.SnapshotProbabilities` - * :class:`qiskit.providers.aer.extensions.SnapshotDensityMatrix` - -- Add move constructor and improve memory usage of the C++ matrix class - to minimize copies of matrices when moving output of simulators into results. - -- Improve performance of unitary simulator. - -- Add approximation to the `"matrix_product_state"` simulation method of the - :class:`~qiskit.providers.aer.QasmSimulator` to limit the bond-dimension of - the MPS. - - There are two modes of approximation. Both discard the smallest - Schmidt coefficients following the SVD algorithm. - There are two parameters that control the degree of approximation: - ``"matrix_product_state_max_bond_dimension"`` (int): Sets a limit - on the number of Schmidt coefficients retained at the end of - the svd algorithm. Coefficients beyond this limit will be discarded. - (Default: None, i.e., no limit on the bond dimension). - ``"matrix_product_state_truncation_threshold"`` (double): - Discard the smallest coefficients for which the sum of - their squares is smaller than this threshold. - (Default: 1e-16). - -- Improve the performance of measure sampling when using the - `"matrix_product_state"` :class:`~qiskit.providers.aer.QasmSimulator` - simulation method. - -- Add support for ``Delay``, ``Phase`` and ``SetPhase`` pulse instructions - to the :class:`qiskit.providers.aer.PulseSimulator`. - -- Improve the performance of the :class:`qiskit.providers.aer.PulseSimulator` - by caching calls to RHS function - -- Introduce alternate DE solving methods, specifiable through ``backend_options`` - in the :class:`qiskit.providers.aer.PulseSimulator`. - -- Improve performance of simulator result classes by using move semantics - and removing unnecessary copies that were happening when combining results - from separate experiments into the final result object. - -- Greatly improve performance of pybind11 conversion of simulator results by - using move semantics where possible, and by moving vector and matrix results - to Numpy arrays without copies. - -- Change the RNG engine for simulators from 32-bit Mersenne twister to - 64-bit Mersenne twister engine. - -- Improves the performance of the `"statevector"` simulation method of the - :class:`qiskit.providers.aer.QasmSimulator` and - :class:`qiskit.providers.aer.StatevectorSimulator` by using SIMD - intrinsics on systems that support the AVX2 instruction set. AVX2 - support is automatically detected and enabled at runtime. - - -.. _Release Notes_0.6.0_Upgrade Notes: - -Upgrade Notes -------------- - -- Changes the build system to use the - `Conan package manager `__. - This tool will handle most of the dependencies needed by the C++ source - code. Internet connection may be needed for the first build or when - dependencies are added or updated, in order to download the required - packages if they are not in your Conan local repository. - - When building the standalone version of qiskit-aer you must install conan - first with: - - .. code-block:: bash - - pip install conan - -- Changes how transpilation passes are handled in the C++ Controller classes - so that each pass must be explicitly called. This allows for greater - customization on when each pass should be called, and with what parameters. - In particular this enables setting different parameters for the gate - fusion optimization pass depending on the QasmController simulation method. - -- Add ``gate_length_units`` kwarg to - :meth:`qiskit.providers.aer.noise.NoiseModel.from_device` - for specifying custom ``gate_lengths`` in the device noise model function - to handle unit conversions for internal code. - -- Add Controlled-Y ("cy") gate to the Stabilizer simulator methods supported - gateset. - -- For Aer's backend the jsonschema validation of input qobj objects from - terra is now opt-in instead of being enabled by default. If you want - to enable jsonschema validation of qobj set the ``validate`` kwarg on - the :meth:`qiskit.providers.aer.QasmSimualtor.run` method for the backend - object to ``True``. - -- Adds an OpSet object to the base simulator State class to allow easier - validation of instructions, gates, and snapshots supported by simulators. - -- Refactor OpSet class. Moved OpSet to separate header file and add - ``contains`` and ``difference`` methods based on ``std::set::contains`` - and ``std::algorithm::set_difference``. These replace the removed invalid - and validate instructions from OpSet, but with the order reversed. It - returns a list of other ops not in current opset rather than opset - instructions not in the other. - -- Improves how measurement sampling optimization is checked. The expensive - part of this operation is now done once during circuit construction where - rather than multiple times during simulation for when checking memory - requirements, simulation method, and final execution. - - -.. _Release Notes_0.6.0_Bug Fixes: - -Bug Fixes ---------- - -- Remove "extended_stabilizer" from the automatically selected simulation - methods. This is needed as the extended stabilizer method is not exact - and may give incorrect results for certain circuits unless the user - knows how to optimize its configuration parameters. - - The automatic method now only selects from "stabilizer", "density_matrix", - and "statevector" methods. If a non-Clifford circuit that is too large for - the statevector method is executed an exception will be raised suggesting - you could try explicitly using the "extended_stabilizer" or - "matrix_product_state" methods instead. - -- Disables gate fusion for the matrix product state simulation method as this - was causing issues with incorrect results being returned in some cases. - -- Fixes a bug causing incorrect channel evaluation in the - :class:`qiskit.providers.aer.PulseSimulator`. - -- Fixes several minor bugs for Hamiltonian parsing edge cases in the - :class:`qiskit.providers.aer.pulse.system_models.hamiltonian_model.HamiltonianModel` - class. - -Ignis 0.4.0 -=========== - -.. _Release Notes_0.4.0_Prelude: - -Prelude -------- - -The main change made in this release is a refactor of the Randomized -Benchmarking code to integrate the updated Clifford class -:class:`qiskit.quantum_info.Clifford` from Terra and to improve the -CNOT-Dihedral class. - - -.. _Release Notes_0.4.0_New Features: - -New Features ------------- - -- The :func:`qiskit.ignis.verification.randomized_benchmarking.randomized_benchmarking_seq` - function was refactored to use the updated Clifford class :class:`~qiskit.quantum_info.Clifford`, - to allow efficient Randomized Benchmarking (RB) on Clifford sequences with more than 2 qubits. - In addition, the code of the CNOT-Dihedral class - :class:`qiskit.ignis.verification.randomized_benchmarking.CNOTDihedral` - was refactored to make it more efficient, by using numpy arrays, as well not using pre-generated - pickle files storing all the 2-qubit group elements. - The :func:`qiskit.ignis.verification.randomized_benchmarking.randomized_benchmarking_seq` - function has a new kwarg ``rand_seed`` which can be used to specify a seed for the random number - generator used to generate the RB circuits. This can be useful for having a reproducible circuit. - -- The :func:`qiskit.ignis.verification.qv_circuits` function has a new - kwarg ``seed`` which can be used to specify a seed for the random number - generator used to generate the Quantum Volume circuits. This can be useful - for having a reproducible circuit. - - -.. _Release Notes_0.4.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The :func:`qiskit.ignis.verification.randomized_benchmarking.randomized_benchmarking_seq` - function is now using the updated Clifford class :class:`~qiskit.quantum_info.Clifford` - and the updated CNOT-Dihedral class - :class:`qiskit.ignis.verification.randomized_benchmarking.CNOTDihedral` to construct its - output instead of using pre-generated group tables for the Clifford and CNOT-Dihedral - group elements, which were stored in pickle files. - This may result in subtle differences from the output from the previous version. - -- A new requirement `scikit-learn `__ has - been added to the requirements list. This dependency was added in the 0.3.0 - release but wasn't properly exposed as a dependency in that release. This - would lead to an ``ImportError`` if the - :mod:`qiskit.ignis.measurement.discriminator.iq_discriminators` module was - imported. This is now correctly listed as a dependency so that - ``scikit-learn`` will be installed with qiskit-ignis. - -- The :func:`qiskit.ignis.verification.qv_circuits` function is now using - the circuit library class :class:`~qiskit.circuit.library.QuantumVolume` - to construct its output instead of building the circuit from scratch. - This may result in subtle differences from the output from the previous - version. - -- Tomography fitters can now also get list of `Result` objects instead of a single `Result` - as requested in `issue #320 `_. - - -.. _Release Notes_0.4.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The kwarg ``interleaved_gates`` for the - :func:`qiskit.ignis.verification.randomized_benchmarking.randomized_benchmarking_seq` - function has been deprecated and will be removed in a future release. - It is superseded by ``interleaved_elem``. - The helper functions :class:`qiskit.ignis.verification.randomized_benchmarking.BasicUtils`, - :class:`qiskit.ignis.verification.randomized_benchmarking.CliffordUtils` and - :class:`qiskit.ignis.verification.randomized_benchmarking.DihedralUtils` were deprecated. - These classes are superseded by :class:`qiskit.ignis.verification.randomized_benchmarking.RBgroup` - that handles the group operations needed for RB. - The class :class:`qiskit.ignis.verification.randomized_benchmarking.Clifford` - is superseded by :class:`~qiskit.quantum_info.Clifford`. - -- The kwargs ``qr`` and ``cr`` for the - :func:`qiskit.ignis.verification.qv_circuits` function have been deprecated - and will be removed in a future release. These kwargs were documented as - being used for specifying a :class:`qiskit.circuit.QuantumRegister` and - :class:`qiskit.circuit.ClassicalRegister` to use in the generated Quantum - Volume circuits instead of creating new ones. However, the parameters were - never actually respected and a new Register would always be created - regardless of whether they were set or not. This behavior is unchanged and - these kwargs still do not have any effect, but are being deprecated prior - to removal to avoid a breaking change for users who may have been setting - either. - -- Support for passing in subsets of qubits as a list in the ``qubit_lists`` - parameter for the :func:`qiskit.ignis.verification.qv_circuits` function - has been deprecated and will removed in a future release. In the past - this was used to specify a layout to run the circuit on a device. In - other words if you had a 5 qubit device and wanted to run a 2 qubit - QV circuit on qubits 1, 3, and 4 of that device. You would pass in - ``[1, 3, 4]`` as one of the lists in ``qubit_lists``, which would - generate a 5 qubit virtual circuit and have qv applied to qubits 1, 3, - and 4 in that virtual circuit. However, this functionality is not necessary - and overlaps with the concept of ``initial_layout`` in the transpiler and - whether a circuit has been embedded with a layout set. Moving forward - instead you should just run :func:`~qiskit.compiler.transpile` or - :func:`~qiskit.execute.execute` with initial layout set to do this. For - example, running the above example would become:: - - from qiskit import execute - from qiskit.ignis.verification import qv_circuits - - initial_layout = [1, 3, 4] - qv_circs, _ = qv_circuits([list(range3)]) - execute(qv_circuits, initial_layout=initial_layout) - - -.. _Release Notes_0.4.0_Bug Fixes: - -Bug Fixes ---------- - -- Fix a bug of the position of measurement pulses inserted by - py:func:`qiskit.ignis.characterization.calibrations.pulse_schedules.drag_schedules`. - Fixes `#465 `__ - -Aqua 0.7.5 -========== - -.. _Release Notes_0.7.5_New Features: - -New Features ------------- - -- Removed soft dependency on CPLEX in ADMMOptimizer. Now default optimizers used by ADMMOptimizer - are MinimumEigenOptimizer for QUBO problems and SlsqpOptimizer as a continuous optimizer. You - can still use CplexOptimizer as an optimizer for ADMMOptimizer, but it should be set explicitly. - -- New Yahoo! finance provider created. - -- Introduced ``QuadraticProgramConverter`` which is an abstract class for converters. - Added ``convert``/``interpret`` methods for converters instead of ``encode``/``decode``. - Added ``to_ising`` and ``from_ising`` to ``QuadraticProgram`` class. - Moved all parameters from ``convert`` to constructor except ``name``. - Created setter/getter for converter parameters. - Added ``auto_define_penalty`` and ``interpret`` for``LinearEqualityToPenalty``. - Now error messages of converters are more informative. - -- Added an SLSQP optimizer ``qiskit.optimization.algorithms.SlsqpOptimizer`` as a wrapper - of the corresponding SciPy optimization method. This is a classical optimizer, does not depend - on quantum algorithms and may be used as a replacement for ``CobylaOptimizer``. - -- Cobyla optimizer has been modified to accommodate a multi start feature introduced - in the SLSQP optimizer. By default, the optimizer does not run in the multi start mode. - -- The ``SummedOp`` does a mathematically more correct check for equality, where - expressions such as ``X + X == 2*X`` and ``X + Z == Z + X`` evaluate to ``True``. - - -.. _Release Notes_0.7.5_Deprecation Notes: - -Deprecation Notes ------------------ - -- GSLS optimizer class deprecated ``__init__`` parameter ``max_iter`` in favor of ``maxiter``. - SPSA optimizer class deprecated ``__init__`` parameter ``max_trials`` in favor of ``maxiter``. - optimize_svm function deprecated ``max_iters`` parameter in favor of ``maxiter``. - ADMMParameters class deprecated ``__init__`` parameter ``max_iter`` in favor of ``maxiter``. - -- The ising convert classes - :class:`qiskit.optimization.converters.QuadraticProgramToIsing` and - :class:`qiskit.optimization.converters.IsingToQuadraticProgram` have - been deprecated and will be removed in a future release. Instead the - :class:`qiskit.optimization.QuadraticProgram` methods - :meth:`~qiskit.optimization.QuadraticProgram.to_ising` and - :meth:`~qiskit.optimization.QuadraticPrgraom.from_ising` should be used - instead. - -- The ``pprint_as_string`` method for - :class:`qiskit.optimization.QuadraticProgram` has been deprecated and will - be removed in a future release. Instead you should just run - ``.pprint_as_string()`` on the output from - :meth:`~qiskit.optimization.QuadraticProgram.to_docplex` - -- The ``prettyprint`` method for - :class:`qiskit.optimization.QuadraticProgram` has been deprecated and will - be removed in a future release. Instead you should just run - ``.prettyprint()`` on the output from - :meth:`~qiskit.optimization.QuadraticProgram.to_docplex` - -.. _Release Notes_0.7.5_Bug Fixes: - -Bug Fixes ---------- - -- Changed in python version 3.8: On macOS, the spawn start method is now the - default. The fork start method should be considered unsafe as it can - lead to crashes in subprocesses. - However P_BFGS doesn't support spawn, so we revert to single process. - Refer to - `#1109 ` for more details. - -- Binding parameters in the ``CircuitStateFn`` did not copy - the value of ``is_measurement`` and always set ``is_measurement=False``. - This has been fixed. - -- Previously, SummedOp.to_matrix_op built a list MatrixOp's (with numpy - matrices) and then summed them, returning a single MatrixOp. Some - algorithms (for example vqe) require summing thousands of matrices, which - exhausts memory when building the list of matrices. With this change, - no list is constructed. Rather, each operand in the sum is converted to - a matrix, added to an accumulator, and discarded. - -- Changing backends in VQE from statevector to qasm_simulator or real device - was causing an error due to CircuitSampler incompatible reuse. VQE was changed - to always create a new CircuitSampler and create a new expectation in case not - entered by user. - Refer to - `#1153 ` for more details. - -- Exchange and Wikipedia finance providers were fixed to correctly handle Quandl data. - Refer to - `#775 ` for more details. - Fixes a divide by 0 error on finance providers mean vector and covariance matrix - calculations. Refer to - `#781 ` for more details. - -- The ``ListOp.combo_fn`` property has been lost in several transformations, - such as converting to another operator type, traversing, reducing or - multiplication. Now this attribute is propagated to the resulting operator. - -- The evaluation of some operator expressions, such as of ``SummedOp``s - and evaluations with the ``CircuitSampler`` did not treat coefficients - correctly or ignored them completely. E.g. evaluating - ``~StateFn(0 * (I + Z)) @ Plus`` did not yield 0 or the normalization - of ``~StateFn(I) @ ((Plus + Minus) / sqrt(2))`` missed a factor - of ``sqrt(2)``. This has been fixed. - -- ``OptimizationResult`` included some public setters and class variables - were ``Optional``. This fix makes all class variables read-only so that - mypy and pylint can check types more effectively. - ``MinimumEigenOptimizer.solve`` generated bitstrings in a result as ``str``. - This fix changed the result into ``List[float]`` as the other algorithms do. - Some public classes related to optimization algorithms were missing in - the documentation of ``qiskit.optimization.algorithms``. This fix added - all such classes to the docstring. - `#1131 ` for more details. - -- ``OptimizationResult.__init__`` did not check whether the sizes of ``x`` and - ``variables`` match or not (they should match). This fix added the check to - raise an error if they do not match and fixes bugs detected by the check. - This fix also adds missing unit tests related to ``OptimizationResult.variable_names`` - and ``OptimizationResult.variables_dict`` in ``test_converters``. - `#1167 ` for more details. - -- Fix parameter binding in the ``OperatorStateFn``, which did not bind - parameters of the underlying primitive but just the coefficients. - -- ``op.eval(other)``, where ``op`` is of type ``OperatorBase``, sometimes - silently returns a nonsensical value when the number of qubits in ``op`` - and ``other`` are not equal. This fix results in correct behavior, which - is to throw an error rather than return a value, because the input in - this case is invalid. - -- The ``construct_circuit`` method of ``VQE`` previously returned the - expectation value to be evaluated as type ``OperatorBase``. - This functionality has been moved into ``construct_expectation`` and - ``construct_circuit`` returns a list of the circuits that are evaluated - to compute the expectation value. - - -IBM Q Provider 0.8.0 -==================== - -.. _Release Notes_0.8.0_New Features: - -New Features ------------- - -- :class:`~qiskit.providers.ibmq.IBMQBackend` now has a new - :meth:`~qiskit.providers.ibmq.IBMQBackend.reservations` method that - returns reservation information for the backend, with optional filtering. - In addition, you can now use - :meth:`provider.backends.my_reservations()` - to query for your own reservations. - -- :meth:`qiskit.providers.ibmq.job.IBMQJob.result` raises an - :class:`~qiskit.providers.ibmq.job.IBMQJobFailureError` exception if - the job has failed. The exception message now contains the reason - the job failed, if the entire job failed for a single reason. - -- A new attribute ``client_version`` was added to - :class:`~qiskit.providers.ibmq.job.IBMQJob` and - :class:`qiskit.result.Result` object retrieved via - :meth:`qiskit.providers.ibmq.job.IBMQJob.result`. - ``client_version`` is a dictionary with the key being the name - and the value being the version of the client used to submit - the job, such as Qiskit. - -- The :func:`~qiskit.providers.ibmq.least_busy` function now takes a new, - optional parameter ``reservation_lookahead``. If specified or defaulted to, - a backend is considered unavailable if it has reservations in the next - ``n`` minutes, where ``n`` is the value of ``reservation_lookahead``. - For example, if the default value of 60 is used, then any - backends that have reservations in the next 60 minutes are considered unavailable. - -- :class:`~qiskit.providers.ibmq.managed.ManagedResults` now has a new - :meth:`~qiskit.providers.ibmq.managed.ManagedResults.combine_results` method - that combines results from all managed jobs and returns a single - :class:`~qiskit.result.Result` object. This ``Result`` object can - be used, for example, in ``qiskit-ignis`` fitter methods. - - -.. _Release Notes_0.8.0_Upgrade Notes: - -Upgrade Notes -------------- - -- Timestamps in the following fields are now in local time instead of UTC: - - * Backend properties returned by - :meth:`qiskit.providers.ibmq.IBMQBackend.properties`. - * Backend properties returned by - :meth:`qiskit.providers.ibmq.job.IBMQJob.properties`. - * ``estimated_start_time`` and ``estimated_complete_time`` in - :class:`~qiskit.providers.ibmq.job.QueueInfo`, returned by - :meth:`qiskit.providers.ibmq.job.IBMQJob.queue_info`. - * ``date`` in :class:`~qiskit.result.Result`, returned by - :meth:`qiskit.providers.ibmq.job.IBMQJob.result`. - - In addition, the ``datetime`` parameter for - :meth:`qiskit.providers.ibmq.IBMQBackend.properties` is also expected to be - in local time unless it has UTC timezone information. - -- ``websockets`` 8.0 or above is now required if Python 3.7 or above is used. - ``websockets`` 7.0 will continue to be used for Python 3.6 or below. - -- On Windows, the event loop policy is set to ``WindowsSelectorEventLoopPolicy`` - instead of using the default ``WindowsProactorEventLoopPolicy``. This fixes - the issue that the :meth:`qiskit.providers.ibmq.job.IBMQJob.result` method - could hang on Windows. Fixes - `#691 `_ - - -.. _Release Notes_0.8.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- Use of ``Qconfig.py`` to save IBM Quantum Experience credentials is deprecated - and will be removed in the next release. You should use ``qiskitrc`` - (the default) instead. - - -.. _Release Notes_0.8.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixes an issue wherein a call to :meth:`qiskit.providers.ibmq.IBMQBackend.jobs` - can hang if the number of jobs being returned is large. Fixes - `#674 `_ - -- Fixes an issue which would raise a ``ValueError`` when building - error maps in Jupyter for backends that are offline. Fixes - `#706 `_ - -- :meth:`qiskit.providers.ibmq.IBMQBackend.jobs` will now return the correct - list of :class:`~qiskit.providers.ibmq.job.IBMQJob` objects when the - ``status`` kwarg is set to ``'RUNNING'``. - -- The package metadata has been updated to properly reflect the dependency - on ``qiskit-terra`` >= 0.14.0. This dependency was implicitly added as - part of the 0.7.0 release but was not reflected in the package requirements - so it was previously possible to install ``qiskit-ibmq-provider`` with a - version of ``qiskit-terra`` which was too old. Fixes - `#677 `_ - -############# -Qiskit 0.19.6 -############# - -Terra 0.14.2 -============ - -No Change - -Aer 0.5.2 -========= - -No Change - -Ignis 0.3.3 -=========== - -.. _Release Notes_0.3.3_Upgrade Notes: - -Upgrade Notes -------------- - -- A new requirement `scikit-learn `__ has - been added to the requirements list. This dependency was added in the 0.3.0 - release but wasn't properly exposed as a dependency in that release. This - would lead to an ``ImportError`` if the - :mod:`qiskit.ignis.measurement.discriminator.iq_discriminators` module was - imported. This is now correctly listed as a dependency so that - ``scikit-learn`` will be installed with qiskit-ignis. - - -.. _Release Notes_0.3.3_Bug Fixes: - -Bug Fixes ---------- - -- Fixes an issue in qiskit-ignis 0.3.2 which would raise an ``ImportError`` - when :mod:`qiskit.ignis.verification.tomography.fitters.process_fitter` was - imported without ``cvxpy`` being installed. - -Aqua 0.7.3 -========== - -No Change - -IBM Q Provider 0.7.2 -==================== - -No Change - - -############# -Qiskit 0.19.5 -############# - -Terra 0.14.2 -============ - -No Change - -Aer 0.5.2 -========= - -No Change - -Ignis 0.3.2 -=========== - -Bug Fixes ---------- - -- The :meth:`qiskit.ignis.verification.TomographyFitter.fit` method has improved - detection logic for the default fitter. Previously, the ``cvx`` fitter method - was used whenever `cvxpy `__ was installed. However, - it was possible to install cvxpy without an SDP solver that would work for the - ``cvx`` fitter method. This logic has been reworked so that the ``cvx`` - fitter method is only used if ``cvxpy`` is installed and an SDP solver is present - that can be used. Otherwise, the ``lstsq`` fitter is used. - -- Fixes an edge case in - :meth:`qiskit.ignis.mitigation.measurement.fitters.MeasurementFitter.apply` - for input that has invalid or incorrect state labels that don't match - the calibration circuit. Previously, this would not error and just return - an empty result. Instead now this case is correctly caught and a - ``QiskitError`` exception is raised when using incorrect labels. - -Aqua 0.7.3 -========== - -.. _Release Notes_0.7.3_Upgrade Notes: - -Upgrade Notes -------------- - -- The `cvxpy `__ dependency which is required for - the svm classifier has been removed from the requirements list and made - an optional dependency. This is because installing cvxpy is not seamless - in every environment and often requires a compiler be installed to run. - To use the svm classifier now you'll need to install cvxpy by either - running ``pip install cvxpy<1.1.0`` or to install it with aqua running - ``pip install qiskit-aqua[cvx]``. - - -.. _Release Notes_0.7.3_Bug Fixes: - -Bug Fixes ---------- - -- The ``compose`` method of the ``CircuitOp`` used ``QuantumCircuit.combine`` which has been - changed to use ``QuantumCircuit.compose``. Using combine leads to the problem that composing - an operator with a ``CircuitOp`` based on a named register does not chain the operators but - stacks them. E.g. composing ``Z ^ 2`` with a circuit based on a 2-qubit named register yielded - a 4-qubit operator instead of a 2-qubit operator. - -- The ``MatrixOp.to_instruction`` method previously returned an operator and not - an instruction. This method has been updated to return an Instruction. - Note that this only works if the operator primitive is unitary, otherwise - an error is raised upon the construction of the instruction. - -- The ``__hash__`` method of the ``PauliOp`` class used the ``id()`` method - which prevents set comparisons to work as expected since they rely on hash - tables and identical objects used to not have identical hashes. Now, the - implementation uses a hash of the string representation inline with the - implementation in the ``Pauli`` class. - -IBM Q Provider 0.7.2 -==================== - -No Change - - -############# -Qiskit 0.19.4 -############# - -Terra 0.14.2 -============ - -.. _Release Notes_0.14.2_Upgrade Notes: - -Upgrade Notes -------------- - -- The ``circuit_to_gate`` and ``circuit_to_instruction`` converters had - previously automatically included the generated gate or instruction in the - active ``SessionEquivalenceLibrary``. These converters now accept an - optional ``equivalence_library`` keyword argument to specify if and where - the converted instances should be registered. The default behavior is not - to register the converted instance. - - -.. _Release Notes_0.14.2_Bug Fixes: - -Bug Fixes ---------- - -- Implementations of the multi-controlled X Gate (``MCXGrayCode``, - ``MCXRecursive`` and ``MCXVChain``) have had their ``name`` - properties changed to more accurately describe their - implementation (``mcx_gray``, ``mcx_recursive``, and - ``mcx_vchain`` respectively.) Previously, these gates shared the - name ``mcx` with ``MCXGate``, which caused these gates to be - incorrectly transpiled and simulated. - -- ``ControlledGate`` instances with a set ``ctrl_state`` were in some cases - not being evaluated as equal, even if the compared gates were equivalent. - This has been resolved. - -- Fixed the SI unit conversion for :py:class:`qiskit.pulse.SetFrequency`. The - ``SetFrequency`` instruction should be in Hz on the frontend and has to be - converted to GHz when ``SetFrequency`` is converted to ``PulseQobjInstruction``. - -- Open controls were implemented by modifying a gate\'s - definition. However, when the gate already exists in the basis, - this definition is not used, which yields incorrect circuits sent - to a backend. This modifies the unroller to output the definition - if it encounters a controlled gate with open controls. - -Aer 0.5.2 -========= - -No Change - -Ignis 0.3.0 -=========== - -No Change - -Aqua 0.7.2 -========== - -Prelude -------- -VQE expectation computation with Aer qasm_simulator now defaults to a -computation that has the expected shot noise behavior. - -Upgrade Notes -------------- -- `cvxpy `_ is now in the requirements list - as a dependency for qiskit-aqua. It is used for the quadratic program solver - which is used as part of the :class:`qiskit.aqua.algorithms.QSVM`. Previously - ``cvxopt`` was an optional dependency that needed to be installed to use - this functionality. This is no longer required as cvxpy will be installed - with qiskit-aqua. -- For state tomography run as part of :class:`qiskit.aqua.algorithms.HHL` with - a QASM backend the tomography fitter function - :meth:`qiskit.ignis.verification.StateTomographyFitter.fit` now gets called - explicitly with the method set to ``lstsq`` to always use the least-squares - fitting. Previously it would opportunistically try to use the ``cvx`` fitter - if ``cvxpy`` were installed. But, the ``cvx`` fitter depends on a - specifically configured ``cvxpy`` installation with an SDP solver installed - as part of ``cvxpy`` which is not always present in an environment with - ``cvxpy`` installed. -- The VQE expectation computation using qiskit-aer's - :class:`qiskit.providers.aer.extensions.SnapshotExpectationValue` instruction - is not enabled by default anymore. This was changed to be the default in - 0.7.0 because it is significantly faster, but it led to unexpected ideal - results without shot noise (see - `#1013 `_ for more - details). The default has now changed back to match user expectations. Using - the faster expectation computation is now opt-in by setting the new - ``include_custom`` kwarg to ``True`` on the - :class:`qiskit.aqua.algorithms.VQE` constructor. - -New Features ------------- -- A new kwarg ``include_custom`` has been added to the constructor for - :class:`qiskit.aqua.algorithms.VQE` and it's subclasses (mainly - :class:`qiskit.aqua.algorithms.QAOA`). When set to true and the - ``expectation`` kwarg is set to ``None`` (the default) this will enable - the use of VQE expectation computation with Aer's ``qasm_simulator`` - :class:`qiskit.providers.aer.extensions.SnapshotExpectationValue` instruction. - The special Aer snapshot based computation is much faster but with the ideal - output similar to state vector simulator. - -IBM Q Provider 0.7.2 -==================== - -No Change - -############# -Qiskit 0.19.3 -############# - -Terra 0.14.1 -============ - -No Change - -Aer 0.5.2 -========= - -Bug Fixes ---------- - -- Fixed bug with statevector and unitary simulators running a number of (parallel) - shots equal to the number of CPU threads instead of only running a single shot. - -- Fixes the "diagonal" qobj gate instructions being applied incorrectly - in the density matrix Qasm Simulator method. - -- Fixes bug where conditional gates were not being applied correctly - on the density matrix simulation method. - -- Fix bug in CZ gate and Z gate for "density_matrix_gpu" and - "density_matrix_thrust" QasmSimulator methods. - -- Fixes issue where memory requirements of simulation were not being checked - on the QasmSimulator when using a non-automatic simulation method. - -- Fixed a memory leak that effected the GPU simulator methods - -Ignis 0.3.0 -=========== - -No Change - -Aqua 0.7.1 -========== - -No Change - -IBM Q Provider 0.7.2 -==================== - -Bug Fixes ---------- - -- :meth:`qiskit.provider.ibmq.IBMQBackend.jobs` will now return the correct - list of :class:`~qiskit.provider.ibmq.job.IBMQJob` objects when the - ``status`` kwarg is set to ``'RUNNING'``. Fixes - `#523 `_ - -- The package metadata has been updated to properly reflect the dependency - on ``qiskit-terra`` >= 0.14.0. This dependency was implicitly added as - part of the 0.7.0 release but was not reflected in the package requirements - so it was previously possible to install ``qiskit-ibmq-provider`` with a - version of ``qiskit-terra`` which was too old. Fixes - `#677 `_ - -############# -Qiskit 0.19.0 -############# - -Terra 0.14.0 -============ - -.. _Release Notes_0.14.0_Prelude: - -Prelude -------- - -The 0.14.0 release includes several new features and bug fixes. The biggest -change for this release is the introduction of a quantum circuit library -in :mod:`qiskit.circuit.library`, containing some circuit families of -interest. - -The circuit library gives users access to a rich set of well-studied -circuit families, instances of which can be used as benchmarks, -as building blocks in building more complex circuits, or -as a tool to explore quantum computational advantage over classical. -The contents of this library will continue to grow and mature. - -The initial release of the circuit library contains: - -* ``standard_gates``: these are fixed-width gates commonly used as primitive - building blocks, consisting of 1, 2, and 3 qubit gates. For example - the :class:`~qiskit.circuit.library.XGate`, - :class:`~qiskit.circuit.library.RZZGate` and - :class:`~qiskit.circuit.library.CSWAPGate`. The old location of these - gates under ``qiskit.extensions.standard`` is deprecated. -* ``generalized_gates``: these are families that can generalize to arbitrarily - many qubits, for example a :class:`~qiskit.circuit.library.Permutation` or - :class:`~qiskit.circuit.library.GMS` (Global Molmer-Sorensen gate). -* ``boolean_logic``: circuits that transform basis states according to simple - Boolean logic functions, such as :class:`~qiskit.circuit.library.ADD` or - :class:`~qiskit.circuit.library.XOR`. -* ``arithmetic``: a set of circuits for doing classical arithmetic such as - :class:`~qiskit.circuit.library.WeightedAdder` and - :class:`~qiskit.circuit.library.IntegerComparator`. -* ``basis_changes``: circuits such as the quantum Fourier transform, - :class:`~qiskit.circuit.library.QFT`, that mathematically apply basis - changes. -* ``n_local``: patterns to easily create large circuits with rotation and - entanglement layers, such as :class:`~qiskit.circuit.library.TwoLocal` - which uses single-qubit rotations and two-qubit entanglements. -* ``data_preparation``: circuits that take classical input data and encode it - in a quantum state that is difficult to simulate, e.g. - :class:`~qiskit.circuit.library.PauliFeatureMap` or - :class:`~qiskit.circuit.library.ZZFeatureMap`. -* Other circuits that have proven interesting in the literature, such as - :class:`~qiskit.circuit.library.QuantumVolume`, - :class:`~qiskit.circuit.library.GraphState`, or - :class:`~qiskit.circuit.library.IQP`. - -To allow easier use of these circuits as building blocks, we have introduced -a :meth:`~qiskit.circuit.QuantumCircuit.compose` method of -:class:`qiskit.circuit.QuantumCircuit` for composition of circuits either -with other circuits (by welding them at the ends and optionally permuting -wires) or with other simpler gates:: - - >>> lhs.compose(rhs, qubits=[3, 2], inplace=True) - -.. parsed-literal:: - ┌───┐ ┌─────┐ ┌───┐ - lqr_1_0: ───┤ H ├─── rqr_0: ──■──┤ Tdg ├ lqr_1_0: ───┤ H ├─────────────── - ├───┤ ┌─┴─┐└─────┘ ├───┤ - lqr_1_1: ───┤ X ├─── rqr_1: ┤ X ├─────── lqr_1_1: ───┤ X ├─────────────── - ┌──┴───┴──┐ └───┘ ┌──┴───┴──┐┌───┐ - lqr_1_2: ┤ U1(0.1) ├ + = lqr_1_2: ┤ U1(0.1) ├┤ X ├─────── - └─────────┘ └─────────┘└─┬─┘┌─────┐ - lqr_2_0: ─────■───── lqr_2_0: ─────■───────■──┤ Tdg ├ - ┌─┴─┐ ┌─┴─┐ └─────┘ - lqr_2_1: ───┤ X ├─── lqr_2_1: ───┤ X ├─────────────── - └───┘ └───┘ - lcr_0: 0 ═══════════ lcr_0: 0 ═══════════════════════ - lcr_1: 0 ═══════════ lcr_1: 0 ═══════════════════════ - -With this, Qiskit's circuits no longer assume an implicit -initial state of :math:`|0\rangle`, and will not be drawn with this -initial state. The all-zero initial state is still assumed on a backend -when a circuit is executed. - - -.. _Release Notes_0.14.0_New Features: - -New Features ------------- - -- A new method, :meth:`~qiskit.circuit.EquivalenceLibrary.has_entry`, has been - added to the :class:`qiskit.circuit.EquivalenceLibrary` class to quickly - check if a given gate has any known decompositions in the library. - -- A new class :class:`~qiskit.circuit.library.IQP`, to construct an - instantaneous quantum polynomial circuit, has been added to the circuit - library module :mod:`qiskit.circuit.library`. - -- A new :meth:`~qiskit.circuit.QuantumCircuit.compose` method has been added - to :class:`qiskit.circuit.QuantumCircuit`. It allows - composition of two quantum circuits without having to turn one into - a gate or instruction. It also allows permutations of qubits/clbits - at the point of composition, as well as optional inplace modification. - It can also be used in place of - :meth:`~qiskit.circuit.QuantumCircuit.append()`, as it allows - composing instructions and operators onto the circuit as well. - -- :class:`qiskit.circuit.library.Diagonal` circuits have been added to the - circuit library. These circuits implement diagonal quantum operators - (consisting of non-zero elements only on the diagonal). They are more - efficiently simulated by the Aer simulator than dense matrices. - -- Add :meth:`~qiskit.quantum_info.Clifford.from_label` method to the - :class:`qiskit.quantum_info.Clifford` class for initializing as the - tensor product of single-qubit I, X, Y, Z, H, or S gates. - -- Schedule transformer :func:`qiskit.pulse.reschedule.compress_pulses` - performs an optimization pass to reduce the usage of waveform - memory in hardware by replacing multiple identical instances of - a pulse in a pulse schedule with a single pulse. - For example:: - - from qiskit.pulse import reschedule - - schedules = [] - for _ in range(2): - schedule = Schedule() - drive_channel = DriveChannel(0) - schedule += Play(SamplePulse([0.0, 0.1]), drive_channel) - schedule += Play(SamplePulse([0.0, 0.1]), drive_channel) - schedules.append(schedule) - - compressed_schedules = reschedule.compress_pulses(schedules) - -- The :class:`qiskit.transpiler.Layout` has a new method - :meth:`~qiskit.transpiler.Layout.reorder_bits` that is used to reorder a - list of virtual qubits based on the layout object. - -- Two new methods have been added to the - :class:`qiskit.providers.models.PulseBackendConfiguration` for - interacting with channels. - - * :meth:`~qiskit.providers.models.PulseBackendConfiguration.get_channel_qubits` - to get a list of all qubits operated by the given channel and - * :meth:`~qiskit.providers.models.PulseBackendConfiguration.get_qubit_channel` - to get a list of channels operating on the given qubit. - -- New :class:`qiskit.extensions.HamiltonianGate` and - :meth:`qiskit.circuit.QuantumCircuit.hamiltonian()` methods are - introduced, representing Hamiltonian evolution of the circuit - wavefunction by a user-specified Hermitian Operator and evolution time. - The evolution time can be a :class:`~qiskit.circuit.Parameter`, allowing - the creation of parameterized UCCSD or QAOA-style circuits which compile to - ``UnitaryGate`` objects if ``time`` parameters are provided. The Unitary of - a ``HamiltonianGate`` with Hamiltonian Operator ``H`` and time parameter - ``t`` is :math:`e^{-iHt}`. - -- The circuit library module :mod:`qiskit.circuit.library` now provides a - new boolean logic AND circuit, :class:`qiskit.circuit.library.AND`, and - OR circuit, :class:`qiskit.circuit.library.OR`, which implement the - respective operations on a variable number of provided qubits. - -- New fake backends are added under :mod:`qiskit.test.mock`. These include - mocked versions of ``ibmq_armonk``, ``ibmq_essex``, ``ibmq_london``, - ``ibmq_valencia``, ``ibmq_cambridge``, ``ibmq_paris``, ``ibmq_rome``, and - ``ibmq_athens``. As with other fake backends, these include snapshots of - calibration data (i.e. ``backend.defaults()``) and error data (i.e. - ``backend.properties()``) taken from the real system, and can be used for - local testing, compilation and simulation. - -- The ``last_update_date`` parameter for - :class:`~qiskit.providers.models.BackendProperties` can now also be - passed in as a ``datetime`` object. Previously only a string in - ISO8601 format was accepted. - -- Adds :meth:`qiskit.quantum_info.Statevector.from_int` and - :meth:`qiskit.quantum_info.DensityMatrix.from_int` methods that allow - constructing a computational basis state for specified system dimensions. - -- The methods on the :class:`qiskit.circuit.QuantumCircuit` class for adding - gates (for example :meth:`~qiskit.circuit.QuantumCircuit.h`) which were - previously added dynamically at run time to the class definition have been - refactored to be statically defined methods of the class. This means that - static analyzer (such as IDEs) can now read these methods. - - -.. _Release Notes_0.14.0_Upgrade Notes: - -Upgrade Notes -------------- - -- A new package, - `python-dateutil `_, is now - required and has been added to the requirements list. It is being used - to parse datetime strings received from external providers in - :class:`~qiskit.providers.models.BackendProperties` objects. - -- The marshmallow schema classes in :mod:`qiskit.providers.models` have been - removed since they are no longer used by the BackendObjects. - -- The output of the ``to_dict()`` method for the classes in - :mod:`qiskit.providers.models` is no longer in a format for direct JSON - serialization. Depending on the content contained in instances of these - class there may be numpy arrays and/or complex numbers in the fields of the dict. - If you're JSON serializing the output of the to_dict methods you should - ensure your JSON encoder can handle numpy arrays and complex numbers. This - includes: - - * :meth:`qiskit.providers.models.BackendConfiguration.to_dict` - * :meth:`qiskit.providers.models.BackendProperties.to_dict` - * :meth:`qiskit.providers.models.BackendStatus.to_dict` - * :meth:`qiskit.providers.models.QasmBackendConfiguration.to_dict` - * :meth:`qiskit.providers.models.PulseBackendConfiguration.to_dict` - * :meth:`qiskit.providers.models.UchannelLO.to_dict` - * :meth:`qiskit.providers.models.GateConfig.to_dict` - * :meth:`qiskit.providers.models.PulseDefaults.to_dict` - * :meth:`qiskit.providers.models.Command.to_dict` - * :meth:`qiskit.providers.models.JobStatus.to_dict` - * :meth:`qiskit.providers.models.Nduv.to_dict` - * :meth:`qiskit.providers.models.Gate.to_dict` - - -.. _Release Notes_0.14.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The :meth:`qiskit.dagcircuit.DAGCircuit.compose` method now takes a list - of qubits/clbits that specify the positional order of bits to compose onto. - The dictionary-based method of mapping using the ``edge_map`` argument is - deprecated and will be removed in a future release. - -- The ``combine_into_edge_map()`` method for the - :class:`qiskit.transpiler.Layout` class has been deprecated and will be - removed in a future release. Instead, the new method - :meth:`~qiskit.transpiler.Layout.reorder_bits` should be used to reorder - a list of virtual qubits according to the layout object. - -- Passing a :class:`qiskit.pulse.ControlChannel` object in via the - parameter ``channel`` for the - :class:`qiskit.providers.models.PulseBackendConfiguration` method - :meth:`~qiskit.providers.models.PulseBackendConfiguration.control` has been - deprecated and will be removed in a future release. The - ``ControlChannel`` objects are now generated from the backend configuration - ``channels`` attribute which has the information of all channels and the - qubits they operate on. Now, the method - :meth:`~qiskit.providers.models.PulseBackendConfiguration.control` - is expected to take the parameter ``qubits`` of the form - ``(control_qubit, target_qubit)`` and type ``list`` - or ``tuple``, and returns a list of control channels. - -- The ``AND`` and ``OR`` methods of :class:`qiskit.circuit.QuantumCircuit` - are deprecated and will be removed in a future release. Instead you should - use the circuit library boolean logic classes - :class:`qiskit.circuit.library.AND` amd :class:`qiskit.circuit.library.OR` - and then append those objects to your class. For example:: - - from qiskit import QuantumCircuit - from qiskit.circuit.library import AND - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - - qc_and = AND(2) - - qc.compose(qc_and, inplace=True) - -- The ``qiskit.extensions.standard`` module is deprecated and will be - removed in a future release. The gate classes in that module have been - moved to :mod:`qiskit.circuit.library.standard_gates`. - - -.. _Release Notes_0.14.0_Bug Fixes: - -Bug Fixes ---------- - -- The :class:`qiskit.circuit.QuantumCircuit` methods - :meth:`~qiskit.circuit.QuantumCircuit.inverse`, - :meth:`~qiskit.circuit.QuantumCircuit.mirror` methods, as well as - the ``QuantumCircuit.data`` setter would generate an invalid circuit when - used on a parameterized circuit instance. This has been resolved and - these methods should now work with a parameterized circuit. Fixes - `#4235 `_ - -- Previously when creating a controlled version of a standard qiskit - gate if a ``ctrl_state`` was specified a generic ``ControlledGate`` - object would be returned whereas without it a standard qiskit - controlled gate would be returned if it was defined. This PR - allows standard qiskit controlled gates to understand - ``ctrl_state``. - - Additionally, this PR fixes what might be considered a bug where - setting the ``ctrl_state`` of an already controlled gate would - assume the specified state applied to the full control width - instead of the control qubits being added. For instance,:: - - circ = QuantumCircuit(2) - circ.h(0) - circ.x(1) - gate = circ.to_gate() - cgate = gate.control(1) - c3gate = cgate.control(2, ctrl_state=0) - - would apply ``ctrl_state`` to all three control qubits instead of just - the two control qubits being added. - -- Fixed a bug in :func:`~qiskit.quantum_info.random_clifford` that stopped it - from sampling the full Clifford group. Fixes - `#4271 `_ - -- The :class:`qiskit.circuit.Instruction` method - :meth:`qiskit.circuit.Instruction.is_parameterized` method had previously - returned ``True`` for any ``Instruction`` instance which had a - :class:`qiskit.circuit.Parameter` in any element of its ``params`` array, - even if that ``Parameter`` had been fully bound. This has been corrected so - that ``.is_parameterized`` will return ``False`` when the instruction is - fully bound. - -- :meth:`qiskit.circuit.ParameterExpression.subs` had not correctly detected - some cases where substituting parameters would result in a two distinct - :class:`~qiskit.circuit.Parameters` objects in an expression with the same - name. This has been corrected so a ``CircuitError`` will be raised in these - cases. - -- Improve performance of :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` for low-qubit circuit - simulations by optimizing the class ``__init__`` methods. Fixes - `#4281 `_ - -- The function :func:`qiskit.compiler.transpile` now correctly handles when - the parameter ``basis_gates`` is set to ``None``. This will allow any gate - in the output tranpiled circuit, including gates added by the transpilation - process. Note that using this parameter may have some - unintended consequences during optimization. Some transpiler passes - depend on having a ``basis_gates`` set. For example, - :class:`qiskit.transpiler.passes.Optimize1qGates` only optimizes the chains - of u1, u2, and u3 gates and without ``basis_gates`` it is unable to unroll - gates that otherwise could be optimized: - - .. code-block:: python - - from qiskit import * - - q = QuantumRegister(1, name='q') - circuit = QuantumCircuit(q) - circuit.h(q[0]) - circuit.u1(0.1, q[0]) - circuit.u2(0.1, 0.2, q[0]) - circuit.h(q[0]) - circuit.u3(0.1, 0.2, 0.3, q[0]) - - result = transpile(circuit, basis_gates=None, optimization_level=3) - result.draw() - - .. parsed-literal:: - ┌───┐┌─────────────┐┌───┐┌─────────────────┐ - q_0: ┤ H ├┤ U2(0.1,0.3) ├┤ H ├┤ U3(0.1,0.2,0.3) ├ - └───┘└─────────────┘└───┘└─────────────────┘ - - Fixes `#3017 `_ - - -.. _Release Notes_0.14.0_Other Notes: - -Other Notes ------------ - -- The objects in :mod:`qiskit.providers.models` which were previously - constructed using the marshmallow library have been refactored to not - depend on marshmallow. This includes: - - * :class:`~qiskit.providers.models.BackendConfiguration` - * :class:`~qiskit.providers.models.BackendProperties` - * :class:`~qiskit.providers.models.BackendStatus` - * :class:`~qiskit.providers.models.QasmBackendConfiguration` - * :class:`~qiskit.providers.models.PulseBackendConfiguration` - * :class:`~qiskit.providers.models.UchannelLO` - * :class:`~qiskit.providers.models.GateConfig` - * :class:`~qiskit.providers.models.PulseDefaults` - * :class:`~qiskit.providers.models.Command` - * :class:`~qiskit.providers.models.JobStatus` - * :class:`~qiskit.providers.models.Nduv` - * :class:`~qiskit.providers.models.Gate` - - These should be drop-in replacements without any noticeable change but - specifics inherited from marshmallow may not work. Please file issues for - any incompatibilities found. - -Aer 0.5.1 -========= - -No Change - - -Ignis 0.3.0 -=========== - -No Change - -Aqua 0.7.0 -========== - -Prelude -------- - -The Qiskit Aqua 0.7.0 release introduces a lot of new functionality along -with an improved integration with :class:`qiskit.circuit.QuantumCircuit` -objects. The central contributions are the Qiskit's optimization module, -a complete refactor on Operators, using circuits as native input for the -algorithms and removal of the declarative JSON API. - -Optimization module -^^^^^^^^^^^^^^^^^^^ -The :mod:`qiskit.optimization`` module now offers functionality for modeling -and solving quadratic programs. It provides various near-term quantum and -conventional algorithms, such as the ``MinimumEigenOptimizer`` -(covering e.g. ``VQE`` or ``QAOA``) or ``CplexOptimizer``, as well as -a set of converters to translate between different -problem representations, such as ``QuadraticProgramToQubo``. -See the -`changelog `_ -for a list of the added features. - -Operator flow -^^^^^^^^^^^^^ -The operator logic provided in :mod:`qiskit.aqua.operators`` was completely -refactored and is now a full set of tools for constructing -physically-intuitive quantum computations. It contains state functions, -operators and measurements and internally relies on Terra's Operator -objects. Computing expectation values and evolutions was heavily simplified -and objects like the ``ExpectationFactory`` produce the suitable, most -efficient expectation algorithm based on the Operator input type. -See the `changelog `_ -for a overview of the added functionality. - -Native circuits -^^^^^^^^^^^^^^^ -Algorithms commonly use parameterized circuits as input, for example the -VQE, VQC or QSVM. Previously, these inputs had to be of type -``VariationalForm`` or ``FeatureMap`` which were wrapping the circuit -object. Now circuits are natively supported in these algorithms, which -means any individually constructed ``QuantumCircuit`` can be passed to -these algorithms. In combination with the release of the circuit library -which offers a wide collection of circuit families, it is now easy to -construct elaborate circuits as algorithm input. - -Declarative JSON API -^^^^^^^^^^^^^^^^^^^^ -The ability of running algorithms using dictionaries as parameters as well -as using the Aqua interfaces GUI has been removed. - - -IBM Q Provider 0.7.0 -==================== - -.. _Release Notes_0.7.0_New Features: - -New Features ------------- - -- A new exception, :class:`qiskit.providers.ibmq.IBMQBackendJobLimitError`, - is now raised if a job could not be submitted because the limit on active - jobs has been reached. - -- :class:`qiskit.providers.ibmq.job.IBMQJob` and - :class:`qiskit.providers.ibmq.managed.ManagedJobSet` each has two new methods - ``update_name`` and ``update_tags``. - They are used to change the name and tags of a job or a job set, respectively. - -- :meth:`qiskit.providers.ibmq.IBMQFactory.save_account` and - :meth:`qiskit.providers.ibmq.IBMQFactory.enable_account` now accept optional - parameters ``hub``, ``group``, and ``project``, which allow specifying a default - provider to save to disk or use, respectively. - - -.. _Release Notes_0.7.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The :class:`qiskit.providers.ibmq.job.IBMQJob` methods ``creation_date`` and - ``time_per_step`` now return date time information as a ``datetime`` object in - local time instead of UTC. Similarly, the parameters ``start_datetime`` and - ``end_datetime``, of - :meth:`qiskit.providers.ibmq.IBMQBackendService.jobs` and - :meth:`qiskit.providers.ibmq.IBMQBackend.jobs` can now be specified in local time. - -- The :meth:`qiskit.providers.ibmq.job.QueueInfo.format` method now uses a custom - ``datetime`` to string formatter, and the package - `arrow `_ is no longer required and has been - removed from the requirements list. - - -.. _Release Notes_0.7.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The :meth:`~qiskit.providers.ibmq.job.IBMQJob.from_dict` and - :meth:`~qiskit.providers.ibmq.job.IBMQJob.to_dict` methods of - :class:`qiskit.providers.ibmq.job.IBMQJob` are deprecated and will be removed in - the next release. - - -.. _Release Notes_0.7.0_Bug Fixes: - -Bug Fixes ---------- - -- Fixed an issue where ``nest_asyncio.apply()`` may raise an exception if there is - no asyncio loop due to threading. - - -############# -Qiskit 0.18.3 -############# - -Terra 0.13.0 -============ - -No Change - -Aer 0.5.1 -========== - -.. _Release Notes_0.5.1_Upgrade Notes: - -Upgrade Notes -------------- - -- Changes how transpilation passes are handled in the C++ Controller classes - so that each pass must be explicitly called. This allows for greater - customization on when each pass should be called, and with what parameters. - In particular this enables setting different parameters for the gate - fusion optimization pass depending on the QasmController simulation method. - -- Add ``gate_length_units`` kwarg to - :meth:`qiskit.providers.aer.noise.NoiseModel.from_device` - for specifying custom ``gate_lengths`` in the device noise model function - to handle unit conversions for internal code. - -- Add Controlled-Y ("cy") gate to the Stabilizer simulator methods supported - gateset. - -- For Aer's backend the jsonschema validation of input qobj objects from - terra is now opt-in instead of being enabled by default. If you want - to enable jsonschema validation of qobj set the ``validate`` kwarg on - the :meth:`qiskit.providers.aer.QasmSimualtor.run` method for the backend - object to ``True``. - - -.. _Release Notes_0.5.1_Bug Fixes: - -Bug Fixes ---------- - -- Remove "extended_stabilizer" from the automatically selected simulation - methods. This is needed as the extended stabilizer method is not exact - and may give incorrect results for certain circuits unless the user - knows how to optimize its configuration parameters. - - The automatic method now only selects from "stabilizer", "density_matrix", - and "statevector" methods. If a non-Clifford circuit that is too large for - the statevector method is executed an exception will be raised suggesting - you could try explicitly using the "extended_stabilizer" or - "matrix_product_state" methods instead. - -- Fixes Controller classes so that the ReduceBarrier transpilation pass is - applied first. This prevents barrier instructions from preventing truncation - of unused qubits if the only instruction defined on them was a barrier. - -- Disables gate fusion for the matrix product state simulation method as this - was causing issues with incorrect results being returned in some cases. - -- Fix error in gate time unit conversion for device noise model with thermal - relaxation errors and gate errors. The error probability the depolarizing - error was being calculated with gate time in microseconds, while for - thermal relaxation it was being calculated in nanoseconds. This resulted - in no depolarizing error being applied as the incorrect units would make - the device seem to be coherence limited. - -- Fix bug in incorrect composition of QuantumErrors when the qubits of - composed instructions differ. - -- Fix issue where the "diagonal" gate is checked to be unitary with too - high a tolerance. This was causing diagonals generated from Numpy functions - to often fail the test. - -- Fix remove-barrier circuit optimization pass to be applied before qubit - trucation. This fixes an issue where barriers inserted by the Terra - transpiler across otherwise inactive qubits would prevent them from being - truncated. - -Ignis 0.3.0 -=========== - -No Change - - -Aqua 0.6.6 -========== - -No Change - - -IBM Q Provider 0.6.1 -==================== - -No Change - - -############# -Qiskit 0.18.0 -############# - -.. _Release Notes_0.13.0: - -Terra 0.13.0 -============ - -.. _Release Notes_0.13.0_Prelude: - -Prelude -------- - -The 0.13.0 release includes many big changes. Some highlights for this -release are: - -For the transpiler we have switched the graph library used to build the -:class:`qiskit.dagcircuit.DAGCircuit` class which is the underlying data -structure behind all operations to be based on -`retworkx `_ for greatly improved -performance. Circuit transpilation speed in the 0.13.0 release should -be significanlty faster than in previous releases. - -There has been a significant simplification to the style in which Pulse -instructions are built. Now, ``Command`` s are deprecated and a unified -set of :class:`~qiskit.pulse.instructions.Instruction` s are supported. - -The :mod:`qiskit.quantum_info` module includes several new functions -for generating random operators (such as Cliffords and quantum channels) -and for computing the diamond norm of quantum channels; upgrades to the -:class:`~qiskit.quantum_info.Statevector` and -:class:`~qiskit.quantum_info.DensityMatrix` classes to support -computing measurement probabilities and sampling measurements; and several -new classes are based on the symplectic representation -of Pauli matrices. These new classes include Clifford operators -(:class:`~qiskit.quantum_info.Clifford`), N-qubit matrices that are -sparse in the Pauli basis (:class:`~qiskit.quantum_info.SparsePauliOp`), -lists of Pauli's (:class:`~qiskit.quantum_info.PauliTable`), -and lists of stabilizers (:class:`~qiskit.quantum_info.StabilizerTable`). - -This release also has vastly improved documentation across Qiskit, -including improved documentation for the :mod:`qiskit.circuit`, -:mod:`qiskit.pulse` and :mod:`qiskit.quantum_info` modules. - -Additionally, the naming of gate objects and -:class:`~qiskit.circuit.QuantumCircuit` methods have been updated to be -more consistent. This has resulted in several classes and methods being -deprecated as things move to a more consistent naming scheme. - -For full details on all the changes made in this release see the detailed -release notes below. - - -.. _Release Notes_0.13.0_New Features: - -New Features ------------- - -- Added a new circuit library module :mod:`qiskit.circuit.library`. This will - be a place for constructors of commonly used circuits that can be used as - building blocks for larger circuits or applications. - -- The :class:`qiskit.providers.BaseJob` class has four new methods: - - * :meth:`~qiskit.providers.BaseJob.done` - * :meth:`~qiskit.providers.BaseJob.running` - * :meth:`~qiskit.providers.BaseJob.cancelled` - * :meth:`~qiskit.providers.BaseJob.in_final_state` - - These methods are used to check wheter a job is in a given job status. - -- Add ability to specify control conditioned on a qubit being in the - ground state. The state of the control qubits is represented by an - integer. For example:: - - from qiskit import QuantumCircuit - from qiskit.extensions.standard import XGate - - qc = QuantumCircuit(4) - cgate = XGate().control(3, ctrl_state=6) - qc.append(cgate, [0, 1, 2, 3]) - - Creates a four qubit gate where the fourth qubit gets flipped if - the first qubit is in the ground state and the second and third - qubits are in the excited state. If ``ctrl_state`` is ``None``, the - default, control is conditioned on all control qubits being - excited. - -- A new jupyter widget, ``%circuit_library_info`` has been added to - :mod:`qiskit.tools.jupyter`. This widget is used for visualizing - details about circuits built from the circuit library. For example - - .. code-block:: python - - from qiskit.circuit.library import XOR - import qiskit.tools.jupyter - circuit = XOR(5, seed=42) - %circuit_library_info circuit - -- A new kwarg option, ``formatted`` , has been added to - :meth:`qiskit.circuit.QuantumCircuit.qasm` . When set to ``True`` the - method will print a syntax highlighted version (using pygments) to - stdout and return ``None`` (which differs from the normal behavior of - returning the QASM code as a string). - -- A new kwarg option, ``filename`` , has been added to - :meth:`qiskit.circuit.QuantumCircuit.qasm`. When set to a path the method - will write the QASM code to that file. It will then continue to output as - normal. - -- A new instruction :py:class:`~qiskit.pulse.SetFrequency` which allows users - to change the frequency of the :class:`~qiskit.pulse.PulseChannel`. This is - done in the following way:: - - from qiskit.pulse import Schedule - from qiskit.pulse import SetFrequency - - sched = pulse.Schedule() - sched += SetFrequency(5.5e9, DriveChannel(0)) - - In this example, the frequency of all pulses before the ``SetFrequency`` - command will be the default frequency and all pulses applied to drive - channel zero after the ``SetFrequency`` command will be at 5.5 GHz. Users - of ``SetFrequency`` should keep in mind any hardware limitations. - -- A new method, :meth:`~qiskit.circuit.QuantumCircuit.assign_parameters` - has been added to the :class:`qiskit.circuit.QuantumCircuit` class. This - method accepts a parameter dictionary with both floats and Parameters - objects in a single dictionary. In other words this new method allows you - to bind floats, Parameters or both in a single dictionary. - - Also, by using the ``inplace`` kwarg it can be specified you can optionally - modify the original circuit in place. By default this is set to ``False`` - and a copy of the original circuit will be returned from the method. - -- A new method :meth:`~qiskit.circuit.QuantumCircuit.num_nonlocal_gates` - has been added to the :class:`qiskit.circuit.QuantumCircuit` class. - This method will return the number of gates in a circuit that involve 2 or - or more qubits. These gates are more costly in terms of time and error to - implement. - -- The :class:`qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.iso` for adding an - :class:`~qiskit.extensions.Isometry` gate to the circuit has a new alias. You - can now call :meth:`qiskit.circuit.QuantumCircuit.isometry` in addition to - calling ``iso``. - -- A ``description`` attribute has been added to the - :class:`~qiskit.transpiler.CouplingMap` class for storing a short - description for different coupling maps (e.g. full, grid, line, etc.). - -- A new method :meth:`~qiskit.dagcircuit.DAGCircuit.compose` has been added to - the :class:`~qiskit.dagcircuit.DAGCircuit` class for composing two circuits - via their DAGs. - - .. code-block:: python - - dag_left.compose(dag_right, edge_map={right_qubit0: self.left_qubit1, - right_qubit1: self.left_qubit4, - right_clbit0: self.left_clbit1, - right_clbit1: self.left_clbit0}) - - .. parsed-literal:: - - ┌───┐ ┌─────┐┌─┐ - lqr_1_0: ───┤ H ├─── rqr_0: ──■──┤ Tdg ├┤M├ - ├───┤ ┌─┴─┐└─┬─┬─┘└╥┘ - lqr_1_1: ───┤ X ├─── rqr_1: ┤ X ├──┤M├───╫─ - ┌──┴───┴──┐ └───┘ └╥┘ ║ - lqr_1_2: ┤ U1(0.1) ├ + rcr_0: ════════╬════╩═ = - └─────────┘ ║ - lqr_2_0: ─────■───── rcr_1: ════════╩══════ - ┌─┴─┐ - lqr_2_1: ───┤ X ├─── - └───┘ - lcr_0: ═══════════ - - lcr_1: ═══════════ - - ┌───┐ - lqr_1_0: ───┤ H ├────────────────── - ├───┤ ┌─────┐┌─┐ - lqr_1_1: ───┤ X ├─────■──┤ Tdg ├┤M├ - ┌──┴───┴──┐ │ └─────┘└╥┘ - lqr_1_2: ┤ U1(0.1) ├──┼──────────╫─ - └─────────┘ │ ║ - lqr_2_0: ─────■───────┼──────────╫─ - ┌─┴─┐ ┌─┴─┐ ┌─┐ ║ - lqr_2_1: ───┤ X ├───┤ X ├──┤M├───╫─ - └───┘ └───┘ └╥┘ ║ - lcr_0: ═══════════════════╩════╬═ - ║ - lcr_1: ════════════════════════╩═ - -- The mock backends in ``qiskit.test.mock`` now have a functional ``run()`` - method that will return results similar to the real devices. If - ``qiskit-aer`` is installed a simulation will be run with a noise model - built from the device snapshot in the fake backend. Otherwise, - :class:`qiskit.providers.basicaer.QasmSimulatorPy` will be used to run an - ideal simulation. Additionally, if a pulse experiment is passed to ``run`` - and qiskit-aer is installed the ``PulseSimulator`` will be used to simulate - the pulse schedules. - -- The :meth:`qiskit.result.Result` method - :meth:`~qiskit.result.Result.get_counts` will now return a list of all the - counts available when there are multiple circuits in a job. This works when - ``get_counts()`` is called with no arguments. - - The main consideration for this feature was for drawing all the results - from multiple circuits in the same histogram. For example it is now - possible to do something like: - - .. code-block:: python - - from qiskit import execute - from qiskit import QuantumCircuit - from qiskit.providers.basicaer import BasicAer - from qiskit.visualization import plot_histogram - - sim = BasicAer.get_backend('qasm_simulator') - - qc = QuantumCircuit(2) - qc.h(0) - qc.cx(0, 1) - qc.measure_all() - result = execute([qc, qc, qc], sim).result() - - plot_histogram(result.get_counts()) - -- A new kwarg, ``initial_state`` has been added to the - :func:`qiskit.visualization.circuit_drawer` function and the - :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.draw`. When set to ``True`` the - initial state will be included in circuit visualizations for all backends. - For example: - - .. code-block:: python - - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(2) - circuit.measure_all() - circuit.draw(output='mpl', initial_state=True) - -- It is now possible to insert a callable into a :class:`qiskit.pulse.InstructionScheduleMap` - which returns a new :class:`qiskit.pulse.Schedule` when it is called with parameters. - For example: - - .. code-block:: - - def test_func(x): - sched = Schedule() - sched += pulse_lib.constant(int(x), amp_test)(DriveChannel(0)) - return sched - - inst_map = InstructionScheduleMap() - inst_map.add('f', (0,), test_func) - output_sched = inst_map.get('f', (0,), 10) - assert output_sched.duration == 10 - -- Two new gate classes, :class:`qiskit.extensions.iSwapGate` and - :class:`qiskit.extensions.DCXGate`, along with their - :class:`~qiskit.circuit.QuantumCircuit` methods - :meth:`~qiskit.circuit.QuantumCircuit.iswap` and - :meth:`~qiskit.circuit.QuantumCircuit.dcx` have been added to the standard - extensions. These gates, which are locally equivalent to each other, can be - used to enact particular XY interactions. A brief motivation for these gates - can be found in: - `arxiv.org/abs/quant-ph/0209035 `_ - -- The :class:`qiskit.providers.BaseJob` class now has a new method - :meth:`~qiskit.providers.BaseJob.wait_for_final_state` that polls for the - job status until the job reaches a final state (such as ``DONE`` or - ``ERROR``). This method also takes an optional ``callback`` kwarg which - takes a Python callable that will be called during each iteration of the - poll loop. - -- The ``search_width`` and ``search_depth`` attributes of the - :class:`qiskit.transpiler.passes.LookaheadSwap` pass are now settable when - initializing the pass. A larger search space can often lead to more - optimized circuits, at the cost of longer run time. - -- The number of qubits in - :class:`~qiskit.providers.models.BackendConfiguration` can now be accessed - via the property - :py:attr:`~qiskit.providers.models.BackendConfiguration.num_qubits`. It - was previously only accessible via the ``n_qubits`` attribute. - -- Two new methods, :meth:`~qiskit.quantum_info.OneQubitEulerDecomposer.angles` - and :meth:`~qiskit.quantum_info.OneQubitEulerDecomposer.angles_and_phase`, - have been added to the :class:`qiskit.quantum_info.OneQubitEulerDecomposer` - class. These methods will return the relevant parameters without - validation, and calling the ``OneQubitEulerDecomposer`` object will - perform the full synthesis with validation. - -- An ``RR`` decomposition basis has been added to the - :class:`qiskit.quantum_info.OneQubitEulerDecomposer` for decomposing an - arbitrary 2x2 unitary into a two :class:`~qiskit.extensions.RGate` - circuit. - -- Adds the ability to set ``qargs`` to objects which are subclasses - of the abstract ``BaseOperator`` class. This is done by calling the - object ``op(qargs)`` (where ``op`` is an operator class) and will return - a shallow copy of the original object with a qargs property set. When - such an object is used with the - :meth:`~qiskit.quantum_info.Operator.compose` or - :meth:`~qiskit.quantum_info.Operator.dot` methods the internal value for - qargs will be used when the ``qargs`` method kwarg is not used. This - allows for subsystem composition using binary operators, for example:: - - from qiskit.quantum_info import Operator - - init = Operator.from_label('III') - x = Operator.from_label('X') - h = Operator.from_label('H') - init @ x([0]) @ h([1]) - -- Adds :class:`qiskit.quantum_info.Clifford` operator class to the - `quantum_info` module. This operator is an efficient symplectic - representation an N-qubit unitary operator from the Clifford group. This - class includes a :meth:`~qiskit.quantum_info.Clifford.to_circuit` method - for compilation into a :class:`~qiskit.QuantumCircuit` of Clifford gates - with a minimal number of CX gates for up to 3-qubits. It also providers - general compilation for N > 3 qubits but this method is not optimal in - the number of two-qubit gates. - -- Adds :class:`qiskit.quantum_info.SparsePauliOp` operator class. This is an - efficient representaiton of an N-qubit matrix that is sparse in the Pauli - basis and uses a :class:`qiskit.quantum_info.PauliTable` and vector of - complex coefficients for its data structure. - - This class supports much of the same functionality of the - :class:`qiskit.quantum_info.Operator` class so - :class:`~qiskit.quantum_info.SparsePauliOp` objects can be tensored, - composed, scalar multiplied, added and subtracted. - - Numpy arrays or :class:`~qiskit.quantum_info.Operator` objects can be - converted to a :class:`~qiskit.quantum_info.SparsePauliOp` using the - `:class:`~qiskit.quantum_info.SparsePauliOp.from_operator` method. - :class:`~qiskit.quantum_info.SparsePauliOp` can be convered to a sparse - csr_matrix or dense Numpy array using the - :class:`~qiskit.quantum_info.SparsePauliOp.to_matrix` method, or to an - :class:`~qiskit.quantum_info.Operator` object using the - :class:`~qiskit.quantum_info.SparsePauliOp.to_operator` method. - - A :class:`~qiskit.quantum_info.SparsePauliOp` can be iterated over - in terms of its :class:`~qiskit.quantum_info.PauliTable` components and - coefficients, its coefficients and Pauli string labels using the - :meth:`~qiskit.quantum_info.SparsePauliOp.label_iter` method, and the - (dense or sparse) matrix components using the - :meth:`~qiskit.quantum_info.SparsePauliOp.matrix_iter` method. - -- Add :meth:`qiskit.quantum_info.diamond_norm` function for computing the - diamond norm (completely-bounded trace-norm) of a quantum channel. This - can be used to compute the distance between two quantum channels using - ``diamond_norm(chan1 - chan2)``. - -- A new class :class:`qiskit.quantum_info.PauliTable` has been added. This - is an efficient symplectic representation of a list of N-qubit Pauli - operators. Some features of this class are: - - * :class:`~qiskit.quantum_info.PauliTable` objects may be composed, and - tensored which will return a :class:`~qiskit.quantum_info.PauliTable` - object with the combination of the operation ( - :meth:`~qiskit.quantum_info.PauliTable.compose`, - :meth:`~qiskit.quantum_info.PauliTable.dot`, - :meth:`~qiskit.quantum_info.PauliTable.expand`, - :meth:`~qiskit.quantum_info.PauliTable.tensor`) between each element - of the first table, with each element of the second table. - - * Addition of two tables acts as list concatination of the terms in each - table (``+``). - - * Pauli tables can be sorted by lexicographic (tensor product) order or - by Pauli weights (:meth:`~qiskit.quantum_info.PauliTable.sort`). - - * Duplicate elements can be counted and deleted - (:meth:`~qiskit.quantum_info.PauliTable.unique`). - - * The PauliTable may be iterated over in either its native symplectic - boolean array representation, as Pauli string labels - (:meth:`~qiskit.quantum_info.PauliTable.label_iter`), or as dense - Numpy array or sparse CSR matrices - (:meth:`~qiskit.quantum_info.PauliTable.matrix_iter`). - - * Checking commutation between elements of the Pauli table and another - Pauli (:meth:`~qiskit.quantum_info.PauliTable.commutes`) or Pauli - table (:meth:`~qiskit.quantum_info.PauliTable.commutes_with_all`) - - See the :class:`qiskit.quantum_info.PauliTable` class API documentation for - additional details. - -- Adds :class:`qiskit.quantum_info.StabilizerTable` class. This is a subclass - of the :class:`qiskit.quantum_info.PauliTable` class which includes a - boolean phase vector along with the Pauli table array. This represents a - list of Stabilizer operators which are real-Pauli operators with +1 or -1 - coefficient. Because the stabilizer matrices are real the ``"Y"`` label - matrix is defined as ``[[0, 1], [-1, 0]]``. See the API documentation for - additional information. - -- Adds :func:`qiskit.quantum_info.pauli_basis` function which returns an N-qubit - Pauli basis as a :class:`qiskit.quantum_info.PauliTable` object. The ordering - of this basis can either be by standard lexicographic (tensor product) order, - or by the number of non-identity Pauli terms (weight). - -- Adds :class:`qiskit.quantum_info.ScalarOp` operator class that represents - a scalar multiple of an identity operator. This can be used to initialize - an identity on arbitrary dimension subsystems and it will be implicitly - converted to other ``BaseOperator`` subclasses (such as an - :class:`qiskit.quantum_info.Operator` or - :class:`qiskit.quantum_info.SuperOp`) when it is composed with, - or added to, them. - - Example: Identity operator - - .. code-block:: - - from qiskit.quantum_info import ScalarOp, Operator - - X = Operator.from_label('X') - Z = Operator.from_label('Z') - - init = ScalarOp(2 ** 3) # 3-qubit identity - op = init @ X([0]) @ Z([1]) @ X([2]) # Op XZX - -- A new method, :meth:`~qiskit.quantum_info.Operator.reshape`, has been added - to the :class:`qiskit.quantum_innfo.Operator` class that returns a shallow - copy of an operator subclass with reshaped subsystem input or output dimensions. - The combined dimensions of all subsystems must be the same as the original - operator or an exception will be raised. - -- Adds :func:`qiskit.quantum_info.random_clifford` for generating a random - :class:`qiskit.quantum_info.Clifford` operator. - -- Add :func:`qiskit.quantum_info.random_quantum_channel` function - for generating a random quantum channel with fixed - :class:`~qiskit.quantum_info.Choi`-rank in the - :class:`~qiskit.quantum_info.Stinespring` representation. - -- Add :func:`qiskit.quantum_info.random_hermitian` for generating - a random Hermitian :class:`~qiskit.quantum_info.Operator`. - -- Add :func:`qiskit.quantum_info.random_statevector` for generating - a random :class:`~qiskit.quantum_info.Statevector`. - -- Adds :func:`qiskit.quantum_info.random_pauli_table` for generating a random - :class:`qiskit.quantum_info.PauliTable`. - -- Adds :func:`qiskit.quantum_info.random_stabilizer_table` for generating a random - :class:`qiskit.quantum_info.StabilizerTable`. - -- Add a ``num_qubits`` attribute to :class:`qiskit.quantum_info.StateVector` and - :class:`qiskit.quantum_info.DensityMatrix` classes. This returns the number of - qubits for N-qubit states and returns ``None`` for non-qubit states. - -- Adds :meth:`~qiskit.quantum_info.Statevector.to_dict` and - :meth:`~qiskit.quantum_info.DensityMatrix.to_dict` methods to convert - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` objects into Bra-Ket notation - dictionary. - - Example - - .. code-block:: python - - from qiskit.quantum_info import Statevector - - state = Statevector.from_label('+0') - print(state.to_dict()) - - .. code-block:: python - - from qiskit.quantum_info import DensityMatrix - - state = DensityMatrix.from_label('+0') - print(state.to_dict()) - -- Adds :meth:`~qiskit.quantum_info.Statevector.probabilities` and - :meth:`~qiskit.quantum_info.DensityMatrix.probabilities` to - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes which return an - array of measurement outcome probabilities in the computational - basis for the specified subsystems. - - Example - - .. code-block:: python - - from qiskit.quantum_info import Statevector - - state = Statevector.from_label('+0') - print(state.probabilities()) - - .. code-block:: python - - from qiskit.quantum_info import DensityMatrix - - state = DensityMatrix.from_label('+0') - print(state.probabilities()) - -- Adds :meth:`~qiskit.quantum_info.Statevector.probabilities_dict` and - :meth:`~qiskit.quantum_info.DensityMatrix.probabilities_dict` to - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes which return a - count-style dictionary array of measurement outcome probabilities - in the computational basis for the specified subsystems. - - .. code-block:: python - - from qiskit.quantum_info import Statevector - - state = Statevector.from_label('+0') - print(state.probabilities_dict()) - - .. code-block:: python - - from qiskit.quantum_info import DensityMatrix - - state = DensityMatrix.from_label('+0') - print(state.probabilities_dict()) - -- Add :meth:`~qiskit.quantum_info.Statevector.sample_counts` and - :meth:`~qiskit.quantum_info.Statevector.sample_memory` methods to the - :class:`~qiskit.quantum_info.Statevector` - and :class:`~qiskit.quantum_info.DensityMatrix` classes for sampling - measurement outcomes on subsystems. - - Example: - - Generate a counts dictionary by sampling from a statevector - - .. code-block:: python - - from qiskit.quantum_info import Statevector - - psi = Statevector.from_label('+0') - shots = 1024 - - # Sample counts dictionary - counts = psi.sample_counts(shots) - print('Measure both:', counts) - - # Qubit-0 - counts0 = psi.sample_counts(shots, [0]) - print('Measure Qubit-0:', counts0) - - # Qubit-1 - counts1 = psi.sample_counts(shots, [1]) - print('Measure Qubit-1:', counts1) - - Return the array of measurement outcomes for each sample - - .. code-block:: python - - from qiskit.quantum_info import Statevector - - psi = Statevector.from_label('-1') - shots = 10 - - # Sample memory - mem = psi.sample_memory(shots) - print('Measure both:', mem) - - # Qubit-0 - mem0 = psi.sample_memory(shots, [0]) - print('Measure Qubit-0:', mem0) - - # Qubit-1 - mem1 = psi.sample_memory(shots, [1]) - print('Measure Qubit-1:', mem1) - -- Adds a :meth:`~qiskit.quantum_info.Statevector.measure` method to the - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` quantum state classes. This - allows sampling a single measurement outcome from the specified subsystems - and collapsing the statevector to the post-measurement computational basis - state. For example - - .. code-block:: python - - from qiskit.quantum_info import Statevector - - psi = Statevector.from_label('+1') - - # Measure both qubits - outcome, psi_meas = psi.measure() - print("measure([0, 1]) outcome:", outcome, "Post-measurement state:") - print(psi_meas) - - # Measure qubit-1 only - outcome, psi_meas = psi.measure([1]) - print("measure([1]) outcome:", outcome, "Post-measurement state:") - print(psi_meas) - -- Adds a :meth:`~qiskit.quantum_info.Statevector.reset` method to the - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` quantum state classes. This - allows reseting some or all subsystems to the :math:`|0\rangle` state. - For example - - .. code-block:: python - - from qiskit.quantum_info import Statevector - - psi = Statevector.from_label('+1') - - # Reset both qubits - psi_reset = psi.reset() - print("Post reset state: ") - print(psi_reset) - - # Reset qubit-1 only - psi_reset = psi.reset([1]) - print("Post reset([1]) state: ") - print(psi_reset) - -- A new visualization function - :func:`qiskit.visualization.visualize_transition` for visualizing - single qubit gate transitions has been added. It takes in a single qubit - circuit and returns an animation of qubit state transitions on a Bloch - sphere. To use this function you must have installed - the dependencies for and configured globally a matplotlib animtion - writer. You can refer to the `matplotlib documentation - `_ for - more details on this. However, in the default case simply ensuring - that `FFmpeg `_ is installed is sufficient to - use this function. - - It supports circuits with the following gates: - - * :class:`~qiskit.extensions.HGate` - * :class:`~qiskit.extensions.XGate` - * :class:`~qiskit.extensions.YGate` - * :class:`~qiskit.extensions.ZGate` - * :class:`~qiskit.extensions.RXGate` - * :class:`~qiskit.extensions.RYGate` - * :class:`~qiskit.extensions.RZGate` - * :class:`~qiskit.extensions.SGate` - * :class:`~qiskit.extensions.SdgGate` - * :class:`~qiskit.extensions.TGate` - * :class:`~qiskit.extensions.TdgGate` - * :class:`~qiskit.extensions.U1Gate` - - For example: - - .. code-block:: python - - from qiskit.visualization import visualize_transition - from qiskit import * - - qc = QuantumCircuit(1) - qc.h(0) - qc.ry(70,0) - qc.rx(90,0) - qc.rz(120,0) - - visualize_transition(qc, fpg=20, spg=1, trace=True) - -- :func:`~qiskit.execute.execute` has a new kwarg ``schedule_circuit``. By - setting ``schedule_circuit=True`` this enables scheduling of the circuit - into a :class:`~qiskit.pulse.Schedule`. This allows users building - :class:`qiskit.circuit.QuantumCircuit` objects to make use of custom - scheduler methods, such as the ``as_late_as_possible`` and - ``as_soon_as_possible`` methods. - For example:: - - job = execute(qc, backend, schedule_circuit=True, - scheduling_method="as_late_as_possible") - -- A new environment variable ``QISKIT_SUPPRESS_PACKAGING_WARNINGS`` can be - set to ``Y`` or ``y`` which will suppress the warnings about - ``qiskit-aer`` and ``qiskit-ibmq-provider`` not being installed at import - time. This is useful for users who are only running qiskit-terra (or just - not qiskit-aer and/or qiskit-ibmq-provider) and the warnings are not an - indication of a potential packaging problem. You can set the environment - variable to ``N`` or ``n`` to ensure that warnings are always enabled - even if the user config file is set to disable them. - -- A new user config file option, ``suppress_packaging_warnings`` has been - added. When set to ``true`` in your user config file like:: - - [default] - suppress_packaging_warnings = true - - it will suppress the warnings about ``qiskit-aer`` and - ``qiskit-ibmq-provider`` not being installed at import time. This is useful - for users who are only running qiskit-terra (or just not qiskit-aer and/or - qiskit-ibmq-provider) and the warnings are not an indication of a potential - packaging problem. If the user config file is set to disable the warnings - this can be overridden by setting the ``QISKIT_SUPPRESS_PACKAGING_WARNINGS`` - to ``N`` or ``n`` - -- :func:`qiskit.compiler.transpile()` has two new kwargs, ``layout_method`` - and ``routing_method``. These allow you to select a particular method for - placement and routing of circuits on constrained architectures. For, - example:: - - transpile(circ, backend, layout_method='dense', - routing_method='lookahead') - - will run :class:`~qiskit.transpiler.passes.DenseLayout` layout pass and - :class:`~qiskit.transpiler.passes.LookaheadSwap` routing pass. - -- There has been a significant simplification to the style in which Pulse - instructions are built. - - With the previous style, ``Command`` s were called with channels to make - an :py:class:`~qiskit.pulse.instructions.Instruction`. The usage of both - commands and instructions was a point of confusion. This was the previous - style:: - - sched += Delay(5)(DriveChannel(0)) - sched += ShiftPhase(np.pi)(DriveChannel(0)) - sched += SamplePulse([1.0, ...])(DriveChannel(0)) - sched += Acquire(100)(AcquireChannel(0), MemorySlot(0)) - - or, equivalently (though less used):: - - sched += DelayInstruction(Delay(5), DriveChannel(0)) - sched += ShiftPhaseInstruction(ShiftPhase(np.pi), DriveChannel(0)) - sched += PulseInstruction(SamplePulse([1.0, ...]), DriveChannel(0)) - sched += AcquireInstruction(Acquire(100), AcquireChannel(0), - MemorySlot(0)) - - Now, rather than build a command *and* an instruction, each command has - been migrated into an instruction:: - - sched += Delay(5, DriveChannel(0)) - sched += ShiftPhase(np.pi, DriveChannel(0)) - sched += Play(SamplePulse([1.0, ...]), DriveChannel(0)) - sched += SetFrequency(5.5, DriveChannel(0)) # New instruction! - sched += Acquire(100, AcquireChannel(0), MemorySlot(0)) - -- There is now a :py:class:`~qiskit.pulse.instructions.Play` instruction - which takes a description of a pulse envelope and a channel. There is a - new :py:class:`~qiskit.pulse.pulse_lib.Pulse` class in the - :mod:`~qiskit.pulse.pulse_lib` from which the pulse envelope description - should subclass. - - For example:: - - Play(SamplePulse([0.1]*10), DriveChannel(0)) - Play(ConstantPulse(duration=10, amp=0.1), DriveChannel(0)) - - -.. _Release Notes_0.13.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The :class:`qiskit.dagcircuit.DAGNode` method ``pop`` which was deprecated - in the 0.9.0 release has been removed. If you were using this method you - can leverage Python's ``del`` statement or ``delattr()`` function - to perform the same task. - -- A new optional visualization requirement, - `pygments `_ , has been added. It is used for - providing syntax highlighting of OpenQASM 2.0 code in Jupyter widgets and - optionally for the :meth:`qiskit.circuit.QuantumCircuit.qasm` method. It - must be installed (either with ``pip install pygments`` or - ``pip install qiskit-terra[visualization]``) prior to using the - ``%circuit_library_info`` widget in :mod:`qiskit.tools.jupyter` or - the ``formatted`` kwarg on the :meth:`~qiskit.circuit.QuantumCircuit.qasm` - method. - -- The pulse ``buffer`` option found in :class:`qiskit.pulse.Channel` and - :class:`qiskit.pulse.Schedule` was deprecated in Terra 0.11.0 and has now - been removed. To add a delay on a channel or in a schedule, specify it - explicitly in your Schedule with a Delay:: - - sched = Schedule() - sched += Delay(5)(DriveChannel(0)) - -- ``PulseChannelSpec``, which was deprecated in Terra 0.11.0, has now been - removed. Use BackendConfiguration instead:: - - config = backend.configuration() - drive_chan_0 = config.drives(0) - acq_chan_0 = config.acquires(0) - - or, simply reference the channel directly, such as ``DriveChannel(index)``. - -- An import path was deprecated in Terra 0.10.0 and has now been removed: for - ``PulseChannel``, ``DriveChannel``, ``MeasureChannel``, and - ``ControlChannel``, use ``from qiskit.pulse.channels import X`` in place of - ``from qiskit.pulse.channels.pulse_channels import X``. - -- The pass :class:`qiskit.transpiler.passes.CSPLayout` (which was introduced - in the 0.11.0 release) has been added to the preset pass manager for - optimization levels 2 and 3. For level 2, there is a call limit of 1,000 - and a timeout of 10 seconds. For level 3, the call limit is 10,000 and the - timeout is 1 minute. - - Now that the pass is included in the preset pass managers the - `python-constraint `_ package - is not longer an optional dependency and has been added to the requirements - list. - -- The ``TranspileConfig`` class which was previously used to set - run time configuration for a :class:`qiskit.transpiler.PassManager` has - been removed and replaced by a new class - :class:`qiskit.transpile.PassManagerConfig`. This new class has been - structured to include only the information needed to construct a - :class:`~qiskit.transpiler.PassManager`. The attributes of this class are: - - * ``initial_layout`` - * ``basis_gates`` - * ``coupling_map`` - * ``backend_properties`` - * ``seed_transpiler`` - -- The function ``transpile_circuit`` in - :mod:`qiskit.transpiler` has been removed. To transpile a circuit with a - custom :class:`~qiskit.transpiler.PassManager` now you should use the - :meth:`~qiskit.transpiler.PassManager.run` method of the - :class:~qiskit.transpiler.PassManager` object. - -- The :class:`~qiskit.circuit.QuantumCircuit` method - :meth:`~qiskit.circuit.QuantumCircuit.draw` and - :func:`qiskit.visualization.circuit_drawer` function will no longer include - the initial state included in visualizations by default. If you would like to - retain the initial state in the output visualization you need to set the - ``initial_state`` kwarg to ``True``. For example, running: - - .. code-block:: python - - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(2) - circuit.measure_all() - circuit.draw(output='text') - - This no longer includes the initial state. If you'd like to retain it you can run: - - .. code-block:: python - - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(2) - circuit.measure_all() - circuit.draw(output='text', initial_state=True) - - -- :func:`qiskit.compiler.transpile` (and :func:`qiskit.execute.execute`, - which uses ``transpile`` internally) will now raise an error when the - ``pass_manager`` kwarg is set and a value is set for other kwargs that - are already set in an instantiated :class:`~qiskit.transpiler.PassManager` - object. Previously, these conflicting kwargs would just be silently - ignored and the values in the ``PassManager`` instance would be used. For - example:: - - from qiskit.circuit import QuantumCircuit - from qiskit.transpiler.pass_manager_config import PassManagerConfig - from qiskit.transpiler import preset_passmanagers - from qiskit.compiler import transpile - - qc = QuantumCircuit(5) - - config = PassManagerConfig(basis_gates=['u3', 'cx']) - pm = preset_passmanagers.level_0_pass_manager(config) - transpile(qc, optimization_level=3, pass_manager=pm) - - will now raise an error while prior to this release the value in ``pm`` - would just silently be used and the value for the ``optimization_level`` - kwarg would be ignored. The ``transpile`` kwargs this applies to are: - - * ``optimization_level`` - * ``basis_gates`` - * ``coupling_map`` - * ``seed_transpiler`` - * ``backend_properties`` - * ``initial_layout`` - * ``layout_method`` - * ``routing_method`` - * ``backend`` - -- The :class:`~qiskit.quantum_info.Operator`, - :class:`~qiskit.quantum_info.Clifford`, - :class:`~qiskit.quantum_info.SparsePauliOp`, - :class:`~qiskit.quantum_info.PauliTable`, - :class:`~qiskit.quantum_info.StabilizerTable`, operator classes have an added - ``call`` method that allows them to assign a `qargs` to the operator for use - with the :meth:`~qiskit.quantum_info.Operator.compose`, - :meth:`~qiskit.quantum_info.Operator.dot`, - :meth:`~qiskit.quantum_info.Statevector.evolve`,``+``, and ``-`` operations. - -- The addition method of the :class:`qiskit.quantum_info.Operator`, class now accepts a - ``qarg`` kwarg to allow adding a smaller operator to a larger one assuming identities - on the other subsystems (same as for ``qargs`` on - :meth:`~qiskit.quantum_info.Operator.compose` and - :meth:`~qiskit.quantum_info.Operator.dot` methods). This allows - subsystem addition using the call method as with composition. This support is - added to all BaseOperator subclasses (:class:`~qiskit.quantum_info.ScalarOp`, - :class:`~qiskit.quantum_info.Operator`, - :class:`~qiskit.quantum_info.QuantumChannel`). - - For example: - - .. code-block:: - - from qiskit.quantum_info import Operator, ScalarOp - - ZZ = Operator.from_label('ZZ') - - # Initialize empty Hamiltonian - n_qubits = 10 - ham = ScalarOp(2 ** n_qubits, coeff=0) - - # Add 2-body nearest neighbour terms - for j in range(n_qubits - 1): - ham = ham + ZZ([j, j+1]) - -- The ``BaseOperator`` class has been updated so that addition, - subtraction and scalar multiplication are no longer abstract methods. This - means that they are no longer required to be implemented in subclasses if - they are not supported. The base class will raise a ``NotImplementedError`` - when the methods are not defined. - -- The :func:`qiskit.quantum_info.random_density_matrix` function will - now return a random :class:`~qiskit.quantum_info.DensityMatrix` object. In - previous releases it returned a numpy array. - -- The :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes no longer copy the - input array if it is already the correct dtype. - -- `fastjsonschema `_ is added as a - dependency. This is used for much faster validation of qobj dictionaries - against the JSON schema when the ``to_dict()`` method is called on qobj - objects with the ``validate`` keyword argument set to ``True``. - -- The qobj construction classes in :mod:`qiskit.qobj` will no longer validate - against the qobj jsonschema by default. These include the following classes: - - * :class:`qiskit.qobj.QasmQobjInstruction` - * :class:`qiskit.qobj.QobjExperimentHeader` - * :class:`qiskit.qobj.QasmQobjExperimentConfig` - * :class:`qiskit.qobj.QasmQobjExperiment` - * :class:`qiskit.qobj.QasmQobjConfig` - * :class:`qiskit.qobj.QobjHeader` - * :class:`qiskit.qobj.PulseQobjInstruction` - * :class:`qiskit.qobj.PulseQobjExperimentConfig` - * :class:`qiskit.qobj.PulseQobjExperiment` - * :class:`qiskit.qobj.PulseQobjConfig` - * :class:`qiskit.qobj.QobjMeasurementOption` - * :class:`qiskit.qobj.PulseLibraryItem` - * :class:`qiskit.qobj.QasmQobjInstruction` - * :class:`qiskit.qobj.QasmQobjExperimentConfig` - * :class:`qiskit.qobj.QasmQobjExperiment` - * :class:`qiskit.qobj.QasmQobjConfig` - * :class:`qiskit.qobj.QasmQobj` - * :class:`qiskit.qobj.PulseQobj` - - If you were relying on this validation or would like to validate them - against the qobj schema this can be done by setting the ``validate`` kwarg - to ``True`` on :meth:`~qiskit.qobj.QasmQobj.to_dict` method from either of - the top level Qobj classes :class:`~qiskit.qobj.QasmQobj` or - :class:`~qiskit.qobj.PulseQobj`. For example: - - .. code-block: - - from qiskit import qobj - - my_qasm = qobj.QasmQobj( - qobj_id='12345', - header=qobj.QobjHeader(), - config=qobj.QasmQobjConfig(shots=1024, memory_slots=2, - max_credits=10), - experiments=[ - qobj.QasmQobjExperiment(instructions=[ - qobj.QasmQobjInstruction(name='u1', qubits=[1], - params=[0.4]), - qobj.QasmQobjInstruction(name='u2', qubits=[1], - params=[0.4, 0.2]) - ]) - ] - ) - qasm_dict = my_qasm.to_dict(validate=True) - - which will validate the output dictionary against the Qobj jsonschema. - -- The output dictionary from :meth:`qiskit.qobj.QasmQobj.to_dict` and - :meth:`qiskit.qobj.PulseQobj.to_dict` is no longer in a format for direct - json serialization as expected by IBMQ's API. These Qobj objects are - the current format we use for passing experiments to providers/backends - and while having a dictionary format that could just be passed to the IBMQ - API directly was moderately useful for ``qiskit-ibmq-provider``, it made - things more difficult for other providers. Especially for providers that - wrap local simulators. Moving forward the definitions of what is passed - between providers and the IBMQ API request format will be further decoupled - (in a backwards compatible manner) which should ease the burden of writing - providers and backends. - - In practice, the only functional difference between the output of these - methods now and previous releases is that complex numbers are represented - with the ``complex`` type and numpy arrays are not silently converted to - list anymore. If you were previously calling ``json.dumps()`` directly on - the output of ``to_dict()`` after this release a custom json encoder will - be needed to handle these cases. For example:: - - import json - - from qiskit.circuit import ParameterExpression - from qiskit import qobj - - my_qasm = qobj.QasmQobj( - qobj_id='12345', - header=qobj.QobjHeader(), - config=qobj.QasmQobjConfig(shots=1024, memory_slots=2, - max_credits=10), - experiments=[ - qobj.QasmQobjExperiment(instructions=[ - qobj.QasmQobjInstruction(name='u1', qubits=[1], - params=[0.4]), - qobj.QasmQobjInstruction(name='u2', qubits=[1], - params=[0.4, 0.2]) - ]) - ] - ) - qasm_dict = my_qasm.to_dict() - - class QobjEncoder(json.JSONEncoder): - """A json encoder for pulse qobj""" - def default(self, obj): - # Convert numpy arrays: - if hasattr(obj, 'tolist'): - return obj.tolist() - # Use Qobj complex json format: - if isinstance(obj, complex): - return (obj.real, obj.imag) - if isinstance(obj, ParameterExpression): - return float(obj) - return json.JSONEncoder.default(self, obj) - - json_str = json.dumps(qasm_dict, cls=QobjEncoder) - - will generate a json string in the same exact manner that - ``json.dumps(my_qasm.to_dict())`` did in previous releases. - -- ``CmdDef`` has been deprecated since Terra 0.11.0 and has been removed. - Please continue to use :py:class:`~qiskit.pulse.InstructionScheduleMap` - instead. - -- The methods ``cmds`` and ``cmd_qubits`` in - :py:class:`~qiskit.pulse.InstructionScheduleMap` have been deprecated - since Terra 0.11.0 and have been removed. Please use ``instructions`` - and ``qubits_with_instruction`` instead. - -- PulseDefaults have reported ``qubit_freq_est`` and ``meas_freq_est`` in - Hz rather than GHz since Terra release 0.11.0. A warning which notified - of this change has been removed. - -- The previously deprecated (in the 0.11.0 release) support for passsing in - :class:`qiskit.circuit.Instruction` parameters of types ``sympy.Basic``, - ``sympy.Expr``, ``qiskit.qasm.node.node.Node`` (QASM AST node) and - ``sympy.Matrix`` has been removed. The supported types for instruction - parameters are: - - * ``int`` - * ``float`` - * ``complex`` - * ``str`` - * ``list`` - * ``np.ndarray`` - * :class:`qiskit.circuit.ParameterExpression` - -- The following properties of - :py:class:`~qiskit.providers.models.BackendConfiguration`: - - * ``dt`` - * ``dtm`` - * ``rep_time`` - - all have units of seconds. Prior to release 0.11.0, ``dt`` and ``dtm`` had - units of nanoseconds. Prior to release 0.12.0, ``rep_time`` had units of - microseconds. The warnings alerting users of these changes have now been - removed from ``BackendConfiguration``. - -- A new requirement has been added to the requirements list, - `retworkx `_. It is an Apache 2.0 - licensed graph library that has a similar API to networkx and is being used - to significantly speed up the :class:`qiskit.dagcircuit.DAGCircuit` - operations as part of the transpiler. There are binaries published on PyPI - for all the platforms supported by Qiskit Terra but if you're using a - platform where there aren't precompiled binaries published refer to the - `retworkx documentation - `_ - for instructions on pip installing from sdist. - - If you encounter any issues with the transpiler or DAGCircuit class as part - of the transition you can switch back to the previous networkx - implementation by setting the environment variable ``USE_RETWORKX`` to - ``N``. This option will be removed in the 0.14.0 release. - - -.. _Release Notes_0.13.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- Passing in the data to the constructor for - :class:`qiskit.dagcircuit.DAGNode` as a dictionary arg ``data_dict`` - is deprecated and will be removed in a future release. Instead you should - now pass the fields in as kwargs to the constructor. For example the - previous behavior of:: - - from qiskit.dagcircuit import DAGNode - - data_dict = { - 'type': 'in', - 'name': 'q_0', - } - node = DAGNode(data_dict) - - should now be:: - - from qiskit.dagcircuit import DAGNode - - node = DAGNode(type='in', name='q_0') - -- The naming of gate objects and methods have been updated to be more - consistent. The following changes have been made: - - * The Pauli gates all have one uppercase letter only (``I``, ``X``, ``Y``, - ``Z``) - * The parameterized Pauli gates (i.e. rotations) prepend the uppercase - letter ``R`` (``RX``, ``RY``, ``RZ``) - * A controlled version prepends the uppercase letter ``C`` (``CX``, - ``CRX``, ``CCX``) - * Gates are named according to their action, not their alternative names - (``CCX``, not ``Toffoli``) - - The old names have been deprecated and will be removed in a future release. - This is a list of the changes showing the old and new class, name attribute, - and methods. If a new column is blank then there is no change for that. - - .. list-table:: Gate Name Changes - :header-rows: 1 - - * - Old Class - - New Class - - Old Name Attribute - - New Name Attribute - - Old :class:`qiskit.circuit.QuantumCircuit` method - - New :class:`qiskit.circuit.QuantumCircuit` method - * - ``ToffoliGate`` - - :class:`~qiskit.extensions.CCXGate` - - ``ccx`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.ccx` and - :meth:`~qiskit.circuit.QuantumCircuit.toffoli` - - - * - ``CrxGate`` - - :class:`~qiskit.extensions.CRXGate` - - ``crx`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.crx` - - - * - ``CryGate`` - - :class:`~qiskit.extensions.CRYGate` - - ``cry`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.cry` - - - * - ``CrzGate`` - - :class:`~qiskit.extensions.CRZGate` - - ``crz`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.crz` - - - * - ``FredkinGate`` - - :class:`~qiskit.extensions.CSwapGate` - - ``cswap`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.cswap` and - :meth:`~qiskit.circuit.QuantumCircuit.fredkin` - - - * - ``Cu1Gate`` - - :class:`~qiskit.extensions.CU1Gate` - - ``cu1`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.cu1` - - - * - ``Cu3Gate`` - - :class:`~qiskit.extensions.CU3Gate` - - ``cu3`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.cu3` - - - * - ``CnotGate`` - - :class:`~qiskit.extensions.CXGate` - - ``cx`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.cx` and - :meth:`~qiskit.circuit.QuantumCircuit.cnot` - - - * - ``CyGate`` - - :class:`~qiskit.extensions.CYGate` - - ``cy`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.cy` - - - * - ``CzGate`` - - :class:`~qiskit.extensions.CZGate` - - ``cz`` - - - - :meth:`~qiskit.circuit.QuantumCircuit.cz` - - - * - ``DiagGate`` - - :class:`~qiskit.extensions.DiagonalGate` - - ``diag`` - - ``diagonal`` - - ``diag_gate`` - - :meth:`~qiskit.circuit.QuantumCircuit.diagonal` - * - ``IdGate`` - - :class:`~qiskit.extensions.IGate` - - ``id`` - - - - ``iden`` - - :meth:`~qiskit.circuit.QuantumCircuit.i` and - :meth:`~qiskit.circuit.QuantumCircuit.id` - * - :class:`~qiskit.extensions.Isometry` - - - - ``iso`` - - ``isometry`` - - :meth:`~qiskit.circuit.QuantumCircuit.iso` - - :meth:`~qiskit.circuit.QuantumCircuit.isometry` - and :meth:`~qiskit.circuit.QuantumCircuit.iso` - * - ``UCG`` - - :class:`~qiskit.extensions.UCGate` - - ``multiplexer`` - - - - ``ucg`` - - :meth:`~qiskit.circuit.QuantumCircuit.uc` - * - ``UCRot`` - - :class:`~qiskit.extensions.UCPauliRotGate` - - - - - - - - - * - ``UCX`` - - :class:`~qiskit.extensions.UCRXGate` - - ``ucrotX`` - - ``ucrx`` - - ``ucx`` - - :meth:`~qiskit.circuit.QuantumCircuit.ucrx` - * - ``UCY`` - - :class:`~qiskit.extensions.UCRYGate` - - ``ucroty`` - - ``ucry`` - - ``ucy`` - - :meth:`~qiskit.circuit.QuantumCircuit.ucry` - * - ``UCZ`` - - :class:`~qiskit.extensions.UCRZGate` - - ``ucrotz`` - - ``ucrz`` - - ``ucz`` - - :meth:`~qiskit.circuit.QuantumCircuit.ucrz` - -- The kwarg ``period`` for the function - :func:`~qiskit.pulse.pulse_lib.square`, - :func:`~qiskit.pulse.pulse_lib.sawtooth`, and - :func:`~qiskit.pulse.pulse_lib.triangle` in - :mod:`qiskit.pulse.pulse_lib` is now deprecated and will be removed in a - future release. Instead you should now use the ``freq`` kwarg to set - the frequency. - -- The ``DAGCircuit.compose_back()`` and ``DAGCircuit.extend_back()`` methods - are deprecated and will be removed in a future release. Instead you should - use the :meth:`qiskit.dagcircuit.DAGCircuit.compose` method, which is a more - general and more flexible method that provides the same functionality. - -- The ``callback`` kwarg of the :class:`qiskit.transpiler.PassManager` class's - constructor has been deprecated and will be removed in a future release. - Instead of setting it at the object level during creation it should now - be set as a kwarg parameter on the :meth:`qiskit.transpiler.PassManager.run` - method. - -- The ``n_qubits`` and ``numberofqubits`` keywords are deprecated throughout - Terra and replaced by ``num_qubits``. The old names will be removed in - a future release. The objects affected by this change are listed below: - - .. list-table:: New Methods - :header-rows: 1 - - * - Class - - Old Method - - New Method - * - :class:`~qiskit.circuit.QuantumCircuit` - - ``n_qubits`` - - :meth:`~qiskit.circuit.QuantumCircuit.num_qubits` - * - :class:`~qiskit.quantum_info.Pauli` - - ``numberofqubits`` - - :meth:`~qiskit.quantum_info.Pauli.num_qubits` - - .. list-table:: New arguments - :header-rows: 1 - - * - Function - - Old Argument - - New Argument - * - :func:`~qiskit.circuit.random.random_circuit` - - ``n_qubits`` - - ``num_qubits`` - * - :class:`~qiskit.extensions.MSGate` - - ``n_qubit`` - - ``num_qubits`` - -- The function ``qiskit.quantum_info.synthesis.euler_angles_1q`` is now - deprecated. It has been superseded by the - :class:`qiskit.quantum_info.OneQubitEulerDecomposer` class which provides - the same functionality through:: - - OneQubitEulerDecomposer().angles(mat) - -- The ``pass_manager`` kwarg for the :func:`qiskit.compiler.transpile` - has been deprecated and will be removed in a future release. Moving forward - the preferred way to transpile a circuit with a custom - :class:`~qiskit.transpiler.PassManager` object is to use the - :meth:`~qiskit.transpiler.PassManager.run` method of the ``PassManager`` - object. - -- The :func:`qiskit.quantum_info.random_state` function has been deprecated - and will be removed in a future release. Instead you should use the - :func:`qiskit.quantum_info.random_statevector` function. - -- The ``add``, ``subtract``, and ``multiply`` methods of the - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes are deprecated and will - be removed in a future release. Instead you shoulde use ``+``, ``-``, ``*`` - binary operators instead. - -- Deprecates :meth:`qiskit.quantum_info.Statevector.to_counts`, - :meth:`qiskit.quantum_info.DensityMatrix.to_counts`, and - :func:`qiskit.quantum_info.counts.state_to_counts`. These functions - are superseded by the class methods - :meth:`qiskit.quantum_info.Statevector.probabilities_dict` and - :meth:`qiskit.quantum_info.DensityMatrix.probabilities_dict`. - -- :py:class:`~qiskit.pulse.pulse_lib.SamplePulse` and - :py:class:`~qiskit.pulse.pulse_lib.ParametricPulse` s (e.g. ``Gaussian``) - now subclass from :py:class:`~qiskit.pulse.pulse_lib.Pulse` and have been - moved to the :mod:`qiskit.pulse.pulse_lib`. The previous path via - ``pulse.commands`` is deprecated and will be removed in a future release. - -- ``DelayInstruction`` has been deprecated and replaced by - :py:class:`~qiskit.pulse.instruction.Delay`. This new instruction has been - taken over the previous ``Command`` ``Delay``. The migration pattern is:: - - Delay()() -> Delay(, ) - DelayInstruction(Delay(), ) - -> Delay(, ) - - Until the deprecation period is over, the previous ``Delay`` syntax of - calling a command on a channel will also be supported:: - - Delay()() - - The new ``Delay`` instruction does not support a ``command`` attribute. - -- ``FrameChange`` and ``FrameChangeInstruction`` have been deprecated and - replaced by :py:class:`~qiskit.pulse.instructions.ShiftPhase`. The changes - are:: - - FrameChange()() -> ShiftPhase(, ) - FrameChangeInstruction(FrameChange(), ) - -> ShiftPhase(, ) - - Until the deprecation period is over, the previous FrameChange syntax of - calling a command on a channel will be supported:: - - ShiftPhase()() - -- The ``call`` method of :py:class:`~qiskit.pulse.pulse_lib.SamplePulse` and - :py:class:`~qiskit.pulse.pulse_lib.ParametricPulse` s have been deprecated. - The migration is as follows:: - - Pulse(<*args>)() -> Play(Pulse(*args), ) - -- ``AcquireInstruction`` has been deprecated and replaced by - :py:class:`~qiskit.pulse.instructions.Acquire`. The changes are:: - - Acquire()(<**channels>) -> Acquire(, <**channels>) - AcquireInstruction(Acquire(), <**channels>) - -> Acquire(, <**channels>) - - Until the deprecation period is over, the previous Acquire syntax of - calling the command on a channel will be supported:: - - Acquire()(<**channels>) - - -.. _Release Notes_0.13.0_Bug Fixes: - -Bug Fixes ---------- - -- The :class:`~qiskit.transpiler.passes.BarrierBeforeFinalMeasurements` - transpiler pass, included in the preset transpiler levels when targeting - a physical device, previously inserted a barrier across only measured - qubits. In some cases, this allowed the transpiler to insert a swap after a - measure operation, rendering the circuit invalid for current - devices. The pass has been updated so that the inserted barrier - will span all qubits on the device. Fixes - `#3937 `_ - -- When extending a :class:`~qiskit.circuit.QuantumCircuit` instance - (extendee) with another circuit (extension), the circuit is taken via - reference. If a circuit is extended with itself that leads to an infinite - loop as extendee and extension are the same. This bug has been resolved by - copying the extension if it is the same object as the extendee. - Fixes `#3811 `_ - -- Fixes a case in :meth:`qiskit.result.Result.get_counts`, where the results - for an expirement could not be referenced if the experiment was initialized - as a Schedule without a name. Fixes - `#2753 `_ - -- Previously, replacing :class:`~qiskit.circuit.Parameter` objects in a - circuit with new Parameter objects prior to decomposing a circuit would - result in the substituted values not correctly being substituted into the - decomposed gates. This has been resolved such that binding and - decomposition may occur in any order. - -- The matplotlib output backend for the - :func:`qiskit.visualization.circuit_drawer` function and - :meth:`qiskit.circuit.QuantumCircuit.draw` method drawer has been fixed - to render :class:`~qiskit.extensions.CU1Gate` gates correctly. - Fixes `#3684 `_ - -- A bug in :meth:`qiskit.circuit.QuantumCircuit.from_qasm_str` and - :meth:`qiskit.circuit.QuantumCircuit.from_qasm_file` when - loading QASM with custom gates defined has been fixed. Now, loading - this QASM:: - - OPENQASM 2.0; - include "qelib1.inc"; - gate rinv q {sdg q; h q; sdg q; h q; } - qreg q[1]; - rinv q[0]; - - is equivalent to the following circuit:: - - rinv_q = QuantumRegister(1, name='q') - rinv_gate = QuantumCircuit(rinv_q, name='rinv') - rinv_gate.sdg(rinv_q) - rinv_gate.h(rinv_q) - rinv_gate.sdg(rinv_q) - rinv_gate.h(rinv_q) - rinv = rinv_gate.to_instruction() - qr = QuantumRegister(1, name='q') - expected = QuantumCircuit(qr, name='circuit') - expected.append(rinv, [qr[0]]) - - Fixes `#1566 `_ - -- Allow quantum circuit Instructions to have list parameter values. This is - used in Aer for expectation value snapshot parameters for example - ``params = [[1.0, 'I'], [1.0, 'X']]]`` for :math:`\langle I + X\rangle`. - -- Previously, for circuits containing composite gates (those created via - :meth:`qiskit.circuit.QuantumCircuit.to_gate` or - :meth:`qiskit.circuit.QuantumCircuit.to_instruction` or their corresponding - converters), attempting to bind the circuit more than once would result in - only the first bind value being applied to all circuits when transpiled. - This has been resolved so that the values provided for subsequent binds are - correctly respected. - - -.. _Release Notes_0.13.0_Other Notes: - -Other Notes ------------ - -- The qasm and pulse qobj classes: - - * :class:`~qiskit.qobj.QasmQobjInstruction` - * :class:`~qiskit.qobj.QobjExperimentHeader` - * :class:`~qiskit.qobj.QasmQobjExperimentConfig` - * :class:`~qiskit.qobj.QasmQobjExperiment` - * :class:`~qiskit.qobj.QasmQobjConfig` - * :class:`~qiskit.qobj.QobjHeader` - * :class:`~qiskit.qobj.PulseQobjInstruction` - * :class:`~qiskit.qobj.PulseQobjExperimentConfig` - * :class:`~qiskit.qobj.PulseQobjExperiment` - * :class:`~qiskit.qobj.PulseQobjConfig` - * :class:`~qiskit.qobj.QobjMeasurementOption` - * :class:`~qiskit.qobj.PulseLibraryItem` - * :class:`~qiskit.qobj.QasmQobjInstruction` - * :class:`~qiskit.qobj.QasmQobjExperimentConfig` - * :class:`~qiskit.qobj.QasmQobjExperiment` - * :class:`~qiskit.qobj.QasmQobjConfig` - * :class:`~qiskit.qobj.QasmQobj` - * :class:`~qiskit.qobj.PulseQobj` - - from :mod:`qiskit.qobj` have all been reimplemented without using the - marsmallow library. These new implementations are designed to be drop-in - replacement (except for as noted in the upgrade release notes) but - specifics inherited from marshmallow may not work. Please file issues for - any incompatibilities found. - -Aer 0.5.0 -========= - -Added ------ - - Add support for terra diagonal gate - - Add support for parameterized qobj - -Fixed ------ - - Added postfix for linux on Raspberry Pi - - Handle numpy array inputs from qobj - -Ignis 0.3.0 -=========== - -Added ------ - -* API documentation -* CNOT-Dihedral randomized benchmarking -* Accreditation module for output accrediation of noisy devices -* Pulse calibrations for single qubits -* Pulse Discriminator -* Entanglement verification circuits -* Gateset tomography for single-qubit gate sets -* Adds randomized benchmarking utility functions ``calculate_1q_epg``, - ``calculate_2q_epg`` functions to calculate 1 and 2-qubit error per gate from - error per Clifford -* Adds randomized benchmarking utility functions ``calculate_1q_epc``, - ``calculate_2q_epc`` for calculating 1 and 2-qubit error per Clifford from error - per gate - -Changed -------- -* Support integer labels for qubits in tomography -* Support integer labels for measurement error mitigation - -Deprecated ----------- -* Deprecates ``twoQ_clifford_error`` function. Use ``calculate_2q_epc`` instead. -* Python 3.5 support in qiskit-ignis is deprecated. Support will be removed on - the upstream python community's end of life date for the version, which is - 09/13/2020. - -Aqua 0.6.5 -========== - -No Change - -IBM Q Provider 0.6.0 -==================== - -No Change - -############# -Qiskit 0.17.0 -############# - -Terra 0.12.0 -============ - -No Change - -Aer 0.4.1 -========= - -No Change - -Ignis 0.2.0 -=========== - -No Change - -Aqua 0.6.5 -========== - -No Change - -IBM Q Provider 0.6.0 -==================== - -New Features ------------- - -- There are three new exceptions: ``VisualizationError``, ``VisualizationValueError``, - and ``VisualizationTypeError``. These are now used in the visualization modules when - an exception is raised. -- You can now set the logging level and specify a log file using the environment - variables ``QSIKIT_IBMQ_PROVIDER_LOG_LEVEL`` and ``QISKIT_IBMQ_PROVIDER_LOG_FILE``, - respectively. Note that the name of the logger is ``qiskit.providers.ibmq``. -- :class:`qiskit.providers.ibmq.job.IBMQJob` now has a new method - :meth:`~qiskit.providers.ibmq.job.IBMQJob.scheduling_mode` that returns the scheduling - mode the job is in. -- IQX-related tutorials that used to be in ``qiskit-iqx-tutorials`` are now in - ``qiskit-ibmq-provider``. - -Changed -------- - -- :meth:`qiskit.providers.ibmq.IBMQBackend.jobs` now accepts a new boolean parameter - ``descending``, which can be used to indicate whether the jobs should be returned in - descending or ascending order. -- :class:`qiskit.providers.ibmq.managed.IBMQJobManager` now looks at the job limit and waits - for old jobs to finish before submitting new ones if the limit has been reached. -- :meth:`qiskit.providers.ibmq.IBMQBackend.status` now raises a - :class:`qiskit.providers.ibmq.IBMQBackendApiProtocolError` exception - if there was an issue with validating the status. - -############# -Qiskit 0.16.0 -############# - -Terra 0.12.0 -============ - -No Change - -Aer 0.4.0 -========= - -No Change - -Ignis 0.2.0 -=========== - -No Change - -Aqua 0.6.4 -========== - -No Change - -IBM Q Provider 0.5.0 -==================== - -New Features ------------- - -- Some of the visualization and Jupyter tools, including gate/error map and - backend information, have been moved from ``qiskit-terra`` to ``qiskit-ibmq-provider``. - They are now under the :mod:`qiskit.providers.ibmq.jupyter` and - :mod:`qiskit.providers.ibmq.visualization`. In addition, you can now - use ``%iqx_dashboard`` to get a dashboard that provides both job and - backend information. - -Changed -------- - -- JSON schema validation is no longer run by default on Qobj objects passed - to :meth:`qiskit.providers.ibmq.IBMQBackend.run`. This significantly speeds - up the execution of the `run()` method. Qobj objects are still validated on the - server side, and invalid Qobjs will continue to raise exceptions. To force local - validation, set ``validate_qobj=True`` when you invoke ``run()``. - -############# -Qiskit 0.15.0 -############# - -Terra 0.12.0 -============ - -Prelude -------- - -The 0.12.0 release includes several new features and bug fixes. The biggest -change for this release is the addition of support for parametric pulses to -OpenPulse. These are Pulse commands which take parameters rather than sample -points to describe a pulse. 0.12.0 is also the first release to include -support for Python 3.8. It also marks the beginning of the deprecation for -Python 3.5 support, which will be removed when the upstream community stops -supporting it. - - -.. _Release Notes_0.12.0_New Features: - -New Features ------------- - -- The pass :class:`qiskit.transpiler.passes.CSPLayout` was extended with two - new parameters: ``call_limit`` and ``time_limit``. These options allow - limiting how long the pass will run. The option ``call_limit`` limits the - number of times that the recursive function in the backtracking solver may - be called. Similarly, ``time_limit`` limits how long (in seconds) the solver - will be allowed to run. The defaults are ``1000`` calls and ``10`` seconds - respectively. - -- :class:`qiskit.pulse.Acquire` can now be applied to a single qubit. - This makes pulse programming more consistent and easier to reason - about, as now all operations apply to a single channel. - For example:: - - acquire = Acquire(duration=10) - schedule = Schedule() - schedule.insert(60, acquire(AcquireChannel(0), MemorySlot(0), RegisterSlot(0))) - schedule.insert(60, acquire(AcquireChannel(1), MemorySlot(1), RegisterSlot(1))) - -- A new method :meth:`qiskit.transpiler.CouplingMap.draw` was added to - :class:`qiskit.transpiler.CouplingMap` to generate a graphviz image from - the coupling map graph. For example: - - .. code-block:: python - - from qiskit.transpiler import CouplingMap - - coupling_map = CouplingMap( - [[0, 1], [1, 0], [1, 2], [1, 3], [2, 1], [3, 1], [3, 4], [4, 3]]) - coupling_map.draw() - -- Parametric pulses have been added to OpenPulse. These are pulse commands - which are parameterized and understood by the backend. Arbitrary pulse - shapes are still supported by the SamplePulse Command. The new supported - pulse classes are: - - - :class:`qiskit.pulse.ConstantPulse` - - :class:`qiskit.pulse.Drag` - - :class:`qiskit.pulse.Gaussian` - - :class:`qiskit.pulse.GaussianSquare` - - They can be used like any other Pulse command. An example:: - - from qiskit.pulse import (Schedule, Gaussian, Drag, ConstantPulse, - GaussianSquare) - - sched = Schedule(name='parametric_demo') - sched += Gaussian(duration=25, sigma=4, amp=0.5j)(DriveChannel(0)) - sched += Drag(duration=25, amp=0.1, sigma=5, beta=4)(DriveChannel(1)) - sched += ConstantPulse(duration=25, amp=0.3+0.1j)(DriveChannel(1)) - sched += GaussianSquare(duration=1500, amp=0.2, sigma=8, - width=140)(MeasureChannel(0)) << sched.duration - - The resulting schedule will be similar to a SamplePulse schedule built - using :mod:`qiskit.pulse.pulse_lib`, however, waveform sampling will be - performed by the backend. The method :meth:`qiskit.pulse.Schedule.draw` - can still be used as usual. However, the command will be converted to a - ``SamplePulse`` with the - :meth:`qiskit.pulse.ParametricPulse.get_sample_pulse` method, so the - pulse shown may not sample the continuous function the same way that the - backend will. - - This feature can be used to construct Pulse programs for any backend, but - the pulses will be converted to ``SamplePulse`` objects if the backend does - not support parametric pulses. Backends which support them will have the - following new attribute:: - - backend.configuration().parametric_pulses: List[str] - # e.g. ['gaussian', 'drag', 'constant'] - - Note that the backend does not need to support all of the parametric - pulses defined in Qiskit. - - When the backend supports parametric pulses, and the Pulse schedule is - built with them, the assembled Qobj is significantly smaller. The size - of a PulseQobj built entirely with parametric pulses is dependent only - on the number of instructions, whereas the size of a PulseQobj built - otherwise will grow with the duration of the instructions (since every - sample must be specified with a value). - -- Added utility functions, :func:`qiskit.scheduler.measure` and - :func:`qiskit.scheduler.measure_all` to `qiskit.scheduler` module. These - functions return a :class:`qiskit.pulse.Schedule` object which measures - qubits using OpenPulse. For example:: - - from qiskit.scheduler import measure, measure_all - - measure_q0_schedule = measure(qubits=[0], backend=backend) - measure_all_schedule = measure_all(backend) - measure_custom_schedule = measure(qubits=[0], - inst_map=backend.defaults().instruction_schedule_map, - meas_map=[[0]], - qubit_mem_slots={0: 1}) - -- Pulse :class:`qiskit.pulse.Schedule` objects now have better - representations that for simple schedules should be valid Python - expressions. - -- The :class:`qiskit.circuit.QuantumCircuit` methods - :meth:`qiskit.circuit.QuantumCircuit.measure_active`, - :meth:`qiskit.circuit.QuantumCircuit.measure_all`, and - :meth:`qiskit.circuit.QuantumCircuit.remove_final_measurements` now have - an addition kwarg ``inplace``. When ``inplace`` is set to ``False`` the - function will return a modified **copy** of the circuit. This is different - from the default behavior which will modify the circuit object in-place and - return nothing. - -- Several new constructor methods were added to the - :class:`qiskit.transpiler.CouplingMap` class for building objects - with basic qubit coupling graphs. The new constructor methods are: - - - :meth:`qiskit.transpiler.CouplingMap.from_full` - - :meth:`qiskit.transpiler.CouplingMap.from_line` - - :meth:`qiskit.transpiler.CouplingMap.from_ring` - - :meth:`qiskit.transpiler.CouplingMap.from_grid` - - For example, to use the new constructors to get a coupling map of 5 - qubits connected in a linear chain you can now run: - - .. code-block:: python - - from qiskit.transpiler import CouplingMap - - coupling_map = CouplingMap.from_line(5) - coupling_map.draw() - -- Introduced a new pass - :class:`qiskit.transpiler.passes.CrosstalkAdaptiveSchedule`. This - pass aims to reduce the impact of crosstalk noise on a program. It - uses crosstalk characterization data from the backend to schedule gates. - When a pair of gates has high crosstalk, they get serialized using a - barrier. Naive serialization is harmful because it incurs decoherence - errors. Hence, this pass uses a SMT optimization approach to compute a - schedule which minimizes the impact of crosstalk as well as decoherence - errors. - - The pass takes as input a circuit which is already transpiled onto - the backend i.e., the circuit is expressed in terms of physical qubits and - swap gates have been inserted and decomposed into CNOTs if required. Using - this circuit and crosstalk characterization data, a - `Z3 optimization `_ is used to construct a - new scheduled circuit as output. - - To use the pass on a circuit circ:: - - dag = circuit_to_dag(circ) - pass_ = CrosstalkAdaptiveSchedule(backend_prop, crosstalk_prop) - scheduled_dag = pass_.run(dag) - scheduled_circ = dag_to_circuit(scheduled_dag) - - ``backend_prop`` is a :class:`qiskit.providers.models.BackendProperties` - object for the target backend. ``crosstalk_prop`` is a dict which specifies - conditional error rates. For two gates ``g1`` and ``g2``, - ``crosstalk_prop[g1][g2]`` specifies the conditional error rate of ``g1`` - when ``g1`` and ``g2`` are executed simultaneously. A method for generating - ``crosstalk_prop`` will be added in a future release of qiskit-ignis. Until - then you'll either have to already know the crosstalk properties of your - device, or manually write your own device characterization experiments. - -- In the preset pass manager for optimization level 1, - :func:`qiskit.transpiler.preset_passmanagers.level_1_pass_manager` if - :class:`qiskit.transpiler.passes.TrivialLayout` layout pass is not a - perfect match for a particular circuit, then - :class:`qiskit.transpiler.passes.DenseLayout` layout pass is used - instead. - -- Added a new abstract method - :meth:`qiskit.quantum_info.Operator.dot` to - the abstract ``BaseOperator`` class, so it is included for all - implementations of that abstract - class, including :class:`qiskit.quantum_info.Operator` and - ``QuantumChannel`` (e.g., :class:`qiskit.quantum_info.Choi`) - objects. This method returns the right operator multiplication - ``a.dot(b)`` :math:`= a \cdot b`. This is equivalent to - calling the operator - :meth:`qiskit.quantum_info.Operator.compose` method with the kwarg - ``front`` set to ``True``. - -- Added :func:`qiskit.quantum_info.average_gate_fidelity` and - :func:`qiskit.quantum_info.gate_error` functions to the - :mod:`qiskit.quantum_info` module for working with - :class:`qiskit.quantum_info.Operator` and ``QuantumChannel`` - (e.g., :class:`qiskit.quantum_info.Choi`) objects. - -- Added the :func:`qiskit.quantum_info.partial_trace` function to the - :mod:`qiskit.quantum_info` that works with - :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` quantum state classes. - For example:: - - from qiskit.quantum_info.states import Statevector - from qiskit.quantum_info.states import DensityMatrix - from qiskit.quantum_info.states import partial_trace - - psi = Statevector.from_label('10+') - partial_trace(psi, [0, 1]) - rho = DensityMatrix.from_label('10+') - partial_trace(rho, [0, 1]) - -- When :meth:`qiskit.circuit.QuantumCircuit.draw` or - :func:`qiskit.visualization.circuit_drawer` is called with the - ``with_layout`` kwarg set True (the default) the output visualization - will now display the physical qubits as integers to clearly - distinguish them from the virtual qubits. - - For Example: - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit import transpile - from qiskit.test.mock import FakeVigo - - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, 1) - qc.cx(0, 2) - transpiled_qc = transpile(qc, FakeVigo()) - transpiled_qc.draw(output='mpl') - -- Added new state measure functions to the :mod:`qiskit.quantum_info` - module: :func:`qiskit.quantum_info.entropy`, - :func:`qiskit.quantum_info.mutual_information`, - :func:`qiskit.quantum_info.concurrence`, and - :func:`qiskit.quantum_info.entanglement_of_formation`. These functions work - with the :class:`qiskit.quantum_info.Statevector` and - :class:`qiskit.quantum_info.DensityMatrix` classes. - -- The decomposition methods for single-qubit gates in - :class:`qiskit.quantum_info.synthesis.one_qubit_decompose.OneQubitEulerDecomposer` have - been expanded to now also include the ``'ZXZ'`` basis, characterized by three rotations - about the Z,X,Z axis. This now means that a general 2x2 Operator can be - decomposed into following bases: ``U3``, ``U1X``, ``ZYZ``, ``ZXZ``, - ``XYX``, ``ZXZ``. - - -.. _Release Notes_0.12.0_Known Issues: - -Known Issues ------------- - -- Running functions that use :func:`qiskit.tools.parallel_map` (for example - :func:`qiskit.execute.execute`, :func:`qiskit.compiler.transpile`, and - :meth:`qiskit.transpiler.PassManager.run`) may not work when - called from a script running outside of a ``if __name__ == '__main__':`` - block when using Python 3.8 on MacOS. Other environments are unaffected by - this issue. This is due to changes in how parallel processes are launched - by Python 3.8 on MacOS. If ``RuntimeError`` or ``AttributeError`` are - raised by scripts that are directly calling ``parallel_map()`` or when - calling a function that uses it internally with Python 3.8 on MacOS - embedding the script calls inside ``if __name__ == '__main__':`` should - workaround the issue. For example:: - - from qiskit import QuantumCircuit, QiskitError - from qiskit import execute, BasicAer - - qc1 = QuantumCircuit(2, 2) - qc1.h(0) - qc1.cx(0, 1) - qc1.measure([0,1], [0,1]) - # making another circuit: superpositions - qc2 = QuantumCircuit(2, 2) - qc2.h([0,1]) - qc2.measure([0,1], [0,1]) - execute([qc1, qc2], BasicAer.get_backend('qasm_simulator')) - - should be changed to:: - - from qiskit import QuantumCircuit, QiskitError - from qiskit import execute, BasicAer - - def main(): - qc1 = QuantumCircuit(2, 2) - qc1.h(0) - qc1.cx(0, 1) - qc1.measure([0,1], [0,1]) - # making another circuit: superpositions - qc2 = QuantumCircuit(2, 2) - qc2.h([0,1]) - qc2.measure([0,1], [0,1]) - execute([qc1, qc2], BasicAer.get_backend('qasm_simulator')) - - if __name__ == '__main__': - main() - - if errors are encountered with Python 3.8 on MacOS. - - -.. _Release Notes_0.12.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The value of the ``rep_time`` parameter for Pulse backend's configuration - object is now in units of seconds, not microseconds. The first time a - ``PulseBackendConfiguration`` object is initialized it will raise a single - warning to the user to indicate this. - -- The ``rep_time`` argument for :func:`qiskit.compiler.assemble` now takes - in a value in units of seconds, not microseconds. This was done to make - the units with everything else in pulse. If you were passing in a value for - ``rep_time`` ensure that you update the value to account for this change. - -- The value of the ``base_gate`` property of - :class:`qiskit.circuit.ControlledGate` objects has been changed from the - class of the base gate to an instance of the class of the base gate. - -- The ``base_gate_name`` property of :class:`qiskit.circuit.ControlledGate` - has been removed; you can get the name of the base gate by accessing - ``base_gate.name`` on the object. For example:: - - from qiskit import QuantumCircuit - from qiskit.extensions import HGate - - qc = QuantumCircuit(3) - cch_gate = HGate().control(2) - base_gate_name = cch_gate.base_gate.name - -- Changed :class:`qiskit.quantum_info.Operator` magic methods so that - ``__mul__`` (which gets executed by python's multiplication operation, - if the left hand side of the operation has it defined) implements right - matrix multiplication (i.e. :meth:`qiskit.quantum_info.Operator.dot`), and - ``__rmul__`` (which gets executed by python's multiplication operation - from the right hand side of the operation if the left does not have - ``__mul__`` defined) implements scalar multiplication (i.e. - :meth:`qiskit.quantum_info.Operator.multiply`). Previously both methods - implemented scalar multiplciation. - -- The second argument of the :func:`qiskit.quantum_info.process_fidelity` - function, ``target``, is now optional. If a target unitary is not - specified, then process fidelity of the input channel with the identity - operator will be returned. - -- :func:`qiskit.compiler.assemble` will now respect the configured - ``max_shots`` value for a backend. If a value for the ``shots`` kwarg is - specified that exceed the max shots set in the backend configuration the - function will now raise a ``QiskitError`` exception. Additionally, if no - shots argument is provided the default value is either 1024 (the previous - behavior) or ``max_shots`` from the backend, whichever is lower. - - -.. _Release Notes_0.12.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- Methods for adding gates to a :class:`qiskit.circuit.QuantumCircuit` with - abbreviated keyword arguments (e.g. ``ctl``, ``tgt``) have had their keyword - arguments renamed to be more descriptive (e.g. ``control_qubit``, - ``target_qubit``). The old names have been deprecated. A table including the - old and new calling signatures for the ``QuantumCircuit`` methods is included below. - - .. list-table:: New signatures for ``QuantumCircuit`` gate methods - :header-rows: 1 - - * - Instruction Type - - Former Signature - - New Signature - * - :class:`qiskit.extensions.HGate` - - ``qc.h(q)`` - - ``qc.h(qubit)`` - * - :class:`qiskit.extensions.CHGate` - - ``qc.ch(ctl, tgt)`` - - ``qc.ch((control_qubit, target_qubit))`` - * - :class:`qiskit.extensions.IdGate` - - ``qc.iden(q)`` - - ``qc.iden(qubit)`` - * - :class:`qiskit.extensions.RGate` - - ``qc.iden(q)`` - - ``qc.iden(qubit)`` - * - :class:`qiskit.extensions.RGate` - - ``qc.r(theta, phi, q)`` - - ``qc.r(theta, phi, qubit)`` - * - :class:`qiskit.extensions.RXGate` - - ``qc.rx(theta, q)`` - - ``qc.rx(theta, qubit)`` - * - :class:`qiskit.extensions.CrxGate` - - ``qc.crx(theta, ctl, tgt)`` - - ``qc.crx(theta, control_qubit, target_qubit)`` - * - :class:`qiskit.extensions.RYGate` - - ``qc.ry(theta, q)`` - - ``qc.ry(theta, qubit)`` - * - :class:`qiskit.extensions.CryGate` - - ``qc.cry(theta, ctl, tgt)`` - - ``qc.cry(theta, control_qubit, target_qubit)`` - * - :class:`qiskit.extensions.RZGate` - - ``qc.rz(phi, q)`` - - ``qc.rz(phi, qubit)`` - * - :class:`qiskit.extensions.CrzGate` - - ``qc.crz(theta, ctl, tgt)`` - - ``qc.crz(theta, control_qubit, target_qubit)`` - * - :class:`qiskit.extensions.SGate` - - ``qc.s(q)`` - - ``qc.s(qubit)`` - * - :class:`qiskit.extensions.SdgGate` - - ``qc.sdg(q)`` - - ``qc.sdg(qubit)`` - * - :class:`qiskit.extensions.FredkinGate` - - ``qc.cswap(ctl, tgt1, tgt2)`` - - ``qc.cswap(control_qubit, target_qubit1, target_qubit2)`` - * - :class:`qiskit.extensions.TGate` - - ``qc.t(q)`` - - ``qc.t(qubit)`` - * - :class:`qiskit.extensions.TdgGate` - - ``qc.tdg(q)`` - - ``qc.tdg(qubit)`` - * - :class:`qiskit.extensions.U1Gate` - - ``qc.u1(theta, q)`` - - ``qc.u1(theta, qubit)`` - * - :class:`qiskit.extensions.Cu1Gate` - - ``qc.cu1(theta, ctl, tgt)`` - - ``qc.cu1(theta, control_qubit, target_qubit)`` - * - :class:`qiskit.extensions.U2Gate` - - ``qc.u2(phi, lam, q)`` - - ``qc.u2(phi, lam, qubit)`` - * - :class:`qiskit.extensions.U3Gate` - - ``qc.u3(theta, phi, lam, q)`` - - ``qc.u3(theta, phi, lam, qubit)`` - * - :class:`qiskit.extensions.Cu3Gate` - - ``qc.cu3(theta, phi, lam, ctl, tgt)`` - - ``qc.cu3(theta, phi, lam, control_qubit, target_qubit)`` - * - :class:`qiskit.extensions.XGate` - - ``qc.x(q)`` - - ``qc.x(qubit)`` - * - :class:`qiskit.extensions.CnotGate` - - ``qc.cx(ctl, tgt)`` - - ``qc.cx(control_qubit, target_qubit)`` - * - :class:`qiskit.extensions.ToffoliGate` - - ``qc.ccx(ctl1, ctl2, tgt)`` - - ``qc.ccx(control_qubit1, control_qubit2, target_qubit)`` - * - :class:`qiskit.extensions.YGate` - - ``qc.y(q)`` - - ``qc.y(qubit)`` - * - :class:`qiskit.extensions.CyGate` - - ``qc.cy(ctl, tgt)`` - - ``qc.cy(control_qubit, target_qubit)`` - * - :class:`qiskit.extensions.ZGate` - - ``qc.z(q)`` - - ``qc.z(qubit)`` - * - :class:`qiskit.extensions.CzGate` - - ``qc.cz(ctl, tgt)`` - - ``qc.cz(control_qubit, target_qubit)`` - -- Running :class:`qiskit.pulse.Acquire` on multiple qubits has been - deprecated and will be removed in a future release. Additionally, the - :class:`qiskit.pulse.AcquireInstruction` parameters ``mem_slots`` and - ``reg_slots`` have been deprecated. Instead ``reg_slot`` and ``mem_slot`` - should be used instead. - -- The attribute of the :class:`qiskit.providers.models.PulseDefaults` class - ``circuit_instruction_map`` has been deprecated and will be removed in a - future release. Instead you should use the new attribute - ``instruction_schedule_map``. This was done to match the type of the - value of the attribute, which is an ``InstructionScheduleMap``. - -- The :class:`qiskit.pulse.PersistentValue` command is deprecated and will - be removed in a future release. Similar functionality can be achieved with - the :class:`qiskit.pulse.ConstantPulse` command (one of the new parametric - pulses). Compare the following:: - - from qiskit.pulse import Schedule, PersistentValue, ConstantPulse, \ - DriveChannel - - # deprecated implementation - sched_w_pv = Schedule() - sched_w_pv += PersistentValue(value=0.5)(DriveChannel(0)) - sched_w_pv += PersistentValue(value=0)(DriveChannel(0)) << 10 - - # preferred implementation - sched_w_const = Schedule() - sched_w_const += ConstantPulse(duration=10, amp=0.5)(DriveChannel(0)) - -- Python 3.5 support in qiskit-terra is deprecated. Support will be - removed in the first release after the upstream Python community's end of - life date for the version, which is 09/13/2020. - -- The ``require_cptp`` kwarg of the - :func:`qiskit.quantum_info.process_fidelity` function has been - deprecated and will be removed in a future release. It is superseded by - two separate kwargs ``require_cp`` and ``require_tp``. - -- Setting the ``scale`` parameter for - :meth:`qiskit.circuit.QuantumCircuit.draw` and - :func:`qiskit.visualization.circuit_drawer` as the first positional - argument is deprecated and will be removed in a future release. Instead you - should use ``scale`` as keyword argument. - -- The :mod:`qiskit.tools.qi.qi` module is deprecated and will be removed in a - future release. The legacy functions in the module have all been superseded - by functions and classes in the :mod:`qiskit.quantum_info` module. A table - of the deprecated functions and their replacement are below: - - .. list-table:: ``qiskit.tools.qi.qi`` replacements - :header-rows: 1 - - * - Deprecated - - Replacement - * - :func:`qiskit.tools.partial_trace` - - :func:`qiskit.quantum_info.partial_trace` - * - :func:`qiskit.tools.choi_to_pauli` - - :class:`qiskit.quantum_info.Choi` and :class:`quantum_info.PTM` - * - :func:`qiskit.tools.chop` - - ``numpy.round`` - * - ``qiskit.tools.qi.qi.outer`` - - ``numpy.outer`` - * - :func:`qiskit.tools.concurrence` - - :func:`qiskit.quantum_info.concurrence` - * - :func:`qiskit.tools.shannon_entropy` - - :func:`qiskit.quantum_info.shannon_entropy` - * - :func:`qiskit.tools.entropy` - - :func:`qiskit.quantum_info.entropy` - * - :func:`qiskit.tools.mutual_information` - - :func:`qiskit.quantum_info.mutual_information` - * - :func:`qiskit.tools.entanglement_of_formation` - - :func:`qiskit.quantum_info.entanglement_of_formation` - * - :func:`qiskit.tools.is_pos_def` - - ``quantum_info.operators.predicates.is_positive_semidefinite_matrix`` - -- The :mod:`qiskit.quantum_info.states.states` module is deprecated and will - be removed in a future release. The legacy functions in the module have - all been superseded by functions and classes in the - :mod:`qiskit.quantum_info` module. - - .. list-table:: ``qiskit.quantum_info.states.states`` replacements - :header-rows: 1 - - * - Deprecated - - Replacement - * - ``qiskit.quantum_info.states.states.basis_state`` - - :meth:`qiskit.quantum_info.Statevector.from_label` - * - ``qiskit.quantum_info.states.states.projector`` - - :class:`qiskit.quantum_info.DensityMatrix` - -- The ``scaling`` parameter of the ``draw()`` method for the ``Schedule`` and - ``Pulse`` objects was deprecated and will be removed in a future release. - Instead the new ``scale`` parameter should be used. This was done to have - a consistent argument between pulse and circuit drawings. For example:: - - #The consistency in parameters is seen below - #For circuits - circuit = QuantumCircuit() - circuit.draw(scale=0.2) - #For pulses - pulse = SamplePulse() - pulse.draw(scale=0.2) - #For schedules - schedule = Schedule() - schedule.draw(scale=0.2) - - -.. _Release Notes_0.12.0_Bug Fixes: - -Bug Fixes ---------- - -- Previously, calling :meth:`qiskit.circuit.QuantumCircuit.bind_parameters` - prior to decomposing a circuit would result in the bound values not being - correctly substituted into the decomposed gates. This has been resolved - such that binding and decomposition may occur in any order. Fixes - `issue #2482 `_ and - `issue #3509 `_ - -- The ``Collect2qBlocks`` pass had previously not considered classical - conditions when determining whether to include a gate within an - existing block. In some cases, this resulted in classical - conditions being lost when transpiling with - ``optimization_level=3``. This has been resolved so that classically - conditioned gates are never included in a block. - Fixes `issue #3215 `_ - -- All the output types for the circuit drawers in - :meth:`qiskit.circuit.QuantumCircuit.draw` and - :func:`qiskit.visualization.circuit_drawer` have fixed and/or improved - support for drawing controlled custom gates. Fixes - `issue #3546 `_, - `issue #3763 `_, - and `issue #3764 `_ - -- Explanation and examples have been added to documentation for the - :class:`qiskit.circuit.QuantumCircuit` methods for adding gates: - :meth:`qiskit.circuit.QuantumCircuit.ccx`, - :meth:`qiskit.circuit.QuantumCircuit.ch`, - :meth:`qiskit.circuit.QuantumCircuit.crz`, - :meth:`qiskit.circuit.QuantumCircuit.cswap`, - :meth:`qiskit.circuit.QuantumCircuit.cu1`, - :meth:`qiskit.circuit.QuantumCircuit.cu3`, - :meth:`qiskit.circuit.QuantumCircuit.cx`, - :meth:`qiskit.circuit.QuantumCircuit.cy`, - :meth:`qiskit.circuit.QuantumCircuit.cz`, - :meth:`qiskit.circuit.QuantumCircuit.h`, - :meth:`qiskit.circuit.QuantumCircuit.iden`, - :meth:`qiskit.circuit.QuantumCircuit.rx`, - :meth:`qiskit.circuit.QuantumCircuit.ry`, - :meth:`qiskit.circuit.QuantumCircuit.rz`, - :meth:`qiskit.circuit.QuantumCircuit.s`, - :meth:`qiskit.circuit.QuantumCircuit.sdg`, - :meth:`qiskit.circuit.QuantumCircuit.swap`, - :meth:`qiskit.circuit.QuantumCircuit.t`, - :meth:`qiskit.circuit.QuantumCircuit.tdg`, - :meth:`qiskit.circuit.QuantumCircuit.u1`, - :meth:`qiskit.circuit.QuantumCircuit.u2`, - :meth:`qiskit.circuit.QuantumCircuit.u3`, - :meth:`qiskit.circuit.QuantumCircuit.x`, - :meth:`qiskit.circuit.QuantumCircuit.y`, - :meth:`qiskit.circuit.QuantumCircuit.z`. Fixes - `issue #3400 `_ - -- Fixes for handling of complex number parameter in circuit visualization. - Fixes `issue #3640 `_ - - -.. _Release Notes_0.12.0_Other Notes: - -Other Notes ------------ - -- The transpiler passes in the :mod:`qiskit.transpiler.passes` directory have - been organized into subdirectories to better categorize them by - functionality. They are still all accessible under the - ``qiskit.transpiler.passes`` namespace. - -Aer 0.4.0 -========= - -Added ------ - * Added ``NoiseModel.from_backend`` for building a basic device noise model for an IBMQ - backend (\#569) - * Added multi-GPU enabled simulation methods to the ``QasmSimulator``, - ``StatevectorSimulator``, and ``UnitarySimulator``. The qasm simulator has gpu version - of the density matrix and statevector methods and can be accessed using - ``"method": "density_matrix_gpu"`` or ``"method": "statevector_gpu"`` in ``backend_options``. - The statevector simulator gpu method can be accessed using ``"method": "statevector_gpu"``. - The unitary simulator GPU method can be accessed using ``"method": "unitary_gpu"``. These - backends use CUDA and require an NVidia GPU.(\#544) - * Added ``PulseSimulator`` backend (\#542) - * Added ``PulseSystemModel`` and ``HamiltonianModel`` classes to represent models to be used - in ``PulseSimulator`` (\#496, \#493) - * Added ``duffing_model_generators`` to generate ``PulseSystemModel`` objects from a list - of parameters (\#516) - * Migrated ODE function solver to C++ (\#442, \#350) - * Added high level pulse simulator tests (\#379) - * CMake BLAS_LIB_PATH flag to set path to look for BLAS lib (\#543) - -Changed -------- - - * Changed the structure of the ``src`` directory to organise simulator source code. - Simulator controller headers were moved to ``src/controllers`` and simulator method State - headers are in ``src/simulators`` (\#544) - * Moved the location of several functions (\#568): - * Moved contents of ``qiskit.provider.aer.noise.errors`` into - the ``qiskit.providers.noise`` module - * Moved contents of ``qiskit.provider.aer.noise.utils`` into - the ``qiskit.provider.aer.utils`` module. - * Enabled optimization to aggregate consecutive gates in a circuit (fusion) by default (\#579). - -Deprecated ----------- - * Deprecated ``utils.qobj_utils`` functions (\#568) - * Deprecated ``qiskit.providers.aer.noise.device.basic_device_noise_model``. It is superseded - by the ``NoiseModel.from_backend`` method (\#569) - -Removed -------- - * Removed ``NoiseModel.as_dict``, ``QuantumError.as_dict``, ``ReadoutError.as_dict``, and - ``QuantumError.kron`` methods that were deprecated in 0.3 (\#568). - -Ignis 0.2 -========= - -No Change - -Aqua 0.6 -======== - -No Change - -IBM Q Provider 0.4.6 -==================== - -Added ------ - -- Several new methods were added to - :class:`IBMQBackend`: - - * :meth:`~qiskit.providers.ibmq.job.IBMQJob.wait_for_final_state` - blocks until the job finishes. It takes a callback function that it will invoke - after every query to provide feedback. - * :meth:`~qiskit.providers.ibmq.ibmqbackend.IBMQBackend.active_jobs` returns - the jobs submitted to a backend that are currently in an unfinished status. - * :meth:`~qiskit.providers.ibmq.ibmqbackend.IBMQBackend.job_limit` returns the - job limit for a backend. - * :meth:`~qiskit.providers.ibmq.ibmqbackend.IBMQBackend.remaining_jobs_count` returns the - number of jobs that you can submit to the backend before job limit is reached. - -- :class:`~qiskit.providers.ibmq.job.QueueInfo` now has a new - :meth:`~qiskit.providers.ibmq.job.QueueInfo.format` method that returns a - formatted string of the queue information. - -- :class:`IBMQJob` now has three new methods: - :meth:`~qiskit.providers.ibmq.job.IBMQJob.done`, - :meth:`~qiskit.providers.ibmq.job.IBMQJob.running`, and - :meth:`~qiskit.providers.ibmq.job.IBMQJob.cancelled` that are used to indicate job status. - -- :meth:`qiskit.providers.ibmq.ibmqbackend.IBMQBackend.run()` now accepts an optional `job_tags` - parameter. If specified, the `job_tags` are assigned to the job, which can later be used - as a filter in :meth:`qiskit.providers.ibmq.ibmqbackend.IBMQBackend.jobs()`. - -- :class:`~qiskit.providers.ibmq.managed.IBMQJobManager` now has a new method - :meth:`~qiskit.providers.ibmq.managed.IBMQJobManager.retrieve_job_set()` that allows - you to retrieve a previously submitted job set using the job set ID. - -Changed -------- - -- The ``Exception`` hierarchy has been refined with more specialized classes. - You can, however, continue to catch their parent exceptions (such - as ``IBMQAccountError``). Also, the exception class ``IBMQApiUrlError`` - has been replaced by ``IBMQAccountCredentialsInvalidUrl`` and - ``IBMQAccountCredentialsInvalidToken``. - -Deprecated ----------- - -- The use of proxy urls without a protocol (e.g. ``http://``) is deprecated - due to recent Python changes. - -############# -Qiskit 0.14.0 -############# - -Terra 0.11.0 -============ - -.. _Release Notes_0.11.0_Prelude: - -Prelude -------- - -The 0.11.0 release includes several new features and bug fixes. The biggest -change for this release is the addition of the pulse scheduler. This allows -users to define their quantum program as a ``QuantumCircuit`` and then map it -to the underlying pulse instructions that will control the quantum hardware to -implement the circuit. - -.. _Release Notes_0.11.0_New Features: - -New Features ------------- - -- Added 5 new commands to easily retrieve user-specific data from - ``BackendProperties``: ``gate_property``, ``gate_error``, ``gate_length``, - ``qubit_property``, ``t1``, ``t2``, ``readout_error`` and ``frequency``. - They return the specific values of backend properties. For example:: - - from qiskit.test.mock import FakeOurense - backend = FakeOurense() - properties = backend.properties() - - gate_property = properties.gate_property('u1') - gate_error = properties.gate_error('u1', 0) - gate_length = properties.gate_length('u1', 0) - qubit_0_property = properties.qubit_property(0) - t1_time_0 = properties.t1(0) - t2_time_0 = properties.t2(0) - readout_error_0 = properties.readout_error(0) - frequency_0 = properties.frequency(0) - -- Added method ``Instruction.is_parameterized()`` to check if an instruction - object is parameterized. This method returns ``True`` if and only if - instruction has a ``ParameterExpression`` or ``Parameter`` object for one - of its params. - -- Added a new analysis pass ``Layout2qDistance``. This pass allows to "score" - a layout selection, once ``property_set['layout']`` is set. The score will - be the sum of distances for each two-qubit gate in the circuit, when they - are not directly connected. This scoring does not consider direction in the - coupling map. The lower the number, the better the layout selection is. - - For example, consider a linear coupling map ``[0]--[2]--[1]`` and the - following circuit:: - - qr = QuantumRegister(2, 'qr') - circuit = QuantumCircuit(qr) - circuit.cx(qr[0], qr[1]) - - If the layout is ``{qr[0]:0, qr[1]:1}``, ``Layout2qDistance`` will set - ``property_set['layout_score'] = 1``. If the layout - is ``{qr[0]:0, qr[1]:2}``, then the result - is ``property_set['layout_score'] = 0``. The lower the score, the better. - -- Added ``qiskit.QuantumCircuit.cnot`` as an alias for the ``cx`` method of - ``QuantumCircuit``. The names ``cnot`` and ``cx`` are often used - interchangeably now the `cx` method can be called with either name. - -- Added ``qiskit.QuantumCircuit.toffoli`` as an alias for the ``ccx`` method - of ``QuantumCircuit``. The names ``toffoli`` and ``ccx`` are often used - interchangeably now the `ccx` method can be called with either name. - -- Added ``qiskit.QuantumCircuit.fredkin`` as an alias for the ``cswap`` - method of ``QuantumCircuit``. The names ``fredkin`` and ``cswap`` are - often used interchangeably now the `cswap` method can be called with - either name. - -- The ``latex`` output mode for ``qiskit.visualization.circuit_drawer()`` and - the ``qiskit.circuit.QuantumCircuit.draw()`` method now has a mode to - passthrough raw latex from gate labels and parameters. The syntax - for doing this mirrors matplotlib's - `mathtext mode `__ - syntax. Any portion of a label string between a pair of '$' characters will - be treated as raw latex and passed directly into the generated output latex. - This can be leveraged to add more advanced formatting to circuit diagrams - generated with the latex drawer. - - Prior to this release all gate labels were run through a utf8 -> latex - conversion to make sure that the output latex would compile the string as - expected. This is still what happens for all portions of a label outside - the '$' pair. Also if you want to use a dollar sign in your label make sure - you escape it in the label string (ie ``'\$'``). - - You can mix and match this passthrough with the utf8 -> latex conversion to - create the exact label you want, for example:: - - from qiskit import circuit - circ = circuit.QuantumCircuit(2) - circ.h([0, 1]) - circ.append(circuit.Gate(name='α_gate', num_qubits=1, params=[0]), [0]) - circ.append(circuit.Gate(name='α_gate$_2$', num_qubits=1, params=[0]), [1]) - circ.append(circuit.Gate(name='\$α\$_gate', num_qubits=1, params=[0]), [1]) - circ.draw(output='latex') - - will now render the first custom gate's label as ``α_gate``, the second - will be ``α_gate`` with a 2 subscript, and the last custom gate's label - will be ``$α$_gate``. - -- Add ``ControlledGate`` class for representing controlled - gates. Controlled gate instances are created with the - ``control(n)`` method of ``Gate`` objects where ``n`` represents - the number of controls. The control qubits come before the - controlled qubits in the new gate. For example:: - - from qiskit import QuantumCircuit - from qiskit.extensions import HGate - hgate = HGate() - circ = QuantumCircuit(4) - circ.append(hgate.control(3), [0, 1, 2, 3]) - print(circ) - - generates:: - - q_0: |0>──■── - │ - q_1: |0>──■── - │ - q_2: |0>──■── - ┌─┴─┐ - q_3: |0>┤ H ├ - └───┘ - -- Allowed values of ``meas_level`` parameters and fields can now be a member - from the `IntEnum` class ``qiskit.qobj.utils.MeasLevel``. This can be used - when calling ``execute`` (or anywhere else ``meas_level`` is specified) with - a pulse experiment. For example:: - - from qiskit import QuantumCircuit, transpile, schedule, execute - from qiskit.test.mock import FakeOpenPulse2Q - from qiskit.qobj.utils import MeasLevel, MeasReturnType - - backend = FakeOpenPulse2Q() - qc = QuantumCircuit(2, 2) - qc.h(0) - qc.cx(0,1) - qc_transpiled = transpile(qc, backend) - sched = schedule(qc_transpiled, backend) - execute(sched, backend, meas_level=MeasLevel.CLASSIFIED) - - In this above example, ``meas_level=MeasLevel.CLASSIFIED`` and - ``meas_level=2`` can be used interchangably now. - -- A new layout selector based on constraint solving is included. `CSPLayout` models the problem - of finding a layout as a constraint problem and uses recursive backtracking to solve it. - - .. code-block:: python - - cmap16 = CouplingMap(FakeRueschlikon().configuration().coupling_map) - - qr = QuantumRegister(5, 'q') - circuit = QuantumCircuit(qr) - circuit.cx(qr[0], qr[1]) - circuit.cx(qr[0], qr[2]) - circuit.cx(qr[0], qr[3]) - - pm = PassManager(CSPLayout(cmap16)) - circuit_after = pm.run(circuit) - print(pm.property_set['layout']) - - - .. code-block:: python - - Layout({ - 1: Qubit(QuantumRegister(5, 'q'), 1), - 2: Qubit(QuantumRegister(5, 'q'), 0), - 3: Qubit(QuantumRegister(5, 'q'), 3), - 4: Qubit(QuantumRegister(5, 'q'), 4), - 15: Qubit(QuantumRegister(5, 'q'), 2) - }) - - - The parameter ``CSPLayout(...,strict_direction=True)`` is more restrictive - but it will guarantee there is no need of running ``CXDirection`` after. - - .. code-block:: python - - pm = PassManager(CSPLayout(cmap16, strict_direction=True)) - circuit_after = pm.run(circuit) - print(pm.property_set['layout']) - - .. code-block:: python - - Layout({ - 8: Qubit(QuantumRegister(5, 'q'), 4), - 11: Qubit(QuantumRegister(5, 'q'), 3), - 5: Qubit(QuantumRegister(5, 'q'), 1), - 6: Qubit(QuantumRegister(5, 'q'), 0), - 7: Qubit(QuantumRegister(5, 'q'), 2) - }) - - If the constraint system is not solvable, the `layout` property is not set. - - .. code-block:: python - - circuit.cx(qr[0], qr[4]) - pm = PassManager(CSPLayout(cmap16)) - circuit_after = pm.run(circuit) - print(pm.property_set['layout']) - - .. code-block:: python - - None - -- PulseBackendConfiguration (accessed normally as backend.configuration()) - has been extended with useful methods to explore its data and the - functionality that exists in PulseChannelSpec. PulseChannelSpec will be - deprecated in the future. For example:: - - backend = provider.get_backend(backend_name) - config = backend.configuration() - q0_drive = config.drive(0) # or, DriveChannel(0) - q0_meas = config.measure(0) # MeasureChannel(0) - q0_acquire = config.acquire(0) # AcquireChannel(0) - config.hamiltonian # Returns a dictionary with hamiltonian info - config.sample_rate() # New method which returns 1 / dt - -- ``PulseDefaults`` (accessed normally as ``backend.defaults()``) has an - attribute, ``circuit_instruction_map`` which has the methods of CmdDef. - The new `circuit_instruction_map` is an ``InstructionScheduleMap`` object - with three new functions beyond what CmdDef had: - - * qubit_instructions(qubits) returns the operations defined for the qubits - * assert_has(instruction, qubits) raises an error if the op isn't defined - * remove(instruction, qubits) like pop, but doesn't require parameters - - There are some differences from the CmdDef: - - * ``__init__`` takes no arguments - * ``cmds`` and ``cmd_qubits`` are deprecated and replaced with - ``instructions`` and ``qubits_with_instruction`` - - Example:: - - backend = provider.get_backend(backend_name) - inst_map = backend.defaults().circuit_instruction_map - qubit = inst_map.qubits_with_instruction('u3')[0] - x_gate = inst_map.get('u3', qubit, P0=np.pi, P1=0, P2=np.pi) - pulse_schedule = x_gate(DriveChannel(qubit)) - -- A new kwarg parameter, ``show_framechange_channels`` to optionally disable - displaying channels with only framechange instructions in pulse - visualizations was added to the ``qiskit.visualization.pulse_drawer()`` - function and ``qiskit.pulse.Schedule.draw()`` method. When this new kwarg - is set to ``False`` the output pulse schedule visualization will not - include any channels that only include frame changes. - - For example:: - - from qiskit.pulse import * - from qiskit.pulse import library as pulse_lib - - gp0 = pulse_lib.gaussian(duration=20, amp=1.0, sigma=1.0) - sched = Schedule() - channel_a = DriveChannel(0) - channel_b = DriveChannel(1) - sched += Play(gp0, channel_a) - sched = sched.insert(60, ShiftPhase(-1.57, channel_a)) - sched = sched.insert(30, ShiftPhase(-1.50, channel_b)) - sched = sched.insert(70, ShiftPhase(1.50, channel_b)) - - sched.draw(show_framechange_channels=False) - -- A new utility function ``qiskit.result.marginal_counts()`` is added - which allows marginalization of the counts over some indices of interest. - This is useful when more qubits are measured than needed, and one wishes - to get the observation counts for some subset of them only. - -- When ``passmanager.run(...)`` is invoked with more than one circuit, the - transpilation of these circuits will run in parallel. - -- PassManagers can now be sliced to create a new PassManager containing a - subset of passes using the square bracket operator. This allow running or - drawing a portion of the PassManager for easier testing and visualization. - For example let's try to draw the first 3 passes of a PassManager pm, or - run just the second pass on our circuit: - - .. code-block:: python - - pm[0:4].draw() - circuit2 = pm[1].run(circuit) - - Also now, PassManagers can be created by adding two PassManagers or by - directly adding a pass/list of passes to a PassManager. - - .. code-block:: python - - pm = pm1[0] + pm2[1:3] - pm += [setLayout, unroller] - -- A basic ``scheduler`` module has now been added to Qiskit. The `scheduler` - schedules an input transpiled ``QuantumCircuit`` into a pulse - ``Schedule``. The scheduler accepts as input a ``Schedule`` and either a - pulse ``Backend``, or a ``CmdDef`` which relates circuit ``Instruction`` - objects on specific qubits to pulse Schedules and a ``meas_map`` which - determines which measurements must occur together. - - Scheduling example:: - - from qiskit import QuantumCircuit, transpile, schedule - from qiskit.test.mock import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - qc = QuantumCircuit(2, 2) - qc.h(0) - qc.cx(0,1) - qc_transpiled = transpile(qc, backend) - schedule(qc_transpiled, backend) - - The scheduler currently supports two scheduling policies, - `as_late_as_possible` (``alap``) and `as_soon_as_possible` (``asap``), which - respectively schedule pulse instructions to occur as late as - possible or as soon as possible across qubits in a circuit. - The scheduling policy may be selected with the input argument ``method``, - for example:: - - schedule(qc_transpiled, backend, method='alap') - - It is easy to use a pulse ``Schedule`` within a ``QuantumCircuit`` by - mapping it to a custom circuit instruction such as a gate which may be used - in a ``QuantumCircuit``. To do this, first, define the custom gate and then - add an entry into the ``CmdDef`` for the gate, for each qubit that the gate - will be applied to. The gate can then be used in the ``QuantumCircuit``. - At scheduling time the gate will be mapped to the underlying pulse schedule. - Using this technique allows easy integration with preexisting qiskit modules - such as Ignis. - - For example:: - - from qiskit import pulse, circuit, schedule - from qiskit.pulse import pulse_lib - - custom_cmd_def = pulse.CmdDef() - - # create custom gate - custom_gate = circuit.Gate(name='custom_gate', num_qubits=1, params=[]) - - # define schedule for custom gate - custom_schedule = pulse.Schedule() - custom_schedule += pulse_lib.gaussian(20, 1.0, 10)(pulse.DriveChannel) - - # add schedule to custom gate with same name - custom_cmd_def.add('custom_gate', (0,), custom_schedule) - - # use custom gate in a circuit - custom_qc = circuit.QuantumCircuit(1) - custom_qc.append(custom_gate, qargs=[0]) - - # schedule the custom gate - schedule(custom_qc, cmd_def=custom_cmd_def, meas_map=[[0]]) - - -.. _Release Notes_0.11.0_Known Issues: - -Known Issues ------------- - -- The feature for transpiling in parallel when ``passmanager.run(...)`` is - invoked with more than one circuit is not supported under Windows. See - `#2988 `__ for more - details. - - -.. _Release Notes_0.11.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The ``qiskit.pulse.channels.SystemTopology`` class was used as a helper - class for ``PulseChannelSpec``. It has been removed since with the deprecation - of ``PulseChannelSpec`` and changes to ``BackendConfiguration`` make it - unnecessary. - -- The previously deprecated representation of qubits and classical bits as - tuple, which was deprecated in the 0.9 release, has been removed. The use - of ``Qubit`` and ``Clbit`` objects is the new way to represent qubits and - classical bits. - -- The previously deprecated representation of the basis set as single string - has been removed. A list of strings is the new preferred way. - -- The method ``BaseModel.as_dict``, which was deprecated in the 0.9 release, - has been removed in favor of the method ``BaseModel.to_dict``. - -- In PulseDefaults (accessed normally as backend.defaults()), - ``qubit_freq_est`` and ``meas_freq_est`` are now returned in Hz rather than - GHz. This means the new return values are 1e9 * their previous value. - -- `dill `__ was added as a requirement. This - is needed to enable running ``passmanager.run()`` in parallel for more than - one circuit. - -- The previously deprecated gate ``UBase``, which was deprecated - in the 0.9 release, has been removed. The gate ``U3Gate`` - should be used instead. - -- The previously deprecated gate ``CXBase``, which was deprecated - in the 0.9 release, has been removed. The gate ``CnotGate`` - should be used instead. - -- The instruction ``snapshot`` used to implicitly convert the ``label`` - parameter to string. That conversion has been removed and an error is raised - if a string is not provided. - -- The previously deprecated gate ``U0Gate``, which was deprecated - in the 0.9 release, has been removed. The gate ``IdGate`` - should be used instead to insert delays. - - -.. _Release Notes_0.11.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The ``qiskit.pulse.CmdDef`` class has been deprecated. Instead you should - use the ``qiskit.pulse.InstructionScheduleMap``. The - ``InstructionScheduleMap`` object for a pulse enabled system can be - accessed at ``backend.defaults().instruction_schedules``. - -- ``PulseChannelSpec`` is being deprecated. Use ``BackendConfiguration`` - instead. The backend configuration is accessed normally as - ``backend.configuration()``. The config has been extended with most of - the functionality of PulseChannelSpec, with some modifications as follows, - where `0` is an exemplary qubit index:: - - pulse_spec.drives[0] -> config.drive(0) - pulse_spec.measures[0] -> config.measure(0) - pulse_spec.acquires[0] -> config.acquire(0) - pulse_spec.controls[0] -> config.control(0) - - Now, if there is an attempt to get a channel for a qubit which does not - exist for the device, a ``BackendConfigurationError`` will be raised with - a helpful explanation. - - The methods ``memoryslots`` and ``registerslots`` of the PulseChannelSpec - have not been migrated to the backend configuration. These classical - resources are not restrained by the physical configuration of a backend - system. Please instantiate them directly:: - - pulse_spec.memoryslots[0] -> MemorySlot(0) - pulse_spec.registerslots[0] -> RegisterSlot(0) - - The ``qubits`` method is not migrated to backend configuration. The result - of ``qubits`` can be built as such:: - - [q for q in range(backend.configuration().n_qubits)] - -- ``Qubit`` within ``pulse.channels`` has been deprecated. They should not - be used. It is possible to obtain channel <=> qubit mappings through the - BackendConfiguration (or backend.configuration()). - -- The function ``qiskit.visualization.circuit_drawer.qx_color_scheme()`` has - been deprecated. This function is no longer used internally and doesn't - reflect the current IBM QX style. If you were using this function to - generate a style dict locally you must save the output from it and use - that dictionary directly. - -- The Exception ``TranspilerAccessError`` has been deprecated. An - alternative function ``TranspilerError`` can be used instead to provide - the same functionality. This alternative function provides the exact same - functionality but with greater generality. - -- Buffers in Pulse are deprecated. If a nonzero buffer is supplied, a warning - will be issued with a reminder to use a Delay instead. Other options would - include adding samples to a pulse instruction which are (0.+0.j) or setting - the start time of the next pulse to ``schedule.duration + buffer``. - -- Passing in ``sympy.Basic``, ``sympy.Expr`` and ``sympy.Matrix`` types as - instruction parameters are deprecated and will be removed in a future - release. You'll need to convert the input to one of the supported types - which are: - - * ``int`` - * ``float`` - * ``complex`` - * ``str`` - * ``np.ndarray`` - - -.. _Release Notes_0.11.0_Bug Fixes: - -Bug Fixes ---------- - -- The Collect2qBlocks and CommutationAnalysis passes in the transpiler had been - unable to process circuits containing Parameterized gates, preventing - Parameterized circuits from being transpiled at optimization_level 2 or - above. These passes have been corrected to treat Parameterized gates as - opaque. - -- The align_measures function had an issue where Measure stimulus - pulses weren't properly aligned with Acquire pulses, resulting in - an error. This has been fixed. - -- Uses of ``numpy.random.seed`` have been removed so that calls of qiskit - functions do not affect results of future calls to ``numpy.random`` - -- Fixed race condition occurring in the job monitor when - ``job.queue_position()`` returns ``None``. ``None`` is a valid - return from ``job.queue_position()``. - -- Backend support for ``memory=True`` now checked when that kwarg is passed. - ``QiskitError`` results if not supported. - -- When transpiling without a coupling map, there were no check in the amount - of qubits of the circuit to transpile. Now the transpile process checks - that the backend has enough qubits to allocate the circuit. - - -.. _Release Notes_0.11.0_Other Notes: - -Other Notes ------------ - -- The ``qiskit.result.marginal_counts()`` function replaces a similar utility - function in qiskit-ignis - ``qiskit.ignis.verification.tomography.marginal_counts()``, which - will be deprecated in a future qiskit-ignis release. - -- All sympy parameter output type support have been been removed (or - deprecated as noted) from qiskit-terra. This includes sympy type - parameters in ``QuantumCircuit`` objects, qasm ast nodes, or ``Qobj`` - objects. - -Aer 0.3 -======= - -No Change - -Ignis 0.2 -========= - -No Change - -Aqua 0.6 -======== - -No Change - -IBM Q Provider 0.4 -================== - -Prelude -------- - -The 0.4.0 release is the first release that makes use of all the features -of the new IBM Q API. In particular, the ``IBMQJob`` class has been revamped in -order to be able to retrieve more information from IBM Q, and a Job Manager -class has been added for allowing a higher-level and more seamless usage of -large or complex jobs. If you have not upgraded from the legacy IBM Q -Experience or QConsole yet, please ensure to revisit the release notes for -IBM Q Provider 0.3 (Qiskit 0.11) for more details on how to make the -transition. The legacy accounts will no longer be supported as of this release. - - -New Features ------------- - -Job modifications -^^^^^^^^^^^^^^^^^ - -The ``IBMQJob`` class has been revised, and now mimics more closely to the -contents of a remote job along with new features: - -* You can now assign a name to a job, by specifying - ``IBMQBackend.run(..., job_name='...')`` when submitting a job. This name - can be retrieved via ``IBMQJob.name()`` and can be used for filtering. -* Jobs can now be shared with other users at different levels (global, per - hub, group or project) via an optional ``job_share_level`` parameter when - submitting the job. -* ``IBMQJob`` instances now have more attributes, reflecting the contents of the - remote IBM Q jobs. This implies that new attributes introduced by the IBM Q - API will automatically and immediately be available for use (for example, - ``job.new_api_attribute``). The new attributes will be promoted to methods - when they are considered stable (for example, ``job.name()``). -* ``.error_message()`` returns more information on why a job failed. -* ``.queue_position()`` accepts a ``refresh`` parameter for forcing an update. -* ``.result()`` accepts an optional ``partial`` parameter, for returning - partial results, if any, of jobs that failed. Be aware that ``Result`` - methods, such as ``get_counts()`` will raise an exception if applied on - experiments that failed. - -Please note that the changes include some low-level modifications of the class. -If you were creating the instances manually, note that: - -* the signature of the constructor has changed to account for the new features. -* the ``.submit()`` method can no longer be called directly, and jobs are - expected to be submitted either via the synchronous ``IBMQBackend.run()`` or - via the Job Manager. - -Job Manager -^^^^^^^^^^^ - -A new Job Manager (``IBMQJobManager``) has been introduced, as a higher-level -mechanism for handling jobs composed of multiple circuits or pulse schedules. -The Job Manager aims to provide a transparent interface, intelligently splitting -the input into efficient units of work and taking full advantage of the -different components. It will be expanded on upcoming versions, and become the -recommended entry point for job submission. - -Its ``.run()`` method receives a list of circuits or pulse schedules, and -returns a ``ManagedJobSet instance``, which can then be used to track the -statuses and results of these jobs. For example:: - - from qiskit.providers.ibmq.managed import IBMQJobManager - from qiskit.circuit.random import random_circuit - from qiskit import IBMQ - from qiskit.compiler import transpile - - provider = IBMQ.load_account() - backend = provider.backends.ibmq_ourense - - circs = [] - for _ in range(1000000): - circs.append(random_circuit(2, 2)) - transpile(circs, backend=backend) - - # Farm out the jobs. - jm = IBMQJobManager() - job_set = jm.run(circs, backend=backend, name='foo') - - job_set.statuses() # Gives a list of job statuses - job_set.report() # Prints detailed job information - results = job_set.results() - counts = results.get_counts(5) # Returns data for experiment 5 - - -provider.backends modifications -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``provider.backends`` member, which was previously a function that returned -a list of backends, has been promoted to a service. This implies that it can -be used both in the previous way, as a ``.backends()`` method, and also as a -``.backends`` attribute with expanded capabilities: - -* it contains the existing backends from that provider as attributes, which - can be used for autocompletion. For example:: - - my_backend = provider.get_backend('ibmq_qasm_simulator') - - is equivalent to:: - - my_backend = provider.backends.ibmq_qasm_simulator - -* the ``provider.backends.jobs()`` and ``provider.backends.retrieve_job()`` - methods can be used for retrieving provider-wide jobs. - - -Other changes -^^^^^^^^^^^^^ - -* The ``backend.properties()`` function now accepts an optional ``datetime`` - parameter. If specified, the function returns the backend properties - closest to, but older than, the specified datetime filter. -* Some ``warnings`` have been toned down to ``logger.warning`` messages. - - -############# -Qiskit 0.13.0 -############# - -Terra 0.10.0 -============ - -.. _Release Notes_0.10.0_Prelude: - -Prelude -------- - -The 0.10.0 release includes several new features and bug fixes. The biggest -change for this release is the addition of initial support for using Qiskit -with trapped ion trap backends. - - -.. _Release Notes_0.10.0_New Features: - -New Features ------------- - -- Introduced new methods in ``QuantumCircuit`` which allows the seamless adding or removing - of measurements at the end of a circuit. - - ``measure_all()`` - Adds a ``barrier`` followed by a ``measure`` operation to all qubits in the circuit. - Creates a ``ClassicalRegister`` of size equal to the number of qubits in the circuit, - which store the measurements. - - ``measure_active()`` - Adds a ``barrier`` followed by a ``measure`` operation to all active qubits in the circuit. - A qubit is active if it has at least one other operation acting upon it. - Creates a ``ClassicalRegister`` of size equal to the number of active qubits in the circuit, - which store the measurements. - - ``remove_final_measurements()`` - Removes all final measurements and preceeding ``barrier`` from a circuit. - A measurement is considered "final" if it is not followed by any other operation, - excluding barriers and other measurements. - After the measurements are removed, if all of the classical bits in the ``ClassicalRegister`` - are idle (have no operations attached to them), then the ``ClassicalRegister`` is removed. - - Examples:: - - # Using measure_all() - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.measure_all() - circuit.draw() - - # A ClassicalRegister with prefix measure was created. - # It has 2 clbits because there are 2 qubits to measure - - ┌───┐ ░ ┌─┐ - q_0: |0>┤ H ├─░─┤M├─── - └───┘ ░ └╥┘┌─┐ - q_1: |0>──────░──╫─┤M├ - ░ ║ └╥┘ - measure_0: 0 ═════════╩══╬═ - ║ - measure_1: 0 ════════════╩═ - - - # Using measure_active() - circuit = QuantumCircuit(2) - circuit.h(0) - circuit.measure_active() - circuit.draw() - - # This ClassicalRegister only has 1 clbit because only 1 qubit is active - - ┌───┐ ░ ┌─┐ - q_0: |0>┤ H ├─░─┤M├ - └───┘ ░ └╥┘ - q_1: |0>──────░──╫─ - ░ ║ - measure_0: 0 ═════════╩═ - - - # Using remove_final_measurements() - # Assuming circuit_all and circuit_active are the circuits from the measure_all and - # measure_active examples above respectively - - circuit_all.remove_final_measurements() - circuit_all.draw() - # The ClassicalRegister is removed because, after the measurements were removed, - # all of its clbits were idle - - ┌───┐ - q_0: |0>┤ H ├ - └───┘ - q_1: |0>───── - - - circuit_active.remove_final_measurements() - circuit_active.draw() - # This will result in the same circuit - - ┌───┐ - q_0: |0>┤ H ├ - └───┘ - q_1: |0>───── - -- Initial support for executing experiments on ion trap backends has been - added. - -- An Rxx gate (rxx) and a global Mølmer–Sørensen gate (ms) have been added - to the standard gate set. - -- A Cnot to Rxx/Rx/Ry decomposer ``cnot_rxx_decompose`` and a single qubit - Euler angle decomposer ``OneQubitEulerDecomposer`` have been added to the - ``quantum_info.synthesis`` module. - -- A transpiler pass ``MSBasisDecomposer`` has been added to unroll circuits - defined over U3 and Cnot gates into a circuit defined over Rxx,Ry and Rx. - This pass will be included in preset pass managers for backends which - include the 'rxx' gate in their supported basis gates. - -- The backends in ``qiskit.test.mock`` now contain a snapshot of real - device calibration data. This is accessible via the ``properties()`` method - for each backend. This can be used to test any code that depends on - backend properties, such as noise-adaptive transpiler passes or device - noise models for simulation. This will create a faster testing and - development cycle without the need to go to live backends. - -- Allows the Result class to return partial results. If a valid result schema - is loaded that contains some experiments which succeeded and some which - failed, this allows accessing the data from experiments that succeeded, - while raising an exception for experiments that failed and displaying the - appropriate error message for the failed results. - -- An ``ax`` kwarg has been added to the following visualization functions: - - * ``qiskit.visualization.plot_histogram`` - * ``qiskit.visualization.plot_state_paulivec`` - * ``qiskit.visualization.plot_state_qsphere`` - * ``qiskit.visualization.circuit_drawer`` (``mpl`` backend only) - * ``qiskit.QuantumCircuit.draw`` (``mpl`` backend only) - - This kwarg is used to pass in a ``matplotlib.axes.Axes`` object to the - visualization functions. This enables integrating these visualization - functions into a larger visualization workflow. Also, if an `ax` kwarg is - specified then there is no return from the visualization functions. - -- An ``ax_real`` and ``ax_imag`` kwarg has been added to the - following visualization functions: - - * ``qiskit.visualization.plot_state_hinton`` - * ``qiskit.visualization.plot_state_city`` - - These new kargs work the same as the newly added ``ax`` kwargs for other - visualization functions. However because these plots use two axes (one for - the real component, the other for the imaginary component). Having two - kwargs also provides the flexibility to only generate a visualization for - one of the components instead of always doing both. For example:: - - from matplotlib import pyplot as plt - from qiskit.visualization import plot_state_hinton - - ax = plt.gca() - - plot_state_hinton(psi, ax_real=ax) - - will only generate a plot of the real component. - -- A given pass manager now can be edited with the new method `replace`. This method allows to - replace a particular stage in a pass manager, which can be handy when dealing with preset - pass managers. For example, let's edit the layout selector of the pass manager used at - optimization level 0: - - .. code-block:: python - - from qiskit.transpiler.preset_passmanagers.level0 import level_0_pass_manager - from qiskit.transpiler.transpile_config import TranspileConfig - - pass_manager = level_0_pass_manager(TranspileConfig(coupling_map=CouplingMap([[0,1]]))) - - pass_manager.draw() - - .. code-block:: - - [0] FlowLinear: SetLayout - [1] Conditional: TrivialLayout - [2] FlowLinear: FullAncillaAllocation, EnlargeWithAncilla, ApplyLayout - [3] FlowLinear: Unroller - - The layout selection is set in the stage `[1]`. Let's replace it with `DenseLayout`: - - .. code-block:: python - - from qiskit.transpiler.passes import DenseLayout - - pass_manager.replace(1, DenseLayout(coupling_map), condition=lambda property_set: not property_set['layout']) - pass_manager.draw() - - .. code-block:: - - [0] FlowLinear: SetLayout - [1] Conditional: DenseLayout - [2] FlowLinear: FullAncillaAllocation, EnlargeWithAncilla, ApplyLayout - [3] FlowLinear: Unroller - - If you want to replace it without any condition, you can use set-item shortcut: - - .. code-block:: python - - pass_manager[1] = DenseLayout(coupling_map) - pass_manager.draw() - - .. code-block:: - - [0] FlowLinear: SetLayout - [1] FlowLinear: DenseLayout - [2] FlowLinear: FullAncillaAllocation, EnlargeWithAncilla, ApplyLayout - [3] FlowLinear: Unroller - -- Introduced a new pulse command ``Delay`` which may be inserted into a pulse - ``Schedule``. This command accepts a ``duration`` and may be added to any - ``Channel``. Other commands may not be scheduled on a channel during a delay. - - The delay can be added just like any other pulse command. For example:: - - from qiskit import pulse - from qiskit.pulse.utils import pad - - dc0 = pulse.DriveChannel(0) - - delay = pulse.Delay(1) - test_pulse = pulse.SamplePulse([1.0]) - - sched = pulse.Schedule() - sched += test_pulse(dc0).shift(1) - - # build padded schedule by hand - ref_sched = delay(dc0) | sched - - # pad schedule - padded_sched = pad(sched) - - assert padded_sched == ref_sched - - One may also pass additional channels to be padded and a time to pad until, - for example:: - - from qiskit import pulse - from qiskit.pulse.utils import pad - - dc0 = pulse.DriveChannel(0) - dc1 = pulse.DriveChannel(1) - - delay = pulse.Delay(1) - test_pulse = pulse.SamplePulse([1.0]) - - sched = pulse.Schedule() - sched += test_pulse(dc0).shift(1) - - # build padded schedule by hand - ref_sched = delay(dc0) | delay(dc1) | sched - - # pad schedule across both channels until up until the first time step - padded_sched = pad(sched, channels=[dc0, dc1], until=1) - - assert padded_sched == ref_sched - - -.. _Release Notes_0.10.0_Upgrade Notes: - -Upgrade Notes -------------- - -- Assignments and modifications to the ``data`` attribute of - ``qiskit.QuantumCircuit`` objects are now validated following the same - rules used throughout the ``QuantumCircuit`` API. This was done to - improve the performance of the circuits API since we can now assume the - ``data`` attribute is in a known format. If you were manually modifying - the ``data`` attribute of a circuit object before this may no longer work - if your modifications resulted in a data structure other than the list - of instructions with context in the format ``[(instruction, qargs, cargs)]`` - -- The transpiler default passmanager for optimization level 2 now uses the - ``DenseLayout`` layout selection mechanism by default instead of - ``NoiseAdaptiveLayout``. The ``Denselayout`` pass has also been modified - to be made noise-aware. - -- The deprecated ``DeviceSpecification`` class has been removed. Instead you should - use the ``PulseChannelSpec``. For example, you can run something like:: - - device = pulse.PulseChannelSpec.from_backend(backend) - device.drives[0] # for DeviceSpecification, this was device.q[0].drive - device.memoryslots # this was device.mem - -- The deprecated module ``qiskit.pulse.ops`` has been removed. Use - ``Schedule`` and ``Instruction`` methods directly. For example, rather - than:: - - ops.union(schedule_0, schedule_1) - ops.union(instruction, schedule) # etc - - Instead please use:: - - schedule_0.union(schedule_1) - instruction.union(schedule) - - This same pattern applies to other ``ops`` functions: ``insert``, ``shift``, - ``append``, and ``flatten``. - - -.. _Release Notes_0.10.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- Using the ``control`` property of ``qiskit.circuit.Instruction`` for - classical control is now deprecated. In the future this property will be - used for quantum control. Classically conditioned operations will instead - be handled by the ``condition`` property of ``qiskit.circuit.Instruction``. - -- Support for setting ``qiskit.circuit.Instruction`` parameters with an object - of type ``qiskit.qasm.node.Node`` has been deprecated. ``Node`` objects that - were previously used as parameters should be converted to a supported type - prior to initializing a new ``Instruction`` object or calling the - ``Instruction.params`` setter. Supported types are ``int``, ``float``, - ``complex``, ``str``, ``qiskit.circuit.ParameterExpression``, or - ``numpy.ndarray``. - -- In the qiskit 0.9.0 release the representation of bits (both qubits and - classical bits) changed from tuples of the form ``(register, index)`` to be - instances of the classes ``qiskit.circuit.Qubit`` and - ``qiskit.circuit.Clbit``. For backwards compatibility comparing - the equality between a legacy tuple and the bit classes was supported as - everything transitioned from tuples to being objects. This support is now - deprecated and will be removed in the future. Everything should use the bit - classes instead of tuples moving forward. - -- When the ``mpl`` output is used for either ``qiskit.QuantumCircuit.draw()`` - or ``qiskit.visualization.circuit_drawer()`` and the ``style`` kwarg is - used, passing in unsupported dictionary keys as part of the ``style``` - dictionary is now deprecated. Where these unknown arguments were previously - silently ignored, in the future, unsupported keys will raise an exception. - -- The ``line length`` kwarg for the ``qiskit.QuantumCircuit.draw()`` method - and the ``qiskit.visualization.circuit_drawer()`` function with the text - output mode is deprecated. It has been replaced by the ``fold`` kwarg which - will behave identically for the text output mode (but also now supports - the mpl output mode too). ``line_length`` will be removed in a future - release so calls should be updated to use ``fold`` instead. - -- The ``fold`` field in the ``style`` dict kwarg for the - ``qiskit.QuantumCircuit.draw()`` method and the - ``qiskit.visualization.circuit_drawer()`` function has been deprecated. It - has been replaced by the ``fold`` kwarg on both functions. This kwarg - behaves identically to the field in the style dict. - - -.. _Release Notes_0.10.0_Bug Fixes: - -Bug Fixes ---------- - -- Instructions layering which underlies all types of circuit drawing has - changed to address right/left justification. This sometimes results in - output which is topologically equivalent to the rendering in prior versions - but visually different than previously rendered. Fixes - `issue #2802 `_ - -- Add ``memory_slots`` to ``QobjExperimentHeader`` of pulse Qobj. This fixes - a bug in the data format of ``meas_level=2`` results of pulse experiments. - Measured quantum states are returned as a bit string with zero padding - based on the number set for ``memory_slots``. - -- Fixed the visualization of the rzz gate in the latex circuit drawer to match - the cu1 gate to reflect the symmetry in the rzz gate. The fix is based on - the cds command of the qcircuit latex package. Fixes - `issue #1957 `_ - - -.. _Release Notes_0.10.0_Other Notes: - -Other Notes ------------ - -- ``matplotlib.figure.Figure`` objects returned by visualization functions - are no longer always closed by default. Instead the returned figure objects - are only closed if the configured matplotlib backend is an inline jupyter - backend(either set with ``%matplotlib inline`` or - ``%matplotlib notebook``). Output figure objects are still closed with - these backends to avoid duplicate outputs in jupyter notebooks (which is - why the ``Figure.close()`` were originally added). - -Aer 0.3 -======= - -No Change - -Ignis 0.2 -========= - -No Change - -Aqua 0.6 -======== - -No Change - -IBM Q Provider 0.3 -================== - -No Change - -############# -Qiskit 0.12.0 -############# - -.. _Release Notes_0.9.0: - -Terra 0.9 -========= - -.. _Release Notes_0.9.0_Prelude: - -Prelude -------- - -The 0.9 release includes many new features and many bug fixes. The biggest -changes for this release are new debugging capabilities for PassManagers. This -includes a function to visualize a PassManager, the ability to add a callback -function to a PassManager, and logging of passes run in the PassManager. -Additionally, this release standardizes the way that you can set an initial -layout for your circuit. So now you can leverage ``initial_layout`` the kwarg -parameter on ``qiskit.compiler.transpile()`` and ``qiskit.execute()`` and the -qubits in the circuit will get laid out on the desire qubits on the device. -Visualization of circuits will now also show this clearly when visualizing a -circuit that has been transpiled with a layout. - -.. _Release Notes_0.9.0_New Features: - -New Features ------------- - -- A ``DAGCircuit`` object (i.e. the graph representation of a QuantumCircuit where - operation dependencies are explicit) can now be visualized with the ``.draw()`` - method. This is in line with Qiskit's philosophy of easy visualization. - Other objects which support a ``.draw()`` method are ``QuantumCircuit``, - ``PassManager``, and ``Schedule``. - -- Added a new visualization function - ``qiskit.visualization.plot_error_map()`` to plot the error map for a given - backend. It takes in a backend object from the qiskit-ibmq-provider and - will plot the current error map for that device. - -- Both ``qiskit.QuantumCircuit.draw()`` and - ``qiskit.visualization.circuit_drawer()`` now support annotating the - qubits in the visualization with layout information. If the - ``QuantumCircuit`` object being drawn includes layout metadata (which is - normally only set on the circuit output from ``transpile()`` calls) then - by default that layout will be shown on the diagram. This is done for all - circuit drawer backends. For example:: - - from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister - from qiskit.compiler import transpile - - qr = QuantumRegister(2, 'userqr') - cr = ClassicalRegister(2, 'c0') - qc = QuantumCircuit(qr, cr) - qc.h(qr[0]) - qc.cx(qr[0], qr[1]) - qc.y(qr[0]) - qc.x(qr[1]) - qc.measure(qr, cr) - - # Melbourne coupling map - coupling_map = [[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], - [5, 6], [5, 9], [6, 8], [7, 8], [9, 8], [9, 10], - [11, 3], [11, 10], [11, 12], [12, 2], [13, 1], - [13, 12]] - qc_result = transpile(qc, basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], - coupling_map=coupling_map, optimization_level=0) - qc.draw(output='text') - - will yield a diagram like:: - - ┌──────────┐┌──────────┐┌───┐┌──────────┐┌──────────────────┐┌─┐ - (userqr0) q0|0>┤ U2(0,pi) ├┤ U2(0,pi) ├┤ X ├┤ U2(0,pi) ├┤ U3(pi,pi/2,pi/2) ├┤M├─── - ├──────────┤└──────────┘└─┬─┘├──────────┤└─┬─────────────┬──┘└╥┘┌─┐ - (userqr1) q1|0>┤ U2(0,pi) ├──────────────■──┤ U2(0,pi) ├──┤ U3(pi,0,pi) ├────╫─┤M├ - └──────────┘ └──────────┘ └─────────────┘ ║ └╥┘ - (ancilla0) q2|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla1) q3|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla2) q4|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla3) q5|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla4) q6|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla5) q7|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla6) q8|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla7) q9|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla8) q10|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla9) q11|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla10) q12|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - (ancilla11) q13|0>──────────────────────────────────────────────────────────────╫──╫─ - ║ ║ - c0_0: 0 ══════════════════════════════════════════════════════════════╩══╬═ - ║ - c0_1: 0 ═════════════════════════════════════════════════════════════════╩═ - - If you do not want the layout to be shown on transpiled circuits (or any - other circuits with a layout set) there is a new boolean kwarg for both - functions, ``with_layout`` (which defaults ``True``), which when set - ``False`` will disable the layout annotation in the output circuits. - -- A new analysis pass ``CountOpsLongest`` was added to retrieve the number - of operations on the longest path of the DAGCircuit. When used it will - add a ``count_ops_longest_path`` key to the property set dictionary. - You can add it to your a passmanager with something like:: - - from qiskit.transpiler.passes import CountOpsLongestPath - from qiskit.transpiler.passes import CxCancellation - from qiskit.transpiler import PassManager - - pm = PassManager() - pm.append(CountOpsLongestPath()) - - and then access the longest path via the property set value with something - like:: - - pm.append( - CxCancellation(), - condition=lambda property_set: property_set[ - 'count_ops_longest_path'] < 5) - - which will set a condition on that pass based on the longest path. - -- Two new functions, ``sech()`` and ``sech_deriv()`` were added to the pulse - library module ``qiskit.pulse.pulse_lib`` for creating an unnormalized - hyperbolic secant ``SamplePulse`` object and an unnormalized hyperbolic - secant derviative ``SamplePulse`` object respectively. - -- A new kwarg option ``vertical_compression`` was added to the - ``QuantumCircuit.draw()`` method and the - ``qiskit.visualization.circuit_drawer()`` function. This option only works - with the ``text`` backend. This option can be set to either ``high``, - ``medium`` (the default), or ``low`` to adjust how much vertical space is - used by the output visualization. - -- A new kwarg boolean option ``idle_wires`` was added to the - ``QuantumCircuit.draw()`` method and the - ``qiskit.visualization.circuit_drawer()`` function. It works for all drawer - backends. When ``idle_wires`` is set False in a drawer call the drawer will - not draw any bits that do not have any circuit elements in the output - quantum circuit visualization. - -- A new PassManager visualizer function - ``qiskit.visualization.pass_mamanger_drawer()`` was added. This function - takes in a PassManager object and will generate a flow control diagram - of all the passes run in the PassManager. - -- When creating a PassManager you can now specify a callback function that - if specified will be run after each pass is executed. This function gets - passed a set of kwargs on each call with the state of the pass manager after - each pass execution. Currently these kwargs are: - - * ``pass_`` (``Pass``): the pass being run - * ``dag`` (``DAGCircuit``): the dag output of the pass - * ``time`` (``float``): the time to execute the pass - * ``property_set`` (``PropertySet``): the property set - * ``count`` (``int``): the index for the pass execution - - However, it's worth noting that while these arguments are set for the 0.9 - release they expose the internals of the pass manager and are subject to - change in future release. - - For example you can use this to create a callback function that will - visualize the circuit output after each pass is executed:: - - from qiskit.transpiler import PassManager - - def my_callback(**kwargs): - print(kwargs['dag']) - - pm = PassManager(callback=my_callback) - - Additionally you can specify the callback function when using - ``qiskit.compiler.transpile()``:: - - from qiskit.compiler import transpile - - def my_callback(**kwargs): - print(kwargs['pass']) - - transpile(circ, callback=my_callback) - -- A new method ``filter()`` was added to the ``qiskit.pulse.Schedule`` class. - This enables filtering the instructions in a schedule. For example, - filtering by instruction type:: - - from qiskit.pulse import Schedule - from qiskit.pulse.commands import Acquire - from qiskit.pulse.commands import AcquireInstruction - from qiskit.pulse.commands import FrameChange - - sched = Schedule(name='MyExperiment') - sched.insert(0, FrameChange(phase=-1.57)(device)) - sched.insert(60, Acquire(5)) - acquire_sched = sched.filter(instruction_types=[AcquireInstruction]) - -- Additional decomposition methods for several types of gates. These methods - will use different decomposition techniques to break down a gate into - a sequence of CNOTs and single qubit gates. The following methods are - added: - - +--------------------------------+---------------------------------------+ - | Method | Description | - +================================+=======================================+ - | ``QuantumCircuit.iso()`` | Add an arbitrary isometry from m to n | - | | qubits to a circuit. This allows for | - | | attaching arbitrary unitaries on n | - | | qubits (m=n) or to prepare any state | - | | of n qubits (m=0) | - +--------------------------------+---------------------------------------+ - | ``QuantumCircuit.diag_gate()`` | Add a diagonal gate to the circuit | - +--------------------------------+---------------------------------------+ - | ``QuantumCircuit.squ()`` | Decompose an arbitrary 2x2 unitary | - | | into three rotation gates and add to | - | | a circuit | - +--------------------------------+---------------------------------------+ - | ``QuantumCircuit.ucg()`` | Attach an uniformly controlled gate | - | | (also called a multiplexed gate) to a | - | | circuit | - +--------------------------------+---------------------------------------+ - | ``QuantumCircuit.ucx()`` | Attach a uniformly controlled (also | - | | called multiplexed) Rx rotation gate | - | | to a circuit | - +--------------------------------+---------------------------------------+ - | ``QuantumCircuit.ucy()`` | Attach a uniformly controlled (also | - | | called multiplexed) Ry rotation gate | - | | to a circuit | - +--------------------------------+---------------------------------------+ - | ``QuantumCircuit.ucz()`` | Attach a uniformly controlled (also | - | | called multiplexed) Rz rotation gate | - | | to a circuit | - +--------------------------------+---------------------------------------+ - -- Addition of Gray-Synth and Patel–Markov–Hayes algorithms for - synthesis of CNOT-Phase and CNOT-only linear circuits. These functions - allow the synthesis of circuits that consist of only CNOT gates given - a linear function or a circuit that consists of only CNOT and phase gates - given a matrix description. - -- A new function ``random_circuit`` was added to the - ``qiskit.circuit.random`` module. This function will generate a random - circuit of a specified size by randomly selecting different gates and - adding them to the circuit. For example, you can use this to generate a - 5-qubit circuit with a depth of 10 using:: - - from qiskit.circuit.random import random_circuit - - circ = random_circuit(5, 10) - -- A new kwarg ``output_names`` was added to the - ``qiskit.compiler.transpile()`` function. This kwarg takes in a string - or a list of strings and uses those as the value of the circuit name for - the output circuits that get returned by the ``transpile()`` call. For - example:: - - from qiskit.compiler import transpile - my_circs = [circ_a, circ_b] - tcirc_a, tcirc_b = transpile(my_circs, - output_names=['Circuit A', 'Circuit B']) - - the ``name`` attribute on tcirc_a and tcirc_b will be ``'Circuit A'`` and - ``'Circuit B'`` respectively. - -- A new method ``equiv()`` was added to the ``qiskit.quantum_info.Operator`` - and ``qiskit.quantum_info.Statevector`` classes. These methods are used - to check whether a second ``Operator`` object or ``Statevector`` is - equivalent up to global phase. - -- The user config file has several new options: - - * The ``circuit_drawer`` field now accepts an `auto` value. When set as - the value for the ``circuit_drawer`` field the default drawer backend - will be `mpl` if it is available, otherwise the `text` backend will be - used. - * A new field ``circuit_mpl_style`` can be used to set the default style - used by the matplotlib circuit drawer. Valid values for this field are - ``bw`` and ``default`` to set the default to a black and white or the - default color style respectively. - * A new field ``transpile_optimization_level`` can be used to set the - default transpiler optimization level to use for calls to - ``qiskit.compiler.transpile()``. The value can be set to either 0, 1, 2, - or 3. - -- Introduced a new pulse command ``Delay`` which may be inserted into a pulse - ``Schedule``. This command accepts a ``duration`` and may be added to any - ``Channel``. Other commands may not be scheduled on a channel during a delay. - - The delay can be added just like any other pulse command. For example:: - - from qiskit import pulse - - drive_channel = pulse.DriveChannel(0) - delay = pulse.Delay(20) - - sched = pulse.Schedule() - sched += delay(drive_channel) - - -.. _Release Notes_0.9.0_Upgrade Notes: - -Upgrade Notes -------------- - -- The previously deprecated ``qiskit._util`` module has been removed. - ``qiskit.util`` should be used instead. - -- The ``QuantumCircuit.count_ops()`` method now returns an ``OrderedDict`` - object instead of a ``dict``. This should be compatible for most use cases - since ``OrderedDict`` is a ``dict`` subclass. However type checks and - other class checks might need to be updated. - -- The ``DAGCircuit.width()`` method now returns the total number quantum bits - and classical bits. Before it would only return the number of quantum bits. - If you require just the number of quantum bits you can use - ``DAGCircuit.num_qubits()`` instead. - -- The function ``DAGCircuit.num_cbits()`` has been removed. Instead you can - use ``DAGCircuit.num_clbits()``. - -- Individual quantum bits and classical bits are no longer represented as - ``(register, index)`` tuples. They are now instances of `Qubit` and - `Clbit` classes. If you're dealing with individual bits make sure that - you update any usage or type checks to look for these new classes instead - of tuples. - -- The preset passmanager classes - ``qiskit.transpiler.preset_passmanagers.default_pass_manager`` and - ``qiskit.transpiler.preset_passmanagers.default_pass_manager_simulator`` - (which were the previous default pass managers for - ``qiskit.compiler.transpile()`` calls) have been removed. If you were - manually using this pass managers switch to the new default, - ``qiskit.transpile.preset_passmanagers.level1_pass_manager``. - -- The ``LegacySwap`` pass has been removed. If you were using it in a custom - pass manager, it's usage can be replaced by the ``StochasticSwap`` pass, - which is a faster more stable version. All the preset passmanagers have - been updated to use ``StochasticSwap`` pass instead of the ``LegacySwap``. - -- The following deprecated ``qiskit.dagcircuit.DAGCircuit`` methods have been - removed: - - * ``DAGCircuit.get_qubits()`` - Use ``DAGCircuit.qubits()`` instead - * ``DAGCircuit.get_bits()`` - Use ``DAGCircuit.clbits()`` instead - * ``DAGCircuit.qasm()`` - Use a combination of - ``qiskit.converters.dag_to_circuit()`` and ``QuantumCircuit.qasm()``. For - example:: - - from qiskit.dagcircuit import DAGCircuit - from qiskit.converters import dag_to_circuit - my_dag = DAGCircuit() - qasm = dag_to_circuit(my_dag).qasm() - - * ``DAGCircuit.get_op_nodes()`` - Use ``DAGCircuit.op_nodes()`` instead. - Note that the return type is a list of ``DAGNode`` objects for - ``op_nodes()`` instead of the list of tuples previously returned by - ``get_op_nodes()``. - * ``DAGCircuit.get_gate_nodes()`` - Use ``DAGCircuit.gate_nodes()`` - instead. Note that the return type is a list of ``DAGNode`` objects for - ``gate_nodes()`` instead of the list of tuples previously returned by - ``get_gate_nodes()``. - * ``DAGCircuit.get_named_nodes()`` - Use ``DAGCircuit.named_nodes()`` - instead. Note that the return type is a list of ``DAGNode`` objects for - ``named_nodes()`` instead of the list of node_ids previously returned by - ``get_named_nodes()``. - * ``DAGCircuit.get_2q_nodes()`` - Use ``DAGCircuit.twoQ_gates()`` - instead. Note that the return type is a list of ``DAGNode`` objects for - ``twoQ_gates()`` instead of the list of data_dicts previously returned by - ``get_2q_nodes()``. - * ``DAGCircuit.get_3q_or_more_nodes()`` - Use - ``DAGCircuit.threeQ_or_more_gates()`` instead. Note that the return type - is a list of ``DAGNode`` objects for ``threeQ_or_more_gates()`` instead - of the list of tuples previously returned by ``get_3q_or_more_nodes()``. - -- The following ``qiskit.dagcircuit.DAGCircuit`` methods had deprecated - support for accepting a ``node_id`` as a parameter. This has been removed - and now only ``DAGNode`` objects are accepted as input: - - * ``successors()`` - * ``predecessors()`` - * ``ancestors()`` - * ``descendants()`` - * ``bfs_successors()`` - * ``quantum_successors()`` - * ``remove_op_node()`` - * ``remove_ancestors_of()`` - * ``remove_descendants_of()`` - * ``remove_nonancestors_of()`` - * ``remove_nondescendants_of()`` - * ``substitute_node_with_dag()`` - -- The ``qiskit.dagcircuit.DAGCircuit`` method ``rename_register()`` has been - removed. This was unused by all the qiskit code. If you were relying on it - externally you'll have to re-implement is an external function. - -- The ``qiskit.dagcircuit.DAGCircuit`` property ``multi_graph`` has been - removed. Direct access to the underlying ``networkx`` ``multi_graph`` object - isn't supported anymore. The API provided by the ``DAGCircuit`` class should - be used instead. - -- The deprecated exception class ``qiskit.qiskiterror.QiskitError`` has been - removed. Instead you should use ``qiskit.exceptions.QiskitError``. - -- The boolean kwargs, ``ignore_requires`` and ``ignore_preserves`` from - the ``qiskit.transpiler.PassManager`` constructor have been removed. These - are no longer valid options. - -- The module ``qiskit.tools.logging`` has been removed. This module was not - used by anything and added nothing over the interfaces that Python's - standard library ``logging`` module provides. If you want to set a custom - formatter for logging use the standard library ``logging`` module instead. - -- The ``CompositeGate`` class has been removed. Instead you should - directly create a instruction object from a circuit and append that to your - circuit. For example, you can run something like:: - - custom_gate_circ = qiskit.QuantumCircuit(2) - custom_gate_circ.x(1) - custom_gate_circ.h(0) - custom_gate_circ.cx(0, 1) - custom_gate = custom_gate_circ.to_instruction() - -- The previously deprecated kwargs, ``seed`` and ``config`` for - ``qiskit.compiler.assemble()`` have been removed use ``seed_simulator`` and - ``run_config`` respectively instead. - -- The previously deprecated converters - ``qiskit.converters.qobj_to_circuits()`` and - ``qiskit.converters.circuits_to_qobj()`` have been removed. Use - ``qiskit.assembler.disassemble()`` and ``qiskit.compiler.assemble()`` - respectively instead. - -- The previously deprecated kwarg ``seed_mapper`` for - ``qiskit.compiler.transpile()`` has been removed. Instead you should use - ``seed_transpiler`` - -- The previously deprecated kwargs ``seed``, ``seed_mapper``, ``config``, - and ``circuits`` for the ``qiskit.execute()`` function have been removed. - Use ``seed_simulator``, ``seed_transpiler``, ``run_config``, and - ``experiments`` arguments respectively instead. - -- The previously deprecated ``qiskit.tools.qcvv`` module has been removed - use qiskit-ignis instead. - -- The previously deprecated functions ``qiskit.transpiler.transpile()`` and - ``qiskit.transpiler.transpile_dag()`` have been removed. Instead you should - use ``qiskit.compiler.transpile``. If you were using ``transpile_dag()`` - this can be replaced by running:: - - circ = qiskit.converters.dag_to_circuit(dag) - out_circ = qiskit.compiler.transpile(circ) - qiskit.converters.circuit_to_dag(out_circ) - -- The previously deprecated function ``qiskit.compile()`` has been removed - instead you should use ``qiskit.compiler.transpile()`` and - ``qiskit.compiler.assemble()``. - -- The jupyter cell magic ``%%qiskit_progress_bar`` from - ``qiskit.tools.jupyter`` has been changed to a line magic. This was done - to better reflect how the magic is used and how it works. If you were using - the ``%%qiskit_progress_bar`` cell magic in an existing notebook, you will - have to update this to be a line magic by changing it to be - ``%qiskit_progress_bar`` instead. Everything else should behave - identically. - -- The deprecated function ``qiskit.tools.qi.qi.random_unitary_matrix()`` - has been removed. You should use the - ``qiskit.quantum_info.random.random_unitary()`` function instead. - -- The deprecated function ``qiskit.tools.qi.qi.random_density_matrix()`` - has been removed. You should use the - ``qiskit.quantum_info.random.random_density_matrix()`` function - instead. - -- The deprecated function ``qiskit.tools.qi.qi.purity()`` has been removed. - You should the ``qiskit.quantum_info.purity()`` function instead. - -- The deprecated ``QuantumCircuit._attach()`` method has been removed. You - should use ``QuantumCircuit.append()`` instead. - -- The ``qiskit.qasm.Qasm`` method ``get_filename()`` has been removed. - You can use the ``return_filename()`` method instead. - -- The deprecated ``qiskit.mapper`` module has been removed. The list of - functions and classes with their alternatives are: - - * ``qiskit.mapper.CouplingMap``: ``qiskit.transpiler.CouplingMap`` should - be used instead. - * ``qiskit.mapper.Layout``: ``qiskit.transpiler.Layout`` should be used - instead - * ``qiskit.mapper.compiling.euler_angles_1q()``: - ``qiskit.quantum_info.synthesis.euler_angles_1q()`` should be used - instead - * ``qiskit.mapper.compiling.two_qubit_kak()``: - ``qiskit.quantum_info.synthesis.two_qubit_cnot_decompose()`` should be - used instead. - - The deprecated exception classes ``qiskit.mapper.exceptions.CouplingError`` - and ``qiskit.mapper.exceptions.LayoutError`` don't have an alternative - since they serve no purpose without a ``qiskit.mapper`` module. - -- The ``qiskit.pulse.samplers`` module has been moved to - ``qiskit.pulse.pulse_lib.samplers``. You will need to update imports of - ``qiskit.pulse.samplers`` to ``qiskit.pulse.pulse_lib.samplers``. - -- `seaborn`_ is now a dependency for the function - ``qiskit.visualization.plot_state_qsphere()``. It is needed to generate - proper angular color maps for the visualization. The - ``qiskit-terra[visualization]`` extras install target has been updated to - install ``seaborn>=0.9.0`` If you are using visualizations and specifically - the ``plot_state_qsphere()`` function you can use that to install - ``seaborn`` or just manually run ``pip install seaborn>=0.9.0`` - - .. _seaborn: https://seaborn.pydata.org/ - -- The previously deprecated functions ``qiksit.visualization.plot_state`` and - ``qiskit.visualization.iplot_state`` have been removed. Instead you should - use the specific function for each plot type. You can refer to the - following tables to map the deprecated functions to their equivalent new - ones: - - ================================== ======================== - Qiskit Terra 0.6 Qiskit Terra 0.7+ - ================================== ======================== - plot_state(rho) plot_state_city(rho) - plot_state(rho, method='city') plot_state_city(rho) - plot_state(rho, method='paulivec') plot_state_paulivec(rho) - plot_state(rho, method='qsphere') plot_state_qsphere(rho) - plot_state(rho, method='bloch') plot_bloch_multivector(rho) - plot_state(rho, method='hinton') plot_state_hinton(rho) - ================================== ======================== - -- The ``pylatexenc`` and ``pillow`` dependencies for the ``latex`` and - ``latex_source`` circuit drawer backends are no longer listed as - requirements. If you are going to use the latex circuit drawers ensure - you have both packages installed or use the setuptools extras to install - it along with qiskit-terra:: - - pip install qiskit-terra[visualization] - -- The root of the ``qiskit`` namespace will now emit a warning on import if - either ``qiskit.IBMQ`` or ``qiskit.Aer`` could not be setup. This will - occur whenever anything in the ``qiskit`` namespace is imported. These - warnings were added to make it clear for users up front if they're running - qiskit and the qiskit-aer and qiskit-ibmq-provider packages could not be - found. It's not always clear if the packages are missing or python - packaging/pip installed an element incorrectly until you go to use them and - get an empty ``ImportError``. These warnings should make it clear up front - if there these commonly used aliases are missing. - - However, for users that choose not to use either qiskit-aer or - qiskit-ibmq-provider this might cause additional noise. For these users - these warnings are easily suppressable using Python's standard library - ``warnings``. Users can suppress the warnings by putting these two lines - before any imports from qiskit:: - - import warnings - warnings.filterwarnings('ignore', category=RuntimeWarning, - module='qiskit') - - This will suppress the warnings emitted by not having qiskit-aer or - qiskit-ibmq-provider installed, but still preserve any other warnings - emitted by qiskit or any other package. - - -.. _Release Notes_0.9.0_Deprecation Notes: - -Deprecation Notes ------------------ - -- The ``U`` and ``CX`` gates have been deprecated. If you're using these gates - in your code you should update them to use ``u3`` and ``cx`` instead. For - example, if you're using the circuit gate functions ``circuit.u_base()`` - and ``circuit.cx_base()`` you should update these to be ``circuit.u3()`` and - ``circuit.cx()`` respectively. - -- The ``u0`` gate has been deprecated in favor of using multiple ``iden`` - gates and it will be removed in the future. If you're using the ``u0`` gate - in your circuit you should update your calls to use ``iden``. For example, - f you were using ``circuit.u0(2)`` in your circuit before that should be - updated to be:: - - circuit.iden() - circuit.iden() - - instead. - -- The ``qiskit.pulse.DeviceSpecification`` class is deprecated now. Instead - you should use ``qiskit.pulse.PulseChannelSpec``. - -- Accessing a ``qiskit.circuit.Qubit``, ``qiskit.circuit.Clbit``, or - ``qiskit.circuit.Bit`` class by index is deprecated (for compatibility - with the ``(register, index)`` tuples that these classes replaced). - Instead you should use the ``register`` and ``index`` attributes. - -- Passing in a bit to the ``qiskit.QuantumCircuit`` method ``append`` as - a tuple ``(register, index)`` is deprecated. Instead bit objects should - be used directly. - -- Accessing the elements of a ``qiskit.transpiler.Layout`` object with a - tuple ``(register, index)`` is deprecated. Instead a bit object should - be used directly. - -- The ``qiskit.transpiler.Layout`` constructor method - ``qiskit.transpiler.Layout.from_tuplelist()`` is deprecated. Instead the - constructor ``qiskit.transpiler.Layout.from_qubit_list()`` should be used. - -- The module ``qiskit.pulse.ops`` has been deprecated. All the functions it - provided: - - * ``union`` - * ``flatten`` - * ``shift`` - * ``insert`` - * ``append`` - - have equivalent methods available directly on the ``qiskit.pulse.Schedule`` - and ``qiskit.pulse.Instruction`` classes. Those methods should be used - instead. - -- The ``qiskit.qasm.Qasm`` method ``get_tokens()`` is deprecated. Instead - you should use the ``generate_tokens()`` method. - -- The ``qiskit.qasm.qasmparser.QasmParser`` method ``get_tokens()`` is - deprecated. Instead you should use the ``read_tokens()`` method. - -- The ``as_dict()`` method for the Qobj class has been deprecated and will - be removed in the future. You should replace calls to it with ``to_dict()`` - instead. - - -.. _Release Notes_0.9.0_Bug Fixes: - -Bug Fixes ---------- - -- The definition of the ``CU3Gate`` has been changed to be equivalent to the - canonical definition of a controlled ``U3Gate``. - -- The handling of layout in the pass manager has been standardized. This - fixes several reported issues with handling layout. The ``initial_layout`` - kwarg parameter on ``qiskit.compiler.transpile()`` and - ``qiskit.execute()`` will now lay out your qubits from the circuit onto - the desired qubits on the device when transpiling circuits. - -- Support for n-qubit unitaries was added to the BasicAer simulator and - ``unitary`` (arbitrary unitary gates) was added to the set of basis gates - for the simulators - -- The ``qiskit.visualization.plost_state_qsphere()`` has been updated to fix - several issues with it. Now output Q Sphere visualization will be correctly - generated and the following aspects have been updated: - - * All complementary basis states are antipodal - * Phase is indicated by color of line and marker on sphere's surface - * Probability is indicated by translucency of line and volume of marker on - sphere's surface - - -.. _Release Notes_0.9.0_Other Notes: - -Other Notes ------------ - -- The default PassManager for ``qiskit.compiler.transpile()`` and - ``qiskit.execute()`` has been changed to optimization level 1 pass manager - defined at ``qiskit.transpile.preset_passmanagers.level1_pass_manager``. - -- All the circuit drawer backends now will express gate parameters in a - circuit as common fractions of pi in the output visualization. If the value - of a parameter can be expressed as a fraction of pi that will be used - instead of the numeric equivalent. - -- When using ``qiskit.assembler.assemble_schedules()`` if you do not provide - the number of memory_slots to use the number will be inferred based on the - number of acquisitions in the input schedules. - -- The deprecation warning on the ``qiskit.dagcircuit.DAGCircuit`` property - ``node_counter`` has been removed. The behavior change being warned about - was put into effect when the warning was added, so warning that it had - changed served no purpose. - -- Calls to ``PassManager.run()`` now will emit python logging messages at the - INFO level for each pass execution. These messages will include the Pass - name and the total execution time of the pass. Python's standard logging - was used because it allows Qiskit-Terra's logging to integrate in a standard - way with other applications and libraries. All logging for the transpiler - occurs under the ``qiskit.transpiler`` namespace, as used by - ``logging.getLogger('qiskit.transpiler``). For example, to turn on DEBUG - level logging for the transpiler you can run:: - - import logging - - logging.basicConfig() - logging.getLogger('qiskit.transpiler').setLevel(logging.DEBUG) - - which will set the log level for the transpiler to DEBUG and configure - those messages to be printed to stderr. - -Aer 0.3 -======= -- There's a new high-performance Density Matrix Simulator that can be used in - conjunction with our noise models, to better simulate real world scenarios. -- We have added a Matrix Product State (MPS) simulator. MPS allows for - efficient simulation of several classes of quantum circuits, even under - presence of strong correlations and highly entangled states. For cases - amenable to MPS, circuits with several hundred qubits and more can be exactly - simulated, e.g., for the purpose of obtaining expectation values of observables. -- Snapshots can be performed in all of our simulators. -- Now we can measure sampling circuits with read-out errors too, not only ideal - circuits. -- We have increased some circuit optimizations with noise presence. -- A better 2-qubit error approximations have been included. -- Included some tools for making certain noisy simulations easier to craft and - faster to simulate. -- Increased performance with simulations that require less floating point - numerical precision. - -Ignis 0.2 -========= - -New Features ------------- - -- `Logging Module `_ -- `Purity RB `_ -- `Interleaved RB `_ -- `Repetition Code for Verification `_ -- Seed values can now be arbitrarily added to RB (not just in order) -- Support for adding multiple results to measurement mitigation -- RB Fitters now support providing guess values - -Bug Fixes ---------- - -- Fixed a bug in RB fit error -- Fixed a bug in the characterization fitter when selecting a qubit index to - fit - -Other Notes ------------ - -- Measurement mitigation now operates in parallel when applied to multiple - results -- Guess values for RB fitters are improved - -Aqua 0.6 -======== - -Added ------ - -- Relative-Phase Toffoli gates ``rccx`` (with 2 controls) and ``rcccx`` - (with 3 controls). -- Variational form ``RYCRX`` -- A new ``'basic-no-ancilla'`` mode to ``mct``. -- Multi-controlled rotation gates ``mcrx``, ``mcry``, and ``mcrz`` as a general - ``u3`` gate is not supported by graycode implementation -- Chemistry: ROHF open-shell support - - * Supported for all drivers: Gaussian16, PyQuante, PySCF and PSI4 - * HartreeFock initial state, UCCSD variational form and two qubit reduction for - parity mapping now support different alpha and beta particle numbers for open - shell support - -- Chemistry: UHF open-shell support - - * Supported for all drivers: Gaussian16, PyQuante, PySCF and PSI4 - * QMolecule extended to include integrals, coefficients etc for separate beta - -- Chemistry: QMolecule extended with integrals in atomic orbital basis to - facilitate common access to these for experimentation - - * Supported for all drivers: Gaussian16, PyQuante, PySCF and PSI4 - -- Chemistry: Additional PyQuante and PySCF driver configuration - - * Convergence tolerance and max convergence iteration controls. - * For PySCF initial guess choice - -- Chemistry: Processing output added to debug log from PyQuante and PySCF - computations (Gaussian16 and PSI4 outputs were already added to debug log) -- Chemistry: Merged qiskit-chemistry into qiskit-aqua -- Add ``MatrixOperator``, ``WeightedPauliOperator`` and - ``TPBGroupedPauliOperator`` class. -- Add ``evolution_instruction`` function to get registerless instruction of - time evolution. -- Add ``op_converter`` module to unify the place in charge of converting - different types of operators. -- Add ``Z2Symmetries`` class to encapsulate the Z2 symmetries info and has - helper methods for tapering an Operator. -- Amplitude Estimation: added maximum likelihood postprocessing and confidence - interval computation. -- Maximum Likelihood Amplitude Estimation (MLAE): Implemented new algorithm for - amplitude estimation based on maximum likelihood estimation, which reduces - number of required qubits and circuit depth. -- Added (piecewise) linearly and polynomially controlled Pauli-rotation - circuits. -- Add ``q_equation_of_motion`` to study excited state of a molecule, and add - two algorithms to prepare the reference state. - -Changed -------- - -- Improve ``mct``'s ``'basic'`` mode by using relative-phase Toffoli gates to - build intermediate results. -- Adapt to Qiskit Terra's newly introduced ``Qubit`` class. -- Prevent ``QPE/IQPE`` from modifying input ``Operator`` objects. -- The PyEDA dependency was removed; - corresponding oracles' underlying logic operations are now handled by SymPy. -- Refactor the ``Operator`` class, each representation has its own class - ``MatrixOperator``, ``WeightedPauliOperator`` and ``TPBGroupedPauliOperator``. -- The ``power`` in ``evolution_instruction`` was applied on the theta on the - CRZ gate directly, the new version repeats the circuits to implement power. -- CircuitCache is OFF by default, and it can be set via environment variable now - ``QISKIT_AQUA_CIRCUIT_CACHE``. - -Bug Fixes ---------- - -- A bug where ``TruthTableOracle`` would build incorrect circuits for truth - tables with only a single ``1`` value. -- A bug caused by ``PyEDA``'s indeterminism. -- A bug with ``QPE/IQPE``'s translation and stretch computation. -- Chemistry: Bravyi-Kitaev mapping fixed when num qubits was not a power of 2 -- Setup ``initial_layout`` in ``QuantumInstance`` via a list. - -Removed -------- - -- General multi-controlled rotation gate ``mcu3`` is removed and replaced by - multi-controlled rotation gates ``mcrx``, ``mcry``, and ``mcrz`` - -Deprecated ----------- -- The ``Operator`` class is deprecated, in favor of using ``MatrixOperator``, - ``WeightedPauliOperator`` and ``TPBGroupedPauliOperator``. - - -IBM Q Provider 0.3 -================== - -No change - - -############# -Qiskit 0.11.1 -############# - -We have bumped up Qiskit micro version to 0.11.1 because IBM Q Provider has -bumped its micro version as well. - -Terra 0.8 -========= - -No Change - -Aer 0.2 -======= - -No change - -Ignis 0.1 -========= - -No Change - -Aqua 0.5 -======== - -``qiskit-aqua`` has been updated to ``0.5.3`` to fix code related to -changes in how gates inverses are done. - -IBM Q Provider 0.3 -================== - -The ``IBMQProvider`` has been updated to version ``0.3.1`` to fix -backward compatibility issues and work with the default 10 job -limit in single calls to the IBM Q API v2. - - -########### -Qiskit 0.11 -########### - -We have bumped up Qiskit minor version to 0.11 because IBM Q Provider has bumped up -its minor version too. -On Aer, we have jumped from 0.2.1 to 0.2.3 because there was an issue detected -right after releasing 0.2.2 and before Qiskit 0.11 went online. - -Terra 0.8 -========= - -No Change - -Aer 0.2 -======= - -New features ------------- - -- Added support for multi-controlled phase gates -- Added optimized anti-diagonal single-qubit gates - -Improvements ------------- - -- Introduced a technique called Fusion that increments performance of circuit execution - Tuned threading strategy to gain performance in most common scenarios. -- Some of the already implemented error models have been polished. - - - -Ignis 0.1 -========= - -No Change - -Aqua 0.5 -======== - -No Change - -IBM Q Provider 0.3 -================== - -The ``IBMQProvider`` has been updated in order to default to use the new -`IBM Q Experience v2 `__. Accessing the legacy IBM Q Experience v1 and QConsole -will still be supported during the 0.3.x line until its final deprecation one -month from the release. It is encouraged to update to the new IBM Q -Experience to take advantage of the new functionality and features. - -Updating to the new IBM Q Experience v2 ---------------------------------------- - -If you have credentials for the legacy IBM Q Experience stored on disk, you -can make use of the interactive helper:: - - from qiskit import IBMQ - - IBMQ.update_account() - - -For more complex cases or fine tuning your configuration, the following methods -are available: - -* the ``IBMQ.delete_accounts()`` can be used for resetting your configuration - file. -* the ``IBMQ.save_account('MY_TOKEN')`` method can be used for saving your - credentials, following the instructions in the `IBM Q Experience v2 `__ - account page. - -Updating your programs ----------------------- - -When using the new IBM Q Experience v2 through the provider, access to backends -is done via individual ``provider`` instances (as opposed to accessing them -directly through the ``qiskit.IBMQ`` object as in previous versions), which -allows for more granular control over the project you are using. - -You can get a reference to the ``providers`` that you have access to using the -``IBMQ.providers()`` and ``IBMQ.get_provider()`` methods:: - - from qiskit import IBMQ - - provider = IBMQ.load_account() - my_providers = IBMQ.providers() - provider_2 = IBMQ.get_provider(hub='A', group='B', project='C') - - -For convenience, ``IBMQ.load_account()`` and ``IBMQ.enable_account()`` will -return a provider for the open access project, which is the default in the new -IBM Q Experience v2. - -For example, the following program in previous versions:: - - from qiskit import IBMQ - - IBMQ.load_accounts() - backend = IBMQ.get_backend('ibmqx4') - backend_2 = IBMQ.get_backend('ibmq_qasm_simulator', hub='HUB2') - -Would be equivalent to the following program in the current version:: - - from qiskit import IBMQ - - provider = IBMQ.load_account() - backend = provider.get_backend('ibmqx4') - provider_2 = IBMQ.get_provider(hub='HUB2') - backend_2 = provider_2.get_backend('ibmq_qasm_simulator') - -You can find more information and details in the `IBM Q Provider documentation `__. - - -########### -Qiskit 0.10 -########### - -Terra 0.8 -========= - -No Change - -Aer 0.2 -======= - -No Change - -Ignis 0.1 -========= - -No Change - -Aqua 0.5 -======== - -No Change - -IBM Q Provider 0.2 -================== - -New Features ------------- - -- The ``IBMQProvider`` supports connecting to the new version of the IBM Q API. - Please note support for this version is still experimental :pull_ibmq-provider:`78`. -- Added support for Circuits through the new API :pull_ibmq-provider:`79`. - - -Bug Fixes ---------- - -- Fixed incorrect parsing of some API hub URLs :pull_ibmq-provider:`77`. -- Fixed noise model handling for remote simulators :pull_ibmq-provider:`84`. - - -########## -Qiskit 0.9 -########## - -Terra 0.8 -========= - - - -Highlights ----------- - -- Introduction of the Pulse module under ``qiskit.pulse``, which includes - tools for building pulse commands, scheduling them on pulse channels, - visualization, and running them on IBM Q devices. -- Improved QuantumCircuit and Instruction classes, allowing for the - composition of arbitrary sub-circuits into larger circuits, and also - for creating parameterized circuits. -- A powerful Quantum Info module under ``qiskit.quantum_info``, providing - tools to work with operators and channels and to use them inside circuits. -- New transpiler optimization passes and access to predefined transpiling - routines. - - - -New Features ------------- - -- The core ``StochasticSwap`` routine is implemented in `Cython `__. -- Added ``QuantumChannel`` classes for manipulating quantum channels and CPTP - maps. -- Support for parameterized circuits. -- The ``PassManager`` interface has been improved and new functions added for - easier interaction and usage with custom pass managers. -- Preset ``PassManager``\s are now included which offer a predetermined pipeline - of transpiler passes. -- User configuration files to let local environments override default values - for some functions. -- New transpiler passes: ``EnlargeWithAncilla``, ``Unroll2Q``, - ``NoiseAdaptiveLayout``, ``OptimizeSwapBeforeMeasure``, - ``RemoveDiagonalGatesBeforeMeasure``, ``CommutativeCancellation``, - ``Collect2qBlocks``, and ``ConsolidateBlocks``. - - -Compatibility Considerations ----------------------------- - -As part of the 0.8 release the following things have been deprecated and will -either be removed or changed in a backwards incompatible manner in a future -release. While not strictly necessary these are things to adjust for before the -0.9 (unless otherwise noted) release to avoid a breaking change in the future. - -* The methods prefixed by ``_get`` in the ``DAGCircuit`` object are being - renamed without that prefix. -* Changed elements in ``couplinglist`` of ``CouplingMap`` from tuples to lists. -* Unroller bases must now be explicit, and violation raises an informative - ``QiskitError``. -* The ``qiskit.tools.qcvv`` package is deprecated and will be removed in the in - the future. You should migrate to using the Qiskit Ignis which replaces this - module. -* The ``qiskit.compile()`` function is now deprecated in favor of explicitly - using the ``qiskit.compiler.transpile()`` function to transform a circuit, - followed by ``qiskit.compiler.assemble()`` to make a Qobj out of - it. Instead of ``compile(...)``, use ``assemble(transpile(...), ...)``. -* ``qiskit.converters.qobj_to_circuits()`` has been deprecated and will be - removed in a future release. Instead - ``qiskit.assembler.disassemble()`` should be used to extract - ``QuantumCircuit`` objects from a compiled Qobj. -* The ``qiskit.mapper`` namespace has been deprecated. The ``Layout`` and - ``CouplingMap`` classes can be accessed via ``qiskit.transpiler``. -* A few functions in ``qiskit.tools.qi.qi`` have been deprecated and - moved to ``qiskit.quantum_info``. - -Please note that some backwards incompatible changes have been made during this -release. The following notes contain information on how to adapt to these -changes. - -IBM Q Provider -^^^^^^^^^^^^^^ - -The IBM Q provider was previously included in Terra, but it has been split out -into a separate package ``qiskit-ibmq-provider``. This will need to be -installed, either via pypi with ``pip install qiskit-ibmq-provider`` or from -source in order to access ``qiskit.IBMQ`` or ``qiskit.providers.ibmq``. If you -install qiskit with ``pip install qiskit``, that will automatically install -all subpackages of the Qiskit project. - - - -Cython Components -^^^^^^^^^^^^^^^^^ - -Starting in the 0.8 release the core stochastic swap routine is now implemented -in `Cython `__. This was done to significantly improve the performance of the -swapper, however if you build Terra from source or run on a non-x86 or other -platform without prebuilt wheels and install from source distribution you'll -need to make sure that you have Cython installed prior to installing/building -Qiskit Terra. This can easily be done with pip/pypi: ``pip install Cython``. - - - - -Compiler Workflow -^^^^^^^^^^^^^^^^^ - -The ``qiskit.compile()`` function has been deprecated and replaced by first -calling ``qiskit.compiler.transpile()`` to run optimization and mapping on a -circuit, and then ``qiskit.compiler.assemble()`` to build a Qobj from that -optimized circuit to send to a backend. While this is only a deprecation it -will emit a warning if you use the old ``qiskit.compile()`` call. - -**transpile(), assemble(), execute() parameters** - -These functions are heavily overloaded and accept a wide range of inputs. -They can handle circuit and pulse inputs. All kwargs except for ``backend`` -for these functions now also accept lists of the previously accepted types. -The ``initial_layout`` kwarg can now be supplied as a both a list and dictionary, -e.g. to map a Bell experiment on qubits 13 and 14, you can supply: -``initial_layout=[13, 14]`` or ``initial_layout={qr[0]: 13, qr[1]: 14}`` - - - -Qobj -^^^^ - -The Qobj class has been split into two separate subclasses depending on the -use case, either ``PulseQobj`` or ``QasmQobj`` for pulse and circuit jobs -respectively. If you're interacting with Qobj directly you may need to -adjust your usage accordingly. - -The ``qiskit.qobj.qobj_to_dict()`` is removed. Instead use the ``to_dict()`` -method of a Qobj object. - - - -Visualization -^^^^^^^^^^^^^ - -The largest change to the visualization module is it has moved from -``qiskit.tools.visualization`` to ``qiskit.visualization``. This was done to -indicate that the visualization module is more than just a tool. However, since -this interface was declared stable in the 0.7 release the public interface off -of ``qiskit.tools.visualization`` will continue to work. That may change in a -future release, but it will be deprecated prior to removal if that happens. - -The previously deprecated functions, ``plot_circuit()``, -``latex_circuit_drawer()``, ``generate_latex_source()``, and -``matplotlib_circuit_drawer()`` from ``qiskit.tools.visualization`` have been -removed. Instead of these functions, calling -``qiskit.visualization.circuit_drawer()`` with the appropriate arguments should -be used. - -The previously deprecated ``plot_barriers`` and ``reverse_bits`` keys in -the ``style`` kwarg dictionary are deprecated, instead the -``qiskit.visualization.circuit_drawer()`` kwargs ``plot_barriers`` and -``reverse_bits`` should be used. - -The Wigner plotting functions ``plot_wigner_function``, ``plot_wigner_curve``, -``plot_wigner_plaquette``, and ``plot_wigner_data`` previously in the -``qiskit.tools.visualization._state_visualization`` module have been removed. -They were never exposed through the public stable interface and were not well -documented. The code to use this feature can still be accessed through the -qiskit-tutorials repository. - - - -Mapper -^^^^^^ - -The public api from ``qiskit.mapper`` has been moved into ``qiskit.transpiler``. -While it has only been deprecated in this release, it will be removed in the -0.9 release so updating your usage of ``Layout`` and ``CouplingMap`` to import -from ``qiskit.transpiler`` instead of ``qiskit.mapper`` before that takes place -will avoid any surprises in the future. - - - - - - -Aer 0.2 -======= - -New Features ------------- - -- Added multiplexer gate :pull_aer:`192` -- Added ``remap_noise_model`` function to ``noise.utils`` :pull_aer:`181` -- Added ``__eq__`` method to ``NoiseModel``, ``QuantumError``, ``ReadoutError`` - :pull_aer:`181` -- Added support for labelled gates in noise models :pull_aer:`175` -- Added optimized ``mcx``, ``mcy``, ``mcz``, ``mcu1``, ``mcu2``, ``mcu3``, - gates to ``QubitVector`` :pull_aer:`124` -- Added optimized controlled-swap gate to ``QubitVector`` :pull_aer:`142` -- Added gate-fusion optimization for ``QasmController``, which is enabled by - setting ``fusion_enable=true`` :pull_aer:`136` -- Added better management of failed simulations :pull_aer:`167` -- Added qubits truncate optimization for unused qubits :pull_aer:`164` -- Added ability to disable depolarizing error on device noise model - :pull_aer:`131` -- Added initialize simulator instruction to ``statevector_state`` - :pull_aer:`117`, :pull_aer:`137` -- Added coupling maps to simulators :pull_aer:`93` -- Added circuit optimization framework :pull_aer:`83` -- Added benchmarking :pull_aer:`71`, :pull_aer:`177` -- Added wheels support for Debian-like distributions :pull_aer:`69` -- Added autoconfiguration of threads for qasm simulator :pull_aer:`61` -- Added Simulation method based on Stabilizer Rank Decompositions :pull_aer:`51` -- Added ``basis_gates`` kwarg to ``NoiseModel`` init :pull_aer:`175`. -- Added an optional parameter to ``NoiseModel.as_dict()`` for returning - dictionaries that can be serialized using the standard json library directly - :pull_aer:`165` -- Refactor thread management :pull_aer:`50` -- Improve noise transformations :pull_aer:`162` -- Improve error reporting :pull_aer:`160` -- Improve efficiency of parallelization with ``max_memory_mb`` a new parameter - of ``backend_opts`` :pull_aer:`61` -- Improve u1 performance in ``statevector`` :pull_aer:`123` - - -Bug Fixes ---------- - -- Fixed OpenMP clashing problems on macOS for the Terra add-on :pull_aer:`46` - - - - -Compatibility Considerations ----------------------------- - -- Deprecated ``"initial_statevector"`` backend option for ``QasmSimulator`` and - ``StatevectorSimulator`` :pull_aer:`185` -- Renamed ``"chop_threshold"`` backend option to ``"zero_threshold"`` and - changed default value to 1e-10 :pull_aer:`185` - - - -Ignis 0.1 -========= - -New Features ------------- - -* Quantum volume -* Measurement mitigation using tensored calibrations -* Simultaneous RB has the option to align Clifford gates across subsets -* Measurement correction can produce a new calibration for a subset of qubits - - - -Compatibility Considerations ----------------------------- - -* RB writes to the minimal set of classical registers (it used to be - Q[i]->C[i]). This change enables measurement correction with RB. - Unless users had external analysis code, this will not change outcomes. - RB circuits from 0.1 are not compatible with 0.1.1 fitters. - - - - -Aqua 0.5 -======== - -New Features ------------- - -* Implementation of the HHL algorithm supporting ``LinearSystemInput`` -* Pluggable component ``Eigenvalues`` with variant ``EigQPE`` -* Pluggable component ``Reciprocal`` with variants ``LookupRotation`` and - ``LongDivision`` -* Multiple-Controlled U1 and U3 operations ``mcu1`` and ``mcu3`` -* Pluggable component ``QFT`` derived from component ``IQFT`` -* Summarized the transpiled circuits at the DEBUG logging level -* ``QuantumInstance`` accepts ``basis_gates`` and ``coupling_map`` again. -* Support to use ``cx`` gate for the entanglement in ``RY`` and ``RYRZ`` - variational form (``cz`` is the default choice) -* Support to use arbitrary mixer Hamiltonian in QAOA, allowing use of QAOA - in constrained optimization problems [arXiv:1709.03489] -* Added variational algorithm base class ``VQAlgorithm``, implemented by - ``VQE`` and ``QSVMVariational`` -* Added ``ising/docplex.py`` for automatically generating Ising Hamiltonian - from optimization models of DOcplex -* Added ``'basic-dirty-ancilla``' mode for ``mct`` -* Added ``mcmt`` for Multi-Controlled, Multi-Target gate -* Exposed capabilities to generate circuits from logical AND, OR, DNF - (disjunctive normal forms), and CNF (conjunctive normal forms) formulae -* Added the capability to generate circuits from ESOP (exclusive sum of - products) formulae with optional optimization based on Quine-McCluskey and ExactCover -* Added ``LogicalExpressionOracle`` for generating oracle circuits from - arbitrary Boolean logic expressions (including DIMACS support) with optional - optimization capability -* Added ``TruthTableOracle`` for generating oracle circuits from truth-tables - with optional optimization capability -* Added ``CustomCircuitOracle`` for generating oracle from user specified - circuits -* Added implementation of the Deutsch-Jozsa algorithm -* Added implementation of the Bernstein-Vazirani algorithm -* Added implementation of the Simon's algorithm -* Added implementation of the Shor's algorithm -* Added optional capability for Grover's algorithm to take a custom - initial state (as opposed to the default uniform superposition) -* Added capability to create a ``Custom`` initial state using existing - circuit -* Added the ADAM (and AMSGRAD) optimization algorithm -* Multivariate distributions added, so uncertainty models now have univariate - and multivariate distribution components -* Added option to include or skip the swaps operations for qft and iqft - circuit constructions -* Added classical linear system solver ``ExactLSsolver`` -* Added parameters ``auto_hermitian`` and ``auto_resize`` to ``HHL`` algorithm - to support non-Hermitian and non :math:`2^n` sized matrices by default -* Added another feature map, ``RawFeatureVector``, that directly maps feature - vectors to qubits' states for classification -* ``SVM_Classical`` can now load models trained by ``QSVM`` - - - -Bug Fixes ---------- - -* Fixed ``ising/docplex.py`` to correctly multiply constant values in constraints -* Fixed package setup to correctly identify namespace packages using - ``setuptools.find_namespace_packages`` - - - -Compatibility Considerations ----------------------------- - -* ``QuantumInstance`` does not take ``memory`` anymore. -* Moved command line and GUI to separate repo - (``qiskit_aqua_uis``) -* Removed the ``SAT``-specific oracle (now supported by - ``LogicalExpressionOracle``) -* Changed ``advanced`` mode implementation of ``mct``: using simple ``h`` gates - instead of ``ch``, and fixing the old recursion step in ``_multicx`` -* Components ``random_distributions`` renamed to ``uncertainty_models`` -* Reorganized the constructions of various common gates (``ch``, ``cry``, - ``mcry``, ``mct``, ``mcu1``, ``mcu3``, ``mcmt``, ``logic_and``, and - ``logic_or``) and circuits (``PhaseEstimationCircuit``, - ``BooleanLogicCircuits``, ``FourierTransformCircuits``, - and ``StateVectorCircuits``) under the ``circuits`` directory -* Renamed the algorithm ``QSVMVariational`` to ``VQC``, which stands for - Variational Quantum Classifier -* Renamed the algorithm ``QSVMKernel`` to ``QSVM`` -* Renamed the class ``SVMInput`` to ``ClassificationInput`` -* Renamed problem type ``'svm_classification'`` to ``'classification'`` -* Changed the type of ``entangler_map`` used in ``FeatureMap`` and - ``VariationalForm`` to list of lists - - - -IBM Q Provider 0.1 -================== - -New Features ------------- - -- This is the first release as a standalone package. If you - are installing Terra standalone you'll also need to install the - ``qiskit-ibmq-provider`` package with ``pip install qiskit-ibmq-provider`` if - you want to use the IBM Q backends. - -- Support for non-Qobj format jobs has been removed from - the provider. You'll have to convert submissions in an older format to - Qobj before you can submit. - - - -########## -Qiskit 0.8 -########## - -In Qiskit 0.8 we introduced the Qiskit Ignis element. It also includes the -Qiskit Terra element 0.7.1 release which contains a bug fix for the BasicAer -Python simulator. - -Terra 0.7 -========= - -No Change - -Aer 0.1 -======= - -No Change - -Ignis 0.1 -========= - -This is the first release of Qiskit Ignis. - - - -########## -Qiskit 0.7 -########## - -In Qiskit 0.7 we introduced Qiskit Aer and combined it with Qiskit Terra. - - - -Terra 0.7 -========= - -New Features ------------- - -This release includes several new features and many bug fixes. With this release -the interfaces for circuit diagram, histogram, bloch vectors, and state -visualizations are declared stable. Additionally, this release includes a -defined and standardized bit order/endianness throughout all aspects of Qiskit. -These are all declared as stable interfaces in this release which won't have -breaking changes made moving forward, unless there is appropriate and lengthy -deprecation periods warning of any coming changes. - -There is also the introduction of the following new features: - -- A new ASCII art circuit drawing output mode -- A new circuit drawing interface off of ``QuantumCircuit`` objects that - enables calls of ``circuit.draw()`` or ``print(circuit)`` to render a drawing - of circuits -- A visualizer for drawing the DAG representation of a circuit -- A new quantum state plot type for hinton diagrams in the local matplotlib - based state plots -- 2 new constructor methods off the ``QuantumCircuit`` class - ``from_qasm_str()`` and ``from_qasm_file()`` which let you easily create a - circuit object from OpenQASM -- A new function ``plot_bloch_multivector()`` to plot Bloch vectors from a - tensored state vector or density matrix -- Per-shot measurement results are available in simulators and select devices. - These can be accessed by setting the ``memory`` kwarg to ``True`` when - calling ``compile()`` or ``execute()`` and then accessed using the - ``get_memory()`` method on the ``Result`` object. -- A ``qiskit.quantum_info`` module with revamped Pauli objects and methods for - working with quantum states -- New transpile passes for circuit analysis and transformation: - ``CommutationAnalysis``, ``CommutationTransformation``, ``CXCancellation``, - ``Decompose``, ``Unroll``, ``Optimize1QGates``, ``CheckMap``, - ``CXDirection``, ``BarrierBeforeFinalMeasurements`` -- New alternative swap mapper passes in the transpiler: - ``BasicSwap``, ``LookaheadSwap``, ``StochasticSwap`` -- More advanced transpiler infrastructure with support for analysis passes, - transformation passes, a global ``property_set`` for the pass manager, and - repeat-until control of passes - - - -Compatibility Considerations ----------------------------- - -As part of the 0.7 release the following things have been deprecated and will -either be removed or changed in a backwards incompatible manner in a future -release. While not strictly necessary these are things to adjust for before the -next release to avoid a breaking change. - -- ``plot_circuit()``, ``latex_circuit_drawer()``, ``generate_latex_source()``, - and ``matplotlib_circuit_drawer()`` from qiskit.tools.visualization are - deprecated. Instead the ``circuit_drawer()`` function from the same module - should be used, there are kwarg options to mirror the functionality of all - the deprecated functions. -- The current default output of ``circuit_drawer()`` (using latex and falling - back on python) is deprecated and will be changed to just use the ``text`` - output by default in future releases. -- The ``qiskit.wrapper.load_qasm_string()`` and - ``qiskit.wrapper.load_qasm_file()`` functions are deprecated and the - ``QuantumCircuit.from_qasm_str()`` and - ``QuantumCircuit.from_qasm_file()`` constructor methods should be used - instead. -- The ``plot_barriers`` and ``reverse_bits`` keys in the ``style`` kwarg - dictionary are deprecated, instead the - ``qiskit.tools.visualization.circuit_drawer()`` kwargs ``plot_barriers`` and - ``reverse_bits`` should be used instead. -- The functions ``plot_state()`` and ``iplot_state()`` have been depreciated. - Instead the functions ``plot_state_*()`` and ``iplot_state_*()`` should be - called for the visualization method required. -- The ``skip_transpiler`` argument has been deprecated from ``compile()`` and - ``execute()``. Instead you can use the ``PassManager`` directly, just set - the ``pass_manager`` to a blank ``PassManager`` object with ``PassManager()`` -- The ``transpile_dag()`` function ``format`` kwarg for emitting different - output formats is deprecated, instead you should convert the default output - ``DAGCircuit`` object to the desired format. -- The unrollers have been deprecated, moving forward only DAG to DAG unrolling - will be supported. - -Please note that some backwards-incompatible changes have been made during this -release. The following notes contain information on how to adapt to these -changes. - -Changes to Result objects -^^^^^^^^^^^^^^^^^^^^^^^^^ - -As part of the rewrite of the Results object to be more consistent and a -stable interface moving forward a few changes have been made to how you access -the data stored in the result object. First the ``get_data()`` method has been -renamed to just ``data()``. Accompanying that change is a change in the data -format returned by the function. It is now returning the raw data from the -backends instead of doing any post-processing. For example, in previous -versions you could call:: - - result = execute(circuit, backend).result() - unitary = result.get_data()['unitary'] - print(unitary) - -and that would return the unitary matrix like:: - - [[1+0j, 0+0.5j], [0-0.5j][-1+0j]] - -But now if you call (with the renamed method):: - - result.data()['unitary'] - -it will return something like:: - - [[[1, 0], [0, -0.5]], [[0, -0.5], [-1, 0]]] - -To get the post processed results in the same format as before the 0.7 release -you must use the ``get_counts()``, ``get_statevector()``, and ``get_unitary()`` -methods on the result object instead of ``get_data()['counts']``, -``get_data()['statevector']``, and ``get_data()['unitary']`` respectively. - -Additionally, support for ``len()`` and indexing on a ``Result`` object has -been removed. Instead you should deal with the output from the post processed -methods on the Result objects. - -Also, the ``get_snapshot()`` and ``get_snapshots()`` methods from the -``Result`` class have been removed. Instead you can access the snapshots -using ``Result.data()['snapshots']``. - - -Changes to Visualization -^^^^^^^^^^^^^^^^^^^^^^^^ - -The largest change made to visualization in the 0.7 release is the removal of -Matplotlib and other visualization dependencies from the project requirements. -This was done to simplify the requirements and configuration required for -installing Qiskit. If you plan to use any visualizations (including all the -jupyter magics) except for the ``text``, ``latex``, and ``latex_source`` -output for the circuit drawer you'll you must manually ensure that -the visualization dependencies are installed. You can leverage the optional -requirements to the Qiskit Terra package to do this:: - - pip install qiskit-terra[visualization] - -Aside from this there have been changes made to several of the interfaces -as part of the stabilization which may have an impact on existing code. -The first is the ``basis`` kwarg in the ``circuit_drawer()`` function -is no longer accepted. If you were relying on the ``circuit_drawer()`` to -adjust the basis gates used in drawing a circuit diagram you will have to -do this priort to calling ``circuit_drawer()``. For example:: - - from qiskit.tools import visualization - visualization.circuit_drawer(circuit, basis_gates='x,U,CX') - -will have to be adjusted to be:: - - from qiskit import BasicAer - from qiskit import transpiler - from qiskit.tools import visualization - backend = BasicAer.backend('qasm_simulator') - draw_circ = transpiler.transpile(circuit, backend, basis_gates='x,U,CX') - visualization.circuit_drawer(draw_circ) - -Moving forward the ``circuit_drawer()`` function will be the sole interface -for circuit drawing in the visualization module. Prior to the 0.7 release there -were several other functions which either used different output backends or -changed the output for drawing circuits. However, all those other functions -have been deprecated and that functionality has been integrated as options -on ``circuit_drawer()``. - -For the other visualization functions, ``plot_histogram()`` and -``plot_state()`` there are also a few changes to check when upgrading. First -is the output from these functions has changed, in prior releases these would -interactively show the output visualization. However that has changed to -instead return a ``matplotlib.Figure`` object. This provides much more -flexibility and options to interact with the visualization prior to saving or -showing it. This will require adjustment to how these functions are consumed. -For example, prior to this release when calling:: - - plot_histogram(counts) - plot_state(rho) - -would open up new windows (depending on matplotlib backend) to display the -visualization. However starting in the 0.7 you'll have to call ``show()`` on -the output to mirror this behavior. For example:: - - plot_histogram(counts).show() - plot_state(rho).show() - -or:: - - hist_fig = plot_histogram(counts) - state_fig = plot_state(rho) - hist_fig.show() - state_fig.show() - -Note that this is only for when running outside of Jupyter. No adjustment is -required inside a Jupyter environment because Jupyter notebooks natively -understand how to render ``matplotlib.Figure`` objects. - -However, returning the Figure object provides additional flexibility for -dealing with the output. For example instead of just showing the figure you -can now directly save it to a file by leveraging the ``savefig()`` method. -For example:: - - hist_fig = plot_histogram(counts) - state_fig = plot_state(rho) - hist_fig.savefig('histogram.png') - state_fig.savefig('state_plot.png') - -The other key aspect which has changed with these functions is when running -under jupyter. In the 0.6 release ``plot_state()`` and ``plot_histogram()`` -when running under jupyter the default behavior was to use the interactive -Javascript plots if the externally hosted Javascript library for rendering -the visualization was reachable over the network. If not it would just use -the matplotlib version. However in the 0.7 release this no longer the case, -and separate functions for the interactive plots, ``iplot_state()`` and -``iplot_histogram()`` are to be used instead. ``plot_state()`` and -``plot_histogram()`` always use the matplotlib versions. - -Additionally, starting in this release the ``plot_state()`` function is -deprecated in favor of calling individual methods for each method of plotting -a quantum state. While the ``plot_state()`` function will continue to work -until the 0.9 release, it will emit a warning each time it is used. The - -================================== ======================== -Qiskit Terra 0.6 Qiskit Terra 0.7+ -================================== ======================== -plot_state(rho) plot_state_city(rho) -plot_state(rho, method='city') plot_state_city(rho) -plot_state(rho, method='paulivec') plot_state_paulivec(rho) -plot_state(rho, method='qsphere') plot_state_qsphere(rho) -plot_state(rho, method='bloch') plot_bloch_multivector(rho) -plot_state(rho, method='hinton') plot_state_hinton(rho) -================================== ======================== - -The same is true for the interactive JS equivalent, ``iplot_state()``. The -function names are all the same, just with a prepended `i` for each function. -For example, ``iplot_state(rho, method='paulivec')`` is -``iplot_state_paulivec(rho)``. - -Changes to Backends -^^^^^^^^^^^^^^^^^^^ - -With the improvements made in the 0.7 release there are a few things related -to backends to keep in mind when upgrading. The biggest change is the -restructuring of the provider instances in the root ``qiskit``` namespace. -The ``Aer`` provider is not installed by default and requires the installation -of the ``qiskit-aer`` package. This package contains the new high performance -fully featured simulator. If you installed via ``pip install qiskit`` you'll -already have this installed. The python simulators are now available under -``qiskit.BasicAer`` and the old C++ simulators are available with -``qiskit.LegacySimulators``. This also means that the implicit fallback to -python based simulators when the C++ simulators are not found doesn't exist -anymore. If you ask for a local C++ based simulator backend, and it can't be -found an exception will be raised instead of just using the python simulator -instead. - -Additionally the previously deprecation top level functions ``register()`` and -``available_backends()`` have been removed. Also, the deprecated -``backend.parameters()`` and ``backend.calibration()`` methods have been -removed in favor of ``backend.properties()``. You can refer to the 0.6 release -notes section :ref:`backends` for more details on these changes. - -The ``backend.jobs()`` and ``backend.retrieve_jobs()`` calls no longer return -results from those jobs. Instead you must call the ``result()`` method on the -returned jobs objects. - -Changes to the compiler, transpiler, and unrollers -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -As part of an effort to stabilize the compiler interfaces there have been -several changes to be aware of when leveraging the compiler functions. -First it is important to note that the ``qiskit.transpiler.transpile()`` -function now takes a QuantumCircuit object (or a list of them) and returns -a QuantumCircuit object (or a list of them). The DAG processing is done -internally now. - -You can also easily switch between circuits, DAGs, and Qobj now using the -functions in ``qiskit.converters``. - - - - -Aer 0.1 -======= - -New Features ------------- - -Aer provides three simulator backends: - -- ``QasmSimulator``: simulate experiments and return measurement outcomes -- ``StatevectorSimulator``: return the final statevector for a quantum circuit - acting on the all zero state -- ``UnitarySimulator``: return the unitary matrix for a quantum circuit - -``noise`` module: contains advanced noise modeling features for the -``QasmSimulator`` - -- ``NoiseModel``, ``QuantumError``, ``ReadoutError`` classes for simulating a - Qiskit quantum circuit in the presence of errors -- ``errors`` submodule including functions for generating ``QuantumError`` - objects for the following types of quantum errors: Kraus, mixed unitary, - coherent unitary, Pauli, depolarizing, thermal relaxation, amplitude damping, - phase damping, combined phase and amplitude damping -- ``device`` submodule for automatically generating a noise model based on the - ``BackendProperties`` of a device - -``utils`` module: - -- ``qobj_utils`` provides functions for directly modifying a Qobj to insert - special simulator instructions not yet supported through the Qiskit Terra API. - - -Aqua 0.4 -======== - -New Features ------------- - -- Programmatic APIs for algorithms and components -- each component can now be - instantiated and initialized via a single (non-empty) constructor call -- ``QuantumInstance`` API for algorithm/backend decoupling -- - ``QuantumInstance`` encapsulates a backend and its settings -- Updated documentation and Jupyter Notebooks illustrating the new programmatic - APIs -- Transparent parallelization for gradient-based optimizers -- Multiple-Controlled-NOT (cnx) operation -- Pluggable algorithmic component ``RandomDistribution`` -- Concrete implementations of ``RandomDistribution``: - ``BernoulliDistribution``, ``LogNormalDistribution``, - ``MultivariateDistribution``, ``MultivariateNormalDistribution``, - ``MultivariateUniformDistribution``, ``NormalDistribution``, - ``UniformDistribution``, and ``UnivariateDistribution`` -- Concrete implementations of ``UncertaintyProblem``: - ``FixedIncomeExpectedValue``, ``EuropeanCallExpectedValue``, and - ``EuropeanCallDelta`` -- Amplitude Estimation algorithm -- Qiskit Optimization: New Ising models for optimization problems exact cover, - set packing, vertex cover, clique, and graph partition -- Qiskit AI: - - - New feature maps extending the ``FeatureMap`` pluggable interface: - ``PauliExpansion`` and ``PauliZExpansion`` - - Training model serialization/deserialization mechanism - -- Qiskit Finance: - - - Amplitude estimation for Bernoulli random variable: illustration of - amplitude estimation on a single qubit problem - - Loading of multiple univariate and multivariate random distributions - - European call option: expected value and delta (using univariate - distributions) - - Fixed income asset pricing: expected value (using multivariate - distributions) - -- The Pauli string in ``Operator`` class is aligned with Terra 0.7. Now the - order of a n-qubit pauli string is ``q_{n-1}...q{0}`` Thus, the (de)serialier - (``save_to_dict`` and ``load_from_dict``) in the ``Operator`` class are also - changed to adopt the changes of ``Pauli`` class. - -Compatibility Considerations ----------------------------- - -- ``HartreeFock`` component of pluggable type ``InitialState`` moved to Qiskit - Chemistry -- ``UCCSD`` component of pluggable type ``VariationalForm`` moved to Qiskit - Chemistry - - -########## -Qiskit 0.6 -########## - -Terra 0.6 -========= - -Highlights ----------- - -This release includes a redesign of internal components centered around a new, -formal communication format (Qobj), along with long awaited features to -improve the user experience as a whole. The highlights, compared to the 0.5 -release, are: - -- Improvements for inter-operability (based on the Qobj specification) and - extensibility (facilities for extending Qiskit with new backends in a - seamless way) -- New options for handling credentials and authentication for the IBM Q - backends, aimed at simplifying the process and supporting automatic loading - of user credentials -- A revamp of the visualization utilities: stylish interactive visualizations - are now available for Jupyter users, along with refinements for the circuit - drawer (including a matplotlib-based version) -- Performance improvements centered around circuit transpilation: the basis for - a more flexible and modular architecture have been set, including - parallelization of the circuit compilation and numerous optimizations - - -Compatibility Considerations ----------------------------- - -Please note that some backwards-incompatible changes have been introduced -during this release -- the following notes contain information on how to adapt -to the new changes. - -Removal of ``QuantumProgram`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -As hinted during the 0.5 release, the deprecation of the ``QuantumProgram`` -class has now been completed and is no longer available, in favor of working -with the individual components (:class:`~qiskit.backends.basejob.BaseJob`, -:class:`~qiskit._quantumcircuit.QuantumCircuit`, -:class:`~qiskit._classicalregister.ClassicalRegister`, -:class:`~qiskit._quantumregister.QuantumRegister`, -:mod:`~qiskit`) directly. - -Please check the :ref:`0.5 release notes ` and the -examples for details about the transition:: - - - from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister - from qiskit import Aer, execute - - q = QuantumRegister(2) - c = ClassicalRegister(2) - qc = QuantumCircuit(q, c) - - qc.h(q[0]) - qc.cx(q[0], q[1]) - qc.measure(q, c) - - backend = get_backend('qasm_simulator') - - job_sim = execute(qc, backend) - sim_result = job_sim.result() - - print("simulation: ", sim_result) - print(sim_result.get_counts(qc)) - - -IBM Q Authentication and ``Qconfig.py`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The managing of credentials for authenticating when using the IBM Q backends has -been expanded, and there are new options that can be used for convenience: - -1. save your credentials in disk once, and automatically load them in future - sessions. This provides a one-off mechanism:: - - from qiskit import IBMQ - IBMQ.save_account('MY_API_TOKEN', 'MY_API_URL') - - afterwards, your credentials can be automatically loaded from disk by invoking - :meth:`~qiskit.backends.ibmq.ibmqprovider.IBMQ.load_accounts`:: - - from qiskit import IBMQ - IBMQ.load_accounts() - - or you can load only specific accounts if you only want to use those in a session:: - - IBMQ.load_accounts(project='MY_PROJECT') - -2. use environment variables. If ``QE_TOKEN`` and ``QE_URL`` is set, the - ``IBMQ.load_accounts()`` call will automatically load the credentials from - them. - -Additionally, the previous method of having a ``Qconfig.py`` file in the -program folder and passing the credentials explicitly is still supported. - - -.. _backends: - -Working with backends -^^^^^^^^^^^^^^^^^^^^^ - -A new mechanism has been introduced in Terra 0.6 as the recommended way for -obtaining a backend, allowing for more powerful and unified filtering and -integrated with the new credentials system. The previous top-level methods -:meth:`~qiskit.wrapper._wrapper.register`, -:meth:`~qiskit.wrapper._wrapper.available_backends` and -:meth:`~qiskit.wrapper._wrapper.get_backend` are still supported, but will -deprecated in upcoming versions in favor of using the `qiskit.IBMQ` and -`qiskit.Aer` objects directly, which allow for more complex filtering. - -For example, to list and use a local backend:: - - from qiskit import Aer - - all_local_backends = Aer.backends(local=True) # returns a list of instances - qasm_simulator = Aer.backends('qasm_simulator') - -And for listing and using remote backends:: - - from qiskit import IBMQ - - IBMQ.enable_account('MY_API_TOKEN') - 5_qubit_devices = IBMQ.backends(simulator=True, n_qubits=5) - ibmqx4 = IBMQ.get_backend('ibmqx4') - -Please note as well that the names of the local simulators have been -simplified. The previous names can still be used, but it is encouraged to use -the new, shorter names: - -============================= ======================== -Qiskit Terra 0.5 Qiskit Terra 0.6 -============================= ======================== -'local_qasm_simulator' 'qasm_simulator' -'local_statevector_simulator' 'statevector_simulator' -'local_unitary_simulator_py' 'unitary_simulator' -============================= ======================== - - -Backend and Job API changes -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -* Jobs submitted to IBM Q backends have improved capabilities. It is possible - to cancel them and replenish credits (``job.cancel()``), and to retrieve - previous jobs executed on a specific backend either by job id - (``backend.retrieve_job(job_id)``) or in batch of latest jobs - (``backend.jobs(limit)``) - -* Properties for checking each individual job status (``queued``, ``running``, - ``validating``, ``done`` and ``cancelled``) no longer exist. If you - want to check the job status, use the identity comparison against - ``job.status``:: - - from qiskit.backends import JobStatus - - job = execute(circuit, backend) - if job.status() is JobStatus.RUNNING: - handle_job(job) - -Please consult the new documentation of the -:class:`~qiskit.backends.ibmq.ibmqjob.IBMQJob` class to get further insight -in how to use the simplified API. - -* A number of members of :class:`~qiskit.backends.basebackend.BaseBackend` - and :class:`~qiskit.backends.basejob.BaseJob` are no longer properties, - but methods, and as a result they need to be invoked as functions. - - ===================== ======================== - Qiskit Terra 0.5 Qiskit Terra 0.6 - ===================== ======================== - backend.name backend.name() - backend.status backend.status() - backend.configuration backend.configuration() - backend.calibration backend.properties() - backend.parameters backend.jobs() - backend.retrieve_job(job_id) - job.status job.status() - job.cancelled job.queue_position() - job.running job.cancel() - job.queued - job.done - ===================== ======================== - - -Better Jupyter tools -^^^^^^^^^^^^^^^^^^^^ - -The new release contains improvements to the user experience while using -Jupyter notebooks. - -First, new interactive visualizations of counts histograms and quantum states -are provided: -:meth:`~qiskit.tools.visualization.plot_histogram` and -:meth:`~qiskit.tools.visualization.plot_state`. -These methods will default to the new interactive kind when the environment -is Jupyter and internet connection exists. - -Secondly, the new release provides Jupyter cell magics for keeping track of -the progress of your code. Use ``%%qiskit_job_status`` to keep track of the -status of submitted jobs to IBM Q backends. Use ``%%qiskit_progress_bar`` to -keep track of the progress of compilation/execution. - - - -########## -Qiskit 0.5 -########## - -Terra 0.5 -========= - -Highlights ----------- - -This release brings a number of improvements to Qiskit, both for the user -experience and under the hood. Please refer to the full changelog for a -detailed description of the changes - the highlights are: - -* new ``statevector`` :mod:`simulators ` and feature and - performance improvements to the existing ones (in particular to the C++ - simulator), along with a reorganization of how to work with backends focused - on extensibility and flexibility (using aliases and backend providers) -* reorganization of the asynchronous features, providing a friendlier interface - for running jobs asynchronously via :class:`Job` instances -* numerous improvements and fixes throughout the Terra as a whole, both for - convenience of the users (such as allowing anonymous registers) and for - enhanced functionality (such as improved plotting of circuits) - - -Compatibility Considerations ----------------------------- - -Please note that several backwards-incompatible changes have been introduced -during this release as a result of the ongoing development. While some of these -features will continue to be supported during a period of time before being -fully deprecated, it is recommended to update your programs in order to prepare -for the new versions and take advantage of the new functionality. - -.. _quantum-program-0-5: - - -``QuantumProgram`` changes -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Several methods of the :class:`~qiskit.QuantumProgram` class are on their way -to being deprecated: - -* methods for interacting **with the backends and the API**: - - The recommended way for opening a connection to the IBM Q API and for using - the backends is through the - top-level functions directly instead of - the ``QuantumProgram`` methods. In particular, the - :func:`qiskit.register` method provides the equivalent of the previous - :func:`qiskit.QuantumProgram.set_api` call. In a similar vein, there is a new - :func:`qiskit.available_backends`, :func:`qiskit.get_backend` and related - functions for querying the available backends directly. For example, the - following snippet for version 0.4:: - - from qiskit import QuantumProgram - - quantum_program = QuantumProgram() - quantum_program.set_api(token, url) - backends = quantum_program.available_backends() - print(quantum_program.get_backend_status('ibmqx4') - - would be equivalent to the following snippet for version 0.5:: - - from qiskit import register, available_backends, get_backend - - register(token, url) - backends = available_backends() - backend = get_backend('ibmqx4') - print(backend.status) - -* methods for **compiling and executing programs**: - - The top-level functions now also provide - equivalents for the :func:`qiskit.QuantumProgram.compile` and - :func:`qiskit.QuantumProgram.execute` methods. For example, the following - snippet from version 0.4:: - - quantum_program.execute(circuit, args, ...) - - would be equivalent to the following snippet for version 0.5:: - - from qiskit import execute - - execute(circuit, args, ...) - -In general, from version 0.5 onwards we encourage to try to make use of the -individual objects and classes directly instead of relying on -``QuantumProgram``. For example, a :class:`~qiskit.QuantumCircuit` can be -instantiated and constructed by appending :class:`~qiskit.QuantumRegister`, -:class:`~qiskit.ClassicalRegister`, and gates directly. Please check the -update example in the Quickstart section, or the -``using_qiskit_core_level_0.py`` and ``using_qiskit_core_level_1.py`` -examples on the main repository. - -Backend name changes -^^^^^^^^^^^^^^^^^^^^ - -In order to provide a more extensible framework for backends, there have been -some design changes accordingly: - -* **local simulator names** - - The names of the local simulators have been homogenized in order to follow - the same pattern: ``PROVIDERNAME_TYPE_simulator_LANGUAGEORPROJECT`` - - for example, the C++ simulator previously named ``local_qiskit_simulator`` - is now ``local_qasm_simulator_cpp``. An overview of the current - simulators: - - * ``QASM`` simulator is supposed to be like an experiment. You apply a - circuit on some qubits, and observe measurement results - and you repeat - for many shots to get a histogram of counts via ``result.get_counts()``. - * ``Statevector`` simulator is to get the full statevector (:math:`2^n` - amplitudes) after evolving the zero state through the circuit, and can be - obtained via ``result.get_statevector()``. - * ``Unitary`` simulator is to get the unitary matrix equivalent of the - circuit, returned via ``result.get_unitary()``. - * In addition, you can get intermediate states from a simulator by applying - a ``snapshot(slot)`` instruction at various spots in the circuit. This will - save the current state of the simulator in a given slot, which can later - be retrieved via ``result.get_snapshot(slot)``. - -* **backend aliases**: - - The SDK now provides an "alias" system that allows for automatically using - the most performant simulator of a specific type, if it is available in your - system. For example, with the following snippet:: - - from qiskit import get_backend - - backend = get_backend('local_statevector_simulator') - - the backend will be the C++ statevector simulator if available, falling - back to the Python statevector simulator if not present. - -More flexible names and parameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Several functions of the SDK have been made more flexible and user-friendly: - -* **automatic circuit and register names** - - :class:`qiskit.ClassicalRegister`, :class:`qiskit.QuantumRegister` and - :class:`qiskit.QuantumCircuit` can now be instantiated without explicitly - giving them a name - a new autonaming feature will automatically assign them - an identifier:: - - q = QuantumRegister(2) - - Please note as well that the order of the parameters have been swapped - ``QuantumRegister(size, name)``. - -* **methods accepting names or instances** - - In combination with the autonaming changes, several methods such as - :func:`qiskit.Result.get_data` now accept both names and instances for - convenience. For example, when retrieving the results for a job that has a - single circuit such as:: - - qc = QuantumCircuit(..., name='my_circuit') - job = execute(qc, ...) - result = job.result() - - The following calls are equivalent:: - - data = result.get_data('my_circuit') - data = result.get_data(qc) - data = result.get_data() diff --git a/docs/migration_guides/algorithms_migration.rst b/docs/migration_guides/algorithms_migration.rst deleted file mode 100644 index b7b7ccb11380..000000000000 --- a/docs/migration_guides/algorithms_migration.rst +++ /dev/null @@ -1,948 +0,0 @@ -########################## -Algorithms Migration Guide -########################## - -TL;DR -===== - -The :mod:`qiskit.algorithms` module has been fully refactored to use the :mod:`~qiskit.primitives`, for circuit execution, instead of the :class:`~qiskit.utils.QuantumInstance`, which is now deprecated. - -There have been **3 types of refactoring**: - -1. Algorithms refactored in a new location to support :mod:`~qiskit.primitives`. These algorithms have the same - class names as the :class:`~qiskit.utils.QuantumInstance`\-based ones but are in a new sub-package. - - .. attention:: - - **Careful with import paths!!** The legacy algorithms are still importable directly from - :mod:`qiskit.algorithms`. Until the legacy imports are removed, this convenience import is not available - for the refactored algorithms. Thus, to import the refactored algorithms you must always - **specify the full import path** (e.g., ``from qiskit.algorithms.eigensolvers import VQD``) - - - `Minimum Eigensolvers`_ - - `Eigensolvers`_ - - `Time Evolvers`_ - -2. Algorithms refactored in-place (same namespace) to support both :class:`~qiskit.utils.QuantumInstance` and - :mod:`~qiskit.primitives`. In the future, the use of :class:`~qiskit.utils.QuantumInstance` will be removed. - - - `Amplitude Amplifiers`_ - - `Amplitude Estimators`_ - - `Phase Estimators`_ - - -3. Algorithms that were deprecated and are now removed entirely from :mod:`qiskit.algorithms`. These are algorithms that do not currently serve - as building blocks for applications. Their main value is educational, and as such, will be kept as tutorials - in the qiskit textbook. You can consult the tutorials in the following links: - - - `Linear Solvers (HHL) `_ , - - `Factorizers (Shor) `_ - - -The remainder of this migration guide will focus on the algorithms with migration alternatives within -:mod:`qiskit.algorithms`, that is, those under refactoring types 1 and 2. - -Background -========== - -*Back to* `TL;DR`_ - -The :mod:`qiskit.algorithms` module was originally built on top of the :mod:`qiskit.opflow` library and the -:class:`~qiskit.utils.QuantumInstance` utility. The development of the :mod:`~qiskit.primitives` -introduced a higher-level execution paradigm, with the ``Estimator`` for computation of -expectation values for observables, and ``Sampler`` for executing circuits and returning probability -distributions. These tools allowed to refactor the :mod:`qiskit.algorithms` module, and deprecate both -:mod:`qiskit.opflow` and :class:`~qiskit.utils.QuantumInstance`. - -.. attention:: - - The transition away from :mod:`qiskit.opflow` affects the classes that algorithms take as part of the problem - setup. As a rule of thumb, most :mod:`qiskit.opflow` dependencies have a direct :mod:`qiskit.quantum_info` - replacement. One common example is the class :mod:`qiskit.opflow.PauliSumOp`, used to define Hamiltonians - (for example, to plug into VQE), that can be replaced by :mod:`qiskit.quantum_info.SparsePauliOp`. - For information on how to migrate other :mod:`~qiskit.opflow` objects, you can refer to the - `Opflow migration guide `_. - -For further background and detailed migration steps, see the: - -* `Opflow migration guide `_ -* `Quantum Instance migration guide `_ - - -How to choose a primitive configuration for your algorithm -========================================================== - -*Back to* `TL;DR`_ - -The classes in -:mod:`qiskit.algorithms` are initialized with any implementation of :class:`qiskit.primitive.BaseSampler` or class:`qiskit.primitive.BaseEstimator`. - -Once the kind of primitive is known, you can choose between the primitive implementations that better adjust to your case. For example: - - a. For quick prototyping, you can use the **reference implementations of primitives** included in Qiskit: :class:`qiskit.primitives.Sampler` and :class:`qiskit.primitives.Estimator`. - b. For finer algorithm tuning, a local simulator such as the **primitive implementation in Aer**: :class:`qiskit_aer.primitives.Sampler` and :class:`qiskit_aer.primitives.Estimator`. - c. For executing in quantum hardware you can: - - * access services with native primitive implementations, such as **IBM's Qiskit Runtime service** via :class:`qiskit_ibm_runtime.Sampler` and :class:`qiskit_ibm_runtime.Estimator` - * Wrap any backend with **Backend Primitives** (:class:`~qiskit.primitives.BackendSampler` and :class:`~qiskit.primitives.BackendEstimator`). These wrappers implement a primitive interface on top of a backend that only supports ``Backend.run()``. - -For more detailed information and examples, particularly on the use of the **Backend Primitives**, please refer to -the `Quantum Instance migration guide `_. - -In this guide, we will cover 3 different common configurations for algorithms that determine -**which primitive import** you should be selecting: - -1. Running an algorithm with a statevector simulator (i.e., using :mod:`qiskit.opflow`\'s legacy - :class:`.MatrixExpectation`), when you want the ideal outcome without shot noise: - - - Reference Primitives with default configuration (see `QAOA`_ example): - - .. code-block:: python - - from qiskit.primitives import Sampler, Estimator - - - Aer Primitives **with statevector simulator** (see `QAOA`_ example): - - .. code-block:: python - - from qiskit_aer.primitives import Sampler, Estimator - - sampler = Sampler(backend_options={"method": "statevector"}) - estimator = Estimator(backend_options={"method": "statevector"}) - -2. Running an algorithm using a simulator/device with shot noise - (i.e., using :mod:`qiskit.opflow`\'s legacy :class:`.PauliExpectation`): - - - Reference Primitives **with shots** (see `VQE`_ examples): - - .. code-block:: python - - from qiskit.primitives import Sampler, Estimator - - sampler = Sampler(options={"shots": 100}) - estimator = Estimator(options={"shots": 100}) - - # or... - sampler = Sampler() - job = sampler.run(circuits, shots=100) - - estimator = Estimator() - job = estimator.run(circuits, observables, shots=100) - - - Aer Primitives with default configuration (see `VQE`_ examples): - - .. code-block:: python - - from qiskit_aer.primitives import Sampler, Estimator - - - IBM's Qiskit Runtime Primitives with default configuration (see `VQD`_ example): - - .. code-block:: python - - from qiskit_ibm_runtime import Sampler, Estimator - - -3. Running an algorithm on an Aer simulator using a custom instruction (i.e., using :mod:`qiskit.opflow`\'s legacy -:class:`.AerPauliExpectation`): - - - Aer Primitives with ``shots=None``, ``approximation=True`` (see `TrotterQRTE`_ example): - - .. code-block:: python - - from qiskit_aer.primitives import Sampler, Estimator - - sampler = Sampler(run_options={"approximation": True, "shots": None}) - estimator = Estimator(run_options={"approximation": True, "shots": None}) - - -Minimum Eigensolvers -==================== -*Back to* `TL;DR`_ - -The minimum eigensolver algorithms belong to the first type of refactoring listed above -(Algorithms refactored in a new location to support :mod:`~qiskit.primitives`). -Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.minimum_eigensolvers` are now initialized -using an instance of the :mod:`~qiskit.primitives.Sampler` or :mod:`~qiskit.primitives.Estimator` primitive, depending -on the algorithm. The legacy classes can still be found in :mod:`qiskit.algorithms.minimum_eigen_solvers`. - -.. attention:: - - For the :mod:`qiskit.algorithms.minimum_eigensolvers` classes, depending on the import path, - you will access either the primitive-based or the quantum-instance-based - implementation. You have to be extra-careful, because the class name does not change. - - * Old import (Quantum Instance based): ``from qiskit.algorithms import VQE, QAOA, NumPyMinimumEigensolver`` - * New import (Primitives based): ``from qiskit.algorithms.minimum_eigensolvers import VQE, SamplingVQE, QAOA, NumPyMinimumEigensolver`` - -VQE ---- - -The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.VQE` class has now been split according to the use-case: - -- For general-purpose Hamiltonians, you can use the Estimator-based :class:`qiskit.algorithms.minimum_eigensolvers.VQE` - class. -- If you have a diagonal Hamiltonian, and would like the algorithm to return a sampling of the state, you can use - the new Sampler-based :class:`qiskit.algorithms.minimum_eigensolvers.SamplingVQE` algorithm. This could formerly - be realized using the legacy :class:`~qiskit.algorithms.minimum_eigen_solvers.VQE` with - :class:`~qiskit.opflow.expectations.CVaRExpectation`. - -.. note:: - - In addition to taking in an :mod:`~qiskit.primitives.Estimator` instance instead of a :class:`~qiskit.utils.QuantumInstance`, - the new :class:`~qiskit.algorithms.minimum_eigensolvers.VQE` signature has undergone the following changes: - - 1. The ``expectation`` and ``include_custom`` parameters have been removed, as this functionality is now - defined at the ``Estimator`` level. - 2. The ``gradient`` parameter now takes in an instance of a primitive-based gradient class from - :mod:`qiskit.algorithms.gradients` instead of the legacy :mod:`qiskit.opflow.gradients.Gradient` class. - 3. The ``max_evals_grouped`` parameter has been removed, as it can be set directly on the optimizer class. - 4. The ``estimator``, ``ansatz`` and ``optimizer`` are the only parameters that can be defined positionally - (and in this order), all others have become keyword-only arguments. - -.. note:: - - The new :class:`~qiskit.algorithms.minimum_eigensolvers.VQEResult` class does not include the state anymore, as - this output was only useful in the case of diagonal operators. However, if it is available as part of the new - :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE`'s :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQEResult`. - - -.. dropdown:: VQE Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms import VQE - from qiskit.algorithms.optimizers import SPSA - from qiskit.circuit.library import TwoLocal - from qiskit.opflow import PauliSumOp - from qiskit.utils import QuantumInstance - from qiskit_aer import AerSimulator - - ansatz = TwoLocal(2, 'ry', 'cz') - opt = SPSA(maxiter=50) - - # shot-based simulation - backend = AerSimulator() - qi = QuantumInstance(backend=backend, shots=2048, seed_simulator=42) - vqe = VQE(ansatz, optimizer=opt, quantum_instance=qi) - - hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)]) - result = vqe.compute_minimum_eigenvalue(hamiltonian) - - print(result.eigenvalue) - - .. testoutput:: - - (-0.9775390625+0j) - - **[Updated] Using Primitives:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms.minimum_eigensolvers import VQE # new import!!! - from qiskit.algorithms.optimizers import SPSA - from qiskit.circuit.library import TwoLocal - from qiskit.quantum_info import SparsePauliOp - from qiskit.primitives import Estimator - from qiskit_aer.primitives import Estimator as AerEstimator - - ansatz = TwoLocal(2, 'ry', 'cz') - opt = SPSA(maxiter=50) - - # shot-based simulation - estimator = Estimator(options={"shots": 2048}) - vqe = VQE(estimator, ansatz, opt) - - # another option - aer_estimator = AerEstimator(run_options={"shots": 2048, "seed": 42}) - vqe = VQE(aer_estimator, ansatz, opt) - - hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)]) - result = vqe.compute_minimum_eigenvalue(hamiltonian) - - print(result.eigenvalue) - - .. testoutput:: - - -0.986328125 - -.. dropdown:: VQE applying CVaR (SamplingVQE) Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms import VQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.opflow import PauliSumOp, CVaRExpectation - from qiskit.utils import QuantumInstance - from qiskit_aer import AerSimulator - - ansatz = TwoLocal(2, 'ry', 'cz') - opt = SLSQP(maxiter=50) - - # shot-based simulation - backend = AerSimulator() - qi = QuantumInstance(backend=backend, shots=2048) - expectation = CVaRExpectation(alpha=0.2) - vqe = VQE(ansatz, optimizer=opt, expectation=expectation, quantum_instance=qi) - - # diagonal Hamiltonian - hamiltonian = PauliSumOp.from_list([("ZZ",1), ("IZ", -0.5), ("II", 0.12)]) - result = vqe.compute_minimum_eigenvalue(hamiltonian) - - print(result.eigenvalue.real) - - .. testoutput:: - - -1.38 - - **[Updated] Using Primitives:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms.minimum_eigensolvers import SamplingVQE # new import!!! - from qiskit.algorithms.optimizers import SPSA - from qiskit.circuit.library import TwoLocal - from qiskit.quantum_info import SparsePauliOp - from qiskit.primitives import Sampler - from qiskit_aer.primitives import Sampler as AerSampler - - ansatz = TwoLocal(2, 'ry', 'cz') - opt = SPSA(maxiter=50) - - # shot-based simulation - sampler = Sampler(options={"shots": 2048}) - vqe = SamplingVQE(sampler, ansatz, opt, aggregation=0.2) - - # another option - aer_sampler = AerSampler(run_options={"shots": 2048, "seed": 42}) - vqe = SamplingVQE(aer_sampler, ansatz, opt, aggregation=0.2) - - # diagonal Hamiltonian - hamiltonian = SparsePauliOp.from_list([("ZZ",1), ("IZ", -0.5), ("II", 0.12)]) - result = vqe.compute_minimum_eigenvalue(hamiltonian) - - print(result.eigenvalue.real) - - .. testoutput:: - - -1.38 - -For complete code examples, see the following updated tutorials: - -- `VQE Introduction `_ -- `VQE, Callback, Gradients, Initial Point `_ -- `VQE with Aer Primitives `_ - -QAOA ----- - -The legacy :class:`qiskit.algorithms.minimum_eigen_solvers.QAOA` class used to extend -:class:`qiskit.algorithms.minimum_eigen_solvers.VQE`, but now, :class:`qiskit.algorithms.minimum_eigensolvers.QAOA` -extends :class:`qiskit.algorithms.minimum_eigensolvers.SamplingVQE`. -For this reason, **the new QAOA only supports diagonal operators**. - -.. note:: - - In addition to taking in an :mod:`~qiskit.primitives.Sampler` instance instead of a :class:`~qiskit.utils.QuantumInstance`, - the new :class:`~qiskit.algorithms.minimum_eigensolvers.QAOA` signature has undergone the following changes: - - 1. The ``expectation`` and ``include_custom`` parameters have been removed. In return, the ``aggregation`` - parameter has been added (it used to be defined through a custom ``expectation``). - 2. The ``gradient`` parameter now takes in an instance of a primitive-based gradient class from - :mod:`qiskit.algorithms.gradients` instead of the legacy :mod:`qiskit.opflow.gradients.Gradient` class. - 3. The ``max_evals_grouped`` parameter has been removed, as it can be set directly on the optimizer class. - 4. The ``sampler`` and ``optimizer`` are the only parameters that can be defined positionally - (and in this order), all others have become keyword-only arguments. - -.. note:: - - If you want to run QAOA on a non-diagonal operator, you can use the :class:`.QAOAAnsatz` with - :class:`qiskit.algorithms.minimum_eigensolvers.VQE`, but bear in mind there will be no state result. - If your application requires the final probability distribution, you can instantiate a ``Sampler`` - and run it with the optimal circuit after :class:`~qiskit.algorithms.minimum_eigensolvers.VQE`. - -.. dropdown:: QAOA Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms import QAOA - from qiskit.algorithms.optimizers import COBYLA - from qiskit.opflow import PauliSumOp - from qiskit.utils import QuantumInstance - from qiskit_aer import AerSimulator - - # exact statevector simulation - backend = AerSimulator() - qi = QuantumInstance(backend=backend, shots=None, - seed_simulator = 42, seed_transpiler = 42, - backend_options={"method": "statevector"}) - - optimizer = COBYLA() - qaoa = QAOA(optimizer=optimizer, reps=2, quantum_instance=qi) - - # diagonal operator - qubit_op = PauliSumOp.from_list([("ZIII", 1),("IZII", 1), ("IIIZ", 1), ("IIZI", 1)]) - result = qaoa.compute_minimum_eigenvalue(qubit_op) - - print(result.eigenvalue.real) - - .. testoutput:: - - -4.0 - - **[Updated] Using Primitives:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms.minimum_eigensolvers import QAOA - from qiskit.algorithms.optimizers import COBYLA - from qiskit.quantum_info import SparsePauliOp - from qiskit.primitives import Sampler - from qiskit_aer.primitives import Sampler as AerSampler - - # exact statevector simulation - sampler = Sampler() - - # another option - sampler = AerSampler(backend_options={"method": "statevector"}, - run_options={"shots": None, "seed": 42}) - - optimizer = COBYLA() - qaoa = QAOA(sampler, optimizer, reps=2) - - # diagonal operator - qubit_op = SparsePauliOp.from_list([("ZIII", 1),("IZII", 1), ("IIIZ", 1), ("IIZI", 1)]) - result = qaoa.compute_minimum_eigenvalue(qubit_op) - - print(result.eigenvalue) - - .. testoutput:: - - -3.999999832366272 - -For complete code examples, see the following updated tutorials: - -- `QAOA `_ - -NumPyMinimumEigensolver ------------------------ - -Because this is a classical solver, the workflow has not changed between the old and new implementation. -The import has however changed from :class:`qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver` -to :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` to conform to the new interfaces -and result classes. - -.. dropdown:: NumPyMinimumEigensolver Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms import NumPyMinimumEigensolver - from qiskit.opflow import PauliSumOp - - solver = NumPyMinimumEigensolver() - - hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)]) - result = solver.compute_minimum_eigenvalue(hamiltonian) - - print(result.eigenvalue) - - .. testoutput:: - - -1.4142135623730958 - - **[Updated] Using Primitives:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver - from qiskit.quantum_info import SparsePauliOp - - solver = NumPyMinimumEigensolver() - - hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)]) - result = solver.compute_minimum_eigenvalue(hamiltonian) - - print(result.eigenvalue) - - .. testoutput:: - - -1.414213562373095 - -For complete code examples, see the following updated tutorials: - -- `VQE, Callback, Gradients, Initial Point `_ - -Eigensolvers -============ -*Back to* `TL;DR`_ - -The eigensolver algorithms also belong to the first type of refactoring -(Algorithms refactored in a new location to support :mod:`~qiskit.primitives`). Instead of a -:class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.eigensolvers` are now initialized -using an instance of the :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` primitive, or -**a primitive-based subroutine**, depending on the algorithm. The legacy classes can still be found -in :mod:`qiskit.algorithms.eigen_solvers`. - -.. attention:: - - For the :mod:`qiskit.algorithms.eigensolvers` classes, depending on the import path, - you will access either the primitive-based or the quantum-instance-based - implementation. You have to be extra-careful, because the class name does not change. - - * Old import path (Quantum Instance): ``from qiskit.algorithms import VQD, NumPyEigensolver`` - * New import path (Primitives): ``from qiskit.algorithms.eigensolvers import VQD, NumPyEigensolver`` - -VQD ---- - -The new :class:`qiskit.algorithms.eigensolvers.VQD` class is initialized with an instance of the -:class:`~qiskit.primitives.Estimator` primitive instead of a :class:`~qiskit.utils.QuantumInstance`. -In addition to this, it takes an instance of a state fidelity class from mod:`qiskit.algorithms.state_fidelities`, -such as the :class:`~qiskit.primitives.Sampler`-based :class:`~qiskit.algorithms.state_fidelities.ComputeUncompute`. - -.. note:: - - In addition to taking in an :mod:`~qiskit.primitives.Estimator` instance instead of a :class:`~qiskit.utils.QuantumInstance`, - the new :class:`~qiskit.algorithms.eigensolvers.VQD` signature has undergone the following changes: - - 1. The ``expectation`` and ``include_custom`` parameters have been removed, as this functionality is now - defined at the ``Estimator`` level. - 2. The custom ``fidelity`` parameter has been added, and the custom ``gradient`` parameter has - been removed, as current classes in :mod:`qiskit.algorithms.gradients` cannot deal with state fidelity - gradients. - 3. The ``max_evals_grouped`` parameter has been removed, as it can be set directly on the optimizer class. - 4. The ``estimator``, ``fidelity``, ``ansatz`` and ``optimizer`` are the only parameters that can be defined positionally - (and in this order), all others have become keyword-only arguments. - -.. note:: - - Similarly to VQE, the new :class:`~qiskit.algorithms.eigensolvers.VQDResult` class does not include - the state anymore. If your application requires the final probability distribution, you can instantiate - a ``Sampler`` and run it with the optimal circuit for the desired excited state - after running :class:`~qiskit.algorithms.eigensolvers.VQD`. - - -.. dropdown:: VQD Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit import IBMQ - from qiskit.algorithms import VQD - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.opflow import PauliSumOp - from qiskit.utils import QuantumInstance - - ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1) - optimizer = SLSQP(maxiter=10) - hamiltonian = PauliSumOp.from_list([("XXZ", 1), ("XYI", 1)]) - - # example executing in cloud simulator - provider = IBMQ.load_account() - backend = provider.get_backend("ibmq_qasm_simulator") - qi = QuantumInstance(backend=backend) - - vqd = VQD(ansatz, k=3, optimizer=optimizer, quantum_instance=qi) - result = vqd.compute_eigenvalues(operator=hamiltonian) - - print(result.eigenvalues) - - .. testoutput:: - :options: +SKIP - - [ 0.01765114+0.0e+00j -0.58507654+0.0e+00j -0.15003642-2.8e-17j] - - **[Updated] Using Primitives:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit_ibm_runtime import Sampler, Estimator, QiskitRuntimeService, Session - from qiskit.algorithms.eigensolvers import VQD - from qiskit.algorithms.optimizers import SLSQP - from qiskit.algorithms.state_fidelities import ComputeUncompute - from qiskit.circuit.library import TwoLocal - from qiskit.quantum_info import SparsePauliOp - - ansatz = TwoLocal(3, rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1) - optimizer = SLSQP(maxiter=10) - hamiltonian = SparsePauliOp.from_list([("XXZ", 1), ("XYI", 1)]) - - # example executing in cloud simulator - service = QiskitRuntimeService(channel="ibm_quantum") - backend = service.backend("ibmq_qasm_simulator") - - with Session(service=service, backend=backend) as session: - estimator = Estimator() - sampler = Sampler() - fidelity = ComputeUncompute(sampler) - vqd = VQD(estimator, fidelity, ansatz, optimizer, k=3) - result = vqd.compute_eigenvalues(operator=hamiltonian) - - print(result.eigenvalues) - - .. testoutput:: - :options: +SKIP - - [ 0.01765114+0.0e+00j -0.58507654+0.0e+00j -0.15003642-2.8e-17j] - -.. raw:: html - -
- -For complete code examples, see the following updated tutorial: - -- `VQD `_ - -NumPyEigensolver ----------------- -Similarly to its minimum eigensolver counterpart, because this is a classical solver, the workflow has not changed -between the old and new implementation. -The import has however changed from :class:`qiskit.algorithms.eigen_solvers.NumPyEigensolver` -to :class:`qiskit.algorithms.eigensolvers.MinimumEigensolver` to conform to the new interfaces and result classes. - -.. dropdown:: NumPyEigensolver Example - :animate: fade-in-slide-down - - **[Legacy]:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms import NumPyEigensolver - from qiskit.opflow import PauliSumOp - - solver = NumPyEigensolver(k=2) - - hamiltonian = PauliSumOp.from_list([("XX", 1), ("XY", 1)]) - result = solver.compute_eigenvalues(hamiltonian) - - print(result.eigenvalues) - - .. testoutput:: - - [-1.41421356 -1.41421356] - - **[Updated]:** - - .. testsetup:: - - from qiskit.utils import algorithm_globals - algorithm_globals.random_seed = 42 - - .. testcode:: - - from qiskit.algorithms.eigensolvers import NumPyEigensolver - from qiskit.quantum_info import SparsePauliOp - - solver = NumPyEigensolver(k=2) - - hamiltonian = SparsePauliOp.from_list([("XX", 1), ("XY", 1)]) - result = solver.compute_eigenvalues(hamiltonian) - - print(result.eigenvalues) - - .. testoutput:: - - [-1.41421356 -1.41421356] - -Time Evolvers -============= -*Back to* `TL;DR`_ - -The time evolvers are the last group of algorithms to undergo the first type of refactoring -(Algorithms refactored in a new location to support :mod:`~qiskit.primitives`). -Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.time_evolvers` are now initialized -using an instance of the :class:`~qiskit.primitives.Estimator` primitive. The legacy classes can still be found -in :mod:`qiskit.algorithms.evolvers`. - -On top of the migration, the module has been substantially expanded to include **Variational Quantum Time Evolution** -(:class:`~qiskit.algorithms.time_evolvers.VarQTE`\) solvers. - -TrotterQRTE ------------ -.. attention:: - - For the :class:`qiskit.algorithms.time_evolvers.TrotterQRTE` class, depending on the import path, - you will access either the primitive-based or the quantum-instance-based - implementation. You have to be extra-careful, because the class name does not change. - - * Old import path (Quantum Instance): ``from qiskit.algorithms import TrotterQRTE`` - * New import path (Primitives): ``from qiskit.algorithms.time_evolvers import TrotterQRTE`` - -.. note:: - - In addition to taking in an :mod:`~qiskit.primitives.Estimator` instance instead of a :class:`~qiskit.utils.QuantumInstance`, - the new :class:`~qiskit.algorithms.eigensolvers.VQD` signature has undergone the following changes: - - 1. The ``expectation`` parameter has been removed, as this functionality is now - defined at the ``Estimator`` level. - 2. The ``num_timesteps`` parameters has been added, to allow to define the number of steps the full evolution - time is divided into. - -.. dropdown:: TrotterQRTE Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. testcode:: - - from qiskit.algorithms import EvolutionProblem, TrotterQRTE - from qiskit.circuit import QuantumCircuit - from qiskit.opflow import PauliSumOp, AerPauliExpectation - from qiskit.utils import QuantumInstance - from qiskit_aer import AerSimulator - - operator = PauliSumOp.from_list([("X", 1),("Z", 1)]) - initial_state = QuantumCircuit(1) # zero - time = 1 - evolution_problem = EvolutionProblem(operator, 1, initial_state) - - # Aer simulator using custom instruction - backend = AerSimulator() - quantum_instance = QuantumInstance(backend=backend) - expectation = AerPauliExpectation() - - # LieTrotter with 1 rep - trotter_qrte = TrotterQRTE(expectation=expectation, quantum_instance=quantum_instance) - evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state - - print(evolved_state) - - .. testoutput:: - - CircuitStateFn( - ┌─────────────────────┐ - q: ┤ exp(-it (X + Z))(1) ├ - └─────────────────────┘ - ) - - **[Updated] Using Primitives:** - - .. testcode:: - - from qiskit.algorithms.time_evolvers import TimeEvolutionProblem, TrotterQRTE # note new import!!! - from qiskit.circuit import QuantumCircuit - from qiskit.quantum_info import SparsePauliOp - from qiskit_aer.primitives import Estimator as AerEstimator - - operator = SparsePauliOp.from_list([("X", 1),("Z", 1)]) - initial_state = QuantumCircuit(1) # zero - time = 1 - evolution_problem = TimeEvolutionProblem(operator, 1, initial_state) - - # Aer simulator using custom instruction - estimator = AerEstimator(run_options={"approximation": True, "shots": None}) - - # LieTrotter with 1 rep - trotter_qrte = TrotterQRTE(estimator=estimator) - evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state - - print(evolved_state.decompose()) - - .. testoutput:: - - ┌───────────┐┌───────────┐ - q: ┤ exp(it X) ├┤ exp(it Z) ├ - └───────────┘└───────────┘ - -Amplitude Amplifiers -==================== -*Back to* `TL;DR`_ - -The amplitude amplifier algorithms belong to the second type of refactoring (Algorithms refactored in-place). -Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.amplitude_amplifiers` are now initialized -using an instance of any "Sampler" primitive e.g. :mod:`~qiskit.primitives.Sampler`. - -.. note:: - The full :mod:`qiskit.algorithms.amplitude_amplifiers` module has been refactored in place. No need to - change import paths. - -.. dropdown:: Grover Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. code-block:: python - - from qiskit.algorithms import Grover - from qiskit.utils import QuantumInstance - - qi = QuantumInstance(backend=backend) - grover = Grover(quantum_instance=qi) - - - **[Updated] Using Primitives:** - - .. code-block:: python - - from qiskit.algorithms import Grover - from qiskit.primitives import Sampler - - grover = Grover(sampler=Sampler()) - -For complete code examples, see the following updated tutorials: - -- `Amplitude Amplification and Grover `_ -- `Grover Examples `_ - -Amplitude Estimators -==================== -*Back to* `TL;DR`_ - -Similarly to the amplitude amplifiers, the amplitude estimators also belong to the second type of refactoring -(Algorithms refactored in-place). -Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.amplitude_estimators` are now initialized -using an instance of any "Sampler" primitive e.g. :mod:`~qiskit.primitives.Sampler`. - -.. note:: - The full :mod:`qiskit.algorithms.amplitude_estimators` module has been refactored in place. No need to - change import paths. - -.. dropdown:: IAE Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. code-block:: python - - from qiskit.algorithms import IterativeAmplitudeEstimation - from qiskit.utils import QuantumInstance - - qi = QuantumInstance(backend=backend) - iae = IterativeAmplitudeEstimation( - epsilon_target=0.01, # target accuracy - alpha=0.05, # width of the confidence interval - quantum_instance=qi - ) - - **[Updated] Using Primitives:** - - .. code-block:: python - - from qiskit.algorithms import IterativeAmplitudeEstimation - from qiskit.primitives import Sampler - - iae = IterativeAmplitudeEstimation( - epsilon_target=0.01, # target accuracy - alpha=0.05, # width of the confidence interval - sampler=Sampler() - ) - -For complete code examples, see the following updated tutorials: - -- `Amplitude Estimation `_ - -Phase Estimators -================ -*Back to* `TL;DR`_ - -Finally, the phase estimators are the last group of algorithms to undergo the first type of refactoring -(Algorithms refactored in-place). -Instead of a :class:`~qiskit.utils.QuantumInstance`, :mod:`qiskit.algorithms.phase_estimators` are now initialized -using an instance of any "Sampler" primitive e.g. :mod:`~qiskit.primitives.Sampler`. - -.. note:: - The full :mod:`qiskit.algorithms.phase_estimators` module has been refactored in place. No need to - change import paths. - -.. dropdown:: IPE Example - :animate: fade-in-slide-down - - **[Legacy] Using Quantum Instance:** - - .. code-block:: python - - from qiskit.algorithms import IterativePhaseEstimation - from qiskit.utils import QuantumInstance - - qi = QuantumInstance(backend=backend) - ipe = IterativePhaseEstimation( - num_iterations=num_iter, - quantum_instance=qi - ) - - **[Updated] Using Primitives:** - - .. code-block:: python - - from qiskit.algorithms import IterativePhaseEstimation - from qiskit.primitives import Sampler - - ipe = IterativePhaseEstimation( - num_iterations=num_iter, - sampler=Sampler() - ) - -For complete code examples, see the following updated tutorials: - -- `Iterative Phase Estimation `_ - diff --git a/docs/migration_guides/index.rst b/docs/migration_guides/index.rst deleted file mode 100644 index 2c2e1a0c79be..000000000000 --- a/docs/migration_guides/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -####################### -Qiskit Migration Guides -####################### - -.. toctree:: - :maxdepth: 1 - - algorithms_migration - opflow_migration - qi_migration diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst deleted file mode 100644 index 3d0f7fa67d22..000000000000 --- a/docs/migration_guides/opflow_migration.rst +++ /dev/null @@ -1,1674 +0,0 @@ -####################### -Opflow Migration Guide -####################### - -TL;DR -===== -The new :mod:`~qiskit.primitives`, in combination with the :mod:`~qiskit.quantum_info` module, have superseded -functionality of :mod:`~qiskit.opflow`. Thus, the latter is being deprecated. - -In this migration guide, you will find instructions and code examples for how to migrate your code based on -the :mod:`~qiskit.opflow` module to the :mod:`~qiskit.primitives` and :mod:`~qiskit.quantum_info` modules. - -.. note:: - - The use of :mod:`~qiskit.opflow` was tightly coupled to the :class:`~qiskit.utils.QuantumInstance` class, which - is also being deprecated. For more information on migrating the :class:`~qiskit.utils.QuantumInstance`, please - read the `quantum instance migration guide `_. - -.. _attention_primitives: - -.. attention:: - - Most references to the :class:`qiskit.primitives.Sampler` or :class:`qiskit.primitives.Estimator` in this guide - can be replaced with instances of any primitive implementation. For example Aer primitives (:class:`qiskit_aer.primitives.Sampler`/:class:`qiskit_aer.primitives.Estimator`) or IBM's Qiskit Runtime primitives (:class:`qiskit_ibm_runtime.Sampler`/:class:`qiskit_ibm_runtime.Estimator`). - Specific backends can be wrapped with (:class:`qiskit.primitives.BackendSampler`, :class:`qiskit.primitives.BackendEstimator`) to also present primitive-compatible interfaces. - - Certain classes, such as the - :class:`~qiskit.opflow.expectations.AerPauliExpectation`, can only be replaced by a specific primitive instance - (in this case, :class:`qiskit_aer.primitives.Estimator`), or require a specific option configuration. - If this is the case, it will be explicitly indicated in the corresponding section. - - -Background -========== - -The :mod:`~qiskit.opflow` module was originally introduced as a layer between circuits and algorithms, a series of building blocks -for quantum algorithms research and development. - -The recent release of the :mod:`qiskit.primitives` introduced a new paradigm for interacting with backends. Now, instead of -preparing a circuit to execute with a ``backend.run()`` type of method, the algorithms can leverage the :class:`.Sampler` and -:class:`.Estimator` primitives, send parametrized circuits and observables, and directly receive quasi-probability distributions or -expectation values (respectively). This workflow simplifies considerably the pre-processing and post-processing steps -that previously relied on this module; encouraging us to move away from :mod:`~qiskit.opflow` -and find new paths for developing algorithms based on the :mod:`~qiskit.primitives` interface and -the :mod:`~qiskit.quantum_info` module. - -This guide traverses the opflow submodules and provides either a direct alternative -(i.e., using :mod:`~qiskit.quantum_info`), or an explanation of how to replace their functionality in algorithms. - -The functional equivalency can be roughly summarized as follows: - -.. list-table:: - :header-rows: 1 - - * - Opflow Module - - Alternative - * - Operators (:class:`~qiskit.opflow.OperatorBase`, :ref:`operator_globals`, - :mod:`~qiskit.opflow.primitive_ops`, :mod:`~qiskit.opflow.list_ops`) - - ``qiskit.quantum_info`` :ref:`Operators ` - - * - :mod:`qiskit.opflow.state_fns` - - ``qiskit.quantum_info`` :ref:`States ` - - * - :mod:`qiskit.opflow.converters` - - :mod:`qiskit.primitives` - - * - :mod:`qiskit.opflow.evolutions` - - ``qiskit.synthesis`` :ref:`Evolution ` - - * - :mod:`qiskit.opflow.expectations` - - :class:`qiskit.primitives.Estimator` - - * - :mod:`qiskit.opflow.gradients` - - :mod:`qiskit.algorithms.gradients` - -Contents -======== - -This document covers the migration from these opflow submodules: - -**Operators** - -- `Operator Base Class`_ -- `Operator Globals`_ -- `Primitive and List Ops`_ -- `State Functions`_ - -**Converters** - -- `Converters`_ -- `Evolutions`_ -- `Expectations`_ - -**Gradients** - -- `Gradients`_ - - -Operator Base Class -=================== -*Back to* `Contents`_ - -The :class:`qiskit.opflow.OperatorBase` abstract class can be replaced with :class:`qiskit.quantum_info.BaseOperator` , -keeping in mind that :class:`qiskit.quantum_info.BaseOperator` is more generic than its opflow counterpart. - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - * - :class:`qiskit.opflow.OperatorBase` - - :class:`qiskit.quantum_info.BaseOperator` - -.. attention:: - - Despite the similar class names, :class:`qiskit.opflow.OperatorBase` and - :class:`qiskit.quantum_info.BaseOperator` are not completely equivalent to each other, and the transition - should be handled with care. Namely: - - 1. :class:`qiskit.opflow.OperatorBase` implements a broader algebra mixin. Some operator overloads that were - commonly used :mod:`~qiskit.opflow` (for example ``~`` for ``.adjoint()``) are not defined for - :class:`qiskit.quantum_info.BaseOperator`. You might want to check the specific - :mod:`~qiskit.quantum_info` subclass instead. - - 2. :class:`qiskit.opflow.OperatorBase` also implements methods such as ``.to_matrix()`` or ``.to_spmatrix()``, - which are only found in some of the :class:`qiskit.quantum_info.BaseOperator` subclasses. - - See :class:`~qiskit.opflow.OperatorBase` and :class:`~qiskit.quantum_info.BaseOperator` API references - for more information. - - -Operator Globals -================ -*Back to* `Contents`_ - -Opflow provided shortcuts to define common single qubit states, operators, and non-parametrized gates in the -:ref:`operator_globals` module. - -These were mainly used for didactic purposes or quick prototyping, and can easily be replaced by their corresponding -:mod:`~qiskit.quantum_info` class: :class:`~qiskit.quantum_info.Pauli`, :class:`~qiskit.quantum_info.Clifford` or -:class:`~qiskit.quantum_info.Statevector`. - - -1-Qubit Paulis --------------- -*Back to* `Contents`_ - -The 1-qubit paulis were commonly used for quick testing of algorithms, as they could be combined to create more complex operators -(for example, ``0.39 * (I ^ Z) + 0.5 * (X ^ X)``). -These operations implicitly created operators of type :class:`~qiskit.opflow.primitive_ops.PauliSumOp`, and can be replaced by -directly creating a corresponding :class:`~qiskit.quantum_info.SparsePauliOp`, as shown in the examples below. - - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - * - :class:`~qiskit.opflow.X`, :class:`~qiskit.opflow.Y`, :class:`~qiskit.opflow.Z`, :class:`~qiskit.opflow.I` - - :class:`~qiskit.quantum_info.Pauli` - - .. tip:: - - For direct compatibility with classes in :mod:`~qiskit.algorithms`, wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. - - -.. _1_q_pauli: - - -.. dropdown:: Example 1: Defining the XX operator - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import X - - operator = X ^ X - print(repr(operator)) - - .. testoutput:: - - PauliOp(Pauli('XX'), coeff=1.0) - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import Pauli, SparsePauliOp - - operator = Pauli('XX') - - # equivalent to: - X = Pauli('X') - operator = X ^ X - print("As Pauli Op: ", repr(operator)) - - # another alternative is: - operator = SparsePauliOp('XX') - print("As Sparse Pauli Op: ", repr(operator)) - - .. testoutput:: - - As Pauli Op: Pauli('XX') - As Sparse Pauli Op: SparsePauliOp(['XX'], - coeffs=[1.+0.j]) - -.. dropdown:: Example 2: Defining a more complex operator - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import I, X, Z, PauliSumOp - - operator = 0.39 * (I ^ Z ^ I) + 0.5 * (I ^ X ^ X) - - # equivalent to: - operator = PauliSumOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) - - print(repr(operator)) - - .. testoutput:: - - PauliSumOp(SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]), coeff=1.0) - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import SparsePauliOp - - operator = SparsePauliOp(["IZI", "IXX"], coeffs = [0.39, 0.5]) - - # equivalent to: - operator = SparsePauliOp.from_list([("IZI", 0.39), ("IXX", 0.5)]) - - # equivalent to: - operator = SparsePauliOp.from_sparse_list([("Z", [1], 0.39), ("XX", [0,1], 0.5)], num_qubits = 3) - - print(repr(operator)) - - .. testoutput:: - - SparsePauliOp(['IZI', 'IXX'], - coeffs=[0.39+0.j, 0.5 +0.j]) - -Common non-parametrized gates (Clifford) ----------------------------------------- -*Back to* `Contents`_ - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.CX`, :class:`~qiskit.opflow.S`, :class:`~qiskit.opflow.H`, :class:`~qiskit.opflow.T`, - :class:`~qiskit.opflow.CZ`, :class:`~qiskit.opflow.Swap` - - Append corresponding gate to :class:`~qiskit.circuit.QuantumCircuit`. If necessary, - :class:`qiskit.quantum_info.Operator`\s can be directly constructed from quantum circuits. - Another alternative is to wrap the circuit in :class:`~qiskit.quantum_info.Clifford` and call - ``Clifford.to_operator()``. - - .. note:: - - Constructing :mod:`~qiskit.quantum_info` operators from circuits is not efficient, as it is a dense operation and - scales exponentially with the size of the circuit, use with care. - -.. dropdown:: Example 1: Defining the HH operator - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import H - - operator = H ^ H - print(operator) - - .. testoutput:: - - ┌───┐ - q_0: ┤ H ├ - ├───┤ - q_1: ┤ H ├ - └───┘ - - **Alternative** - - .. testcode:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import Clifford, Operator - - qc = QuantumCircuit(2) - qc.h(0) - qc.h(1) - print(qc) - - .. testoutput:: - - ┌───┐ - q_0: ┤ H ├ - ├───┤ - q_1: ┤ H ├ - └───┘ - - If we want to turn this circuit into an operator, we can do: - - .. testcode:: - - operator = Clifford(qc).to_operator() - - # or, directly - operator = Operator(qc) - - print(operator) - - .. testoutput:: - - Operator([[ 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j], - [ 0.5+0.j, -0.5+0.j, 0.5+0.j, -0.5+0.j], - [ 0.5+0.j, 0.5+0.j, -0.5+0.j, -0.5+0.j], - [ 0.5+0.j, -0.5+0.j, -0.5+0.j, 0.5+0.j]], - input_dims=(2, 2), output_dims=(2, 2)) - - -1-Qubit States --------------- -*Back to* `Contents`_ - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.Zero`, :class:`~qiskit.opflow.One`, :class:`~qiskit.opflow.Plus`, :class:`~qiskit.opflow.Minus` - - :class:`~qiskit.quantum_info.Statevector` or simply :class:`~qiskit.circuit.QuantumCircuit`, depending on the use case. - - .. note:: - - For efficient simulation of stabilizer states, :mod:`~qiskit.quantum_info` includes a - :class:`~qiskit.quantum_info.StabilizerState` class. See API reference of :class:`~qiskit.quantum_info.StabilizerState` for more info. - -.. dropdown:: Example 1: Working with stabilizer states - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import Zero, One, Plus, Minus - - # Zero, One, Plus, Minus are all stabilizer states - state1 = Zero ^ One - state2 = Plus ^ Minus - - print("State 1: ", state1) - print("State 2: ", state2) - - .. testoutput:: - - State 1: DictStateFn({'01': 1}) - State 2: CircuitStateFn( - ┌───┐┌───┐ - q_0: ┤ X ├┤ H ├ - ├───┤└───┘ - q_1: ┤ H ├───── - └───┘ - ) - - **Alternative** - - .. testcode:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import StabilizerState, Statevector - - qc_zero = QuantumCircuit(1) - qc_one = qc_zero.copy() - qc_one.x(0) - state1 = Statevector(qc_zero) ^ Statevector(qc_one) - print("State 1: ", state1) - - qc_plus = qc_zero.copy() - qc_plus.h(0) - qc_minus = qc_one.copy() - qc_minus.h(0) - state2 = StabilizerState(qc_plus) ^ StabilizerState(qc_minus) - print("State 2: ", state2) - - .. testoutput:: - - State 1: Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], - dims=(2, 2)) - State 2: StabilizerState(StabilizerTable: ['-IX', '+XI']) - -Primitive and List Ops -====================== -*Back to* `Contents`_ - -Most of the workflows that previously relied on components from :mod:`~qiskit.opflow.primitive_ops` and -:mod:`~qiskit.opflow.list_ops` can now leverage elements from :mod:`~qiskit.quantum_info`\'s -operators instead. -Some of these classes do not require a 1-1 replacement because they were created to interface with other -opflow components. - -Primitive Ops -------------- -*Back to* `Contents`_ - -:class:`~qiskit.opflow.primitive_ops.PrimitiveOp` is the :mod:`~qiskit.opflow.primitive_ops` module's base class. -It also acts as a factory to instantiate a corresponding sub-class depending on the computational primitive used -to initialize it. - -.. tip:: - - Interpreting :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` as a factory class: - - .. list-table:: - :header-rows: 1 - - * - Class passed to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - Subclass returned - - * - :class:`~qiskit.quantum_info.Pauli` - - :class:`~qiskit.opflow.primitive_ops.PauliOp` - - * - :class:`~qiskit.circuit.Instruction`, :class:`~qiskit.circuit.QuantumCircuit` - - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - - * - ``list``, ``np.ndarray``, ``scipy.sparse.spmatrix``, :class:`~qiskit.quantum_info.Operator` - - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - -Thus, when migrating opflow code, it is important to look for alternatives to replace the specific subclasses that -are used "under the hood" in the original code: - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp` - - As mentioned above, this class is used to generate an instance of one of the classes below, so there is - no direct replacement. - - * - :class:`~qiskit.opflow.primitive_ops.CircuitOp` - - :class:`~qiskit.circuit.QuantumCircuit` - - * - :class:`~qiskit.opflow.primitive_ops.MatrixOp` - - :class:`~qiskit.quantum_info.Operator` - - * - :class:`~qiskit.opflow.primitive_ops.PauliOp` - - :class:`~qiskit.quantum_info.Pauli`. For direct compatibility with classes in :mod:`qiskit.algorithms`, - wrap in :class:`~qiskit.quantum_info.SparsePauliOp`. - - * - :class:`~qiskit.opflow.primitive_ops.PauliSumOp` - - :class:`~qiskit.quantum_info.SparsePauliOp`. See example :ref:`below `. - - * - :class:`~qiskit.opflow.primitive_ops.TaperedPauliSumOp` - - This class was used to combine a :class:`.PauliSumOp` with its identified symmetries in one object. - This functionality is not currently used in any workflow, and has been deprecated without replacement. - See :class:`qiskit.quantum_info.analysis.Z2Symmetries` example for updated workflow. - - * - :class:`qiskit.opflow.primitive_ops.Z2Symmetries` - - :class:`qiskit.quantum_info.analysis.Z2Symmetries`. See example :ref:`below `. - -.. _example_pauli_sum_op: - -.. dropdown:: Example 1: ``PauliSumOp`` - :animate: fade-in-slide-down - - - **Opflow** - - .. testcode:: - - from qiskit.opflow import PauliSumOp - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=-3j) - print(repr(qubit_op)) - - .. testoutput:: - - PauliSumOp(SparsePauliOp(['XYZY'], - coeffs=[2.+0.j]), coeff=(-0-3j)) - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import SparsePauliOp, Pauli - - qubit_op = SparsePauliOp(Pauli("XYZY"), coeffs=[-6j]) - print(repr(qubit_op)) - - .. testoutput:: - - SparsePauliOp(['XYZY'], - coeffs=[0.-6.j]) - -.. _example_z2_sym: - -.. dropdown:: Example 2: ``Z2Symmetries`` and ``TaperedPauliSumOp`` - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import PauliSumOp, Z2Symmetries, TaperedPauliSumOp - - qubit_op = PauliSumOp.from_list( - [ - ("II", -1.0537076071291125), - ("IZ", 0.393983679438514), - ("ZI", -0.39398367943851387), - ("ZZ", -0.01123658523318205), - ("XX", 0.1812888082114961), - ] - ) - z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) - print(z2_symmetries) - - tapered_op = z2_symmetries.taper(qubit_op) - print("Tapered Op from Z2 symmetries: ", tapered_op) - - # can be represented as: - tapered_op = TaperedPauliSumOp(qubit_op.primitive, z2_symmetries) - print("Tapered PauliSumOp: ", tapered_op) - - .. testoutput:: - - Z2 symmetries: - Symmetries: - ZZ - Single-Qubit Pauli X: - IX - Cliffords: - 0.7071067811865475 * ZZ - + 0.7071067811865475 * IX - Qubit index: - [0] - Tapering values: - - Possible values: [1], [-1] - Tapered Op from Z2 symmetries: ListOp([ - -1.0649441923622942 * I - + 0.18128880821149604 * X, - -1.0424710218959303 * I - - 0.7879673588770277 * Z - - 0.18128880821149604 * X - ]) - Tapered PauliSumOp: -1.0537076071291125 * II - + 0.393983679438514 * IZ - - 0.39398367943851387 * ZI - - 0.01123658523318205 * ZZ - + 0.1812888082114961 * XX - - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import SparsePauliOp - from qiskit.quantum_info.analysis import Z2Symmetries - - qubit_op = SparsePauliOp.from_list( - [ - ("II", -1.0537076071291125), - ("IZ", 0.393983679438514), - ("ZI", -0.39398367943851387), - ("ZZ", -0.01123658523318205), - ("XX", 0.1812888082114961), - ] - ) - z2_symmetries = Z2Symmetries.find_z2_symmetries(qubit_op) - print(z2_symmetries) - - tapered_op = z2_symmetries.taper(qubit_op) - print("Tapered Op from Z2 symmetries: ", tapered_op) - - .. testoutput:: - - Z2 symmetries: - Symmetries: - ZZ - Single-Qubit Pauli X: - IX - Cliffords: - SparsePauliOp(['ZZ', 'IX'], - coeffs=[0.70710678+0.j, 0.70710678+0.j]) - Qubit index: - [0] - Tapering values: - - Possible values: [1], [-1] - Tapered Op from Z2 symmetries: [SparsePauliOp(['I', 'X'], - coeffs=[-1.06494419+0.j, 0.18128881+0.j]), SparsePauliOp(['I', 'Z', 'X'], - coeffs=[-1.04247102+0.j, -0.78796736+0.j, -0.18128881+0.j])] - -ListOps --------- -*Back to* `Contents`_ - -The :mod:`~qiskit.opflow.list_ops` module contained classes for manipulating lists of :mod:`~qiskit.opflow.primitive_ops` -or :mod:`~qiskit.opflow.state_fns`. The :mod:`~qiskit.quantum_info` alternatives for this functionality are the -:class:`~qiskit.quantum_info.PauliList` and :class:`~qiskit.quantum_info.SparsePauliOp` (for sums of :class:`~qiskit.quantum_info.Pauli`\s). - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.list_ops.ListOp` - - No direct replacement. This is the base class for operator lists. In general, these could be replaced with - Python ``list``\s. For :class:`~qiskit.quantum_info.Pauli` operators, there are a few alternatives, depending on the use-case. - One alternative is :class:`~qiskit.quantum_info.PauliList`. - - * - :class:`~qiskit.opflow.list_ops.ComposedOp` - - No direct replacement. Current workflows do not require composition of states and operators within - one object (no lazy evaluation). - - * - :class:`~qiskit.opflow.list_ops.SummedOp` - - No direct replacement. For :class:`~qiskit.quantum_info.Pauli` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. - - * - :class:`~qiskit.opflow.list_ops.TensoredOp` - - No direct replacement. For :class:`~qiskit.quantum_info.Pauli` operators, use :class:`~qiskit.quantum_info.SparsePauliOp`. - - -State Functions -=============== -*Back to* `Contents`_ - -The :mod:`~qiskit.opflow.state_fns` module can be generally replaced by subclasses of :mod:`~qiskit.quantum_info`\'s -:class:`qiskit.quantum_info.QuantumState`. - -Similarly to :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, :class:`~qiskit.opflow.state_fns.StateFn` -acts as a factory to create the corresponding subclass depending on the computational primitive used to initialize it. - -.. tip:: - - Interpreting :class:`~qiskit.opflow.state_fns.StateFn` as a factory class: - - .. list-table:: - :header-rows: 1 - - * - Class passed to :class:`~qiskit.opflow.state_fns.StateFn` - - Sub-class returned - - * - ``str``, ``dict``, :class:`~qiskit.result.Result` - - :class:`~qiskit.opflow.state_fns.DictStateFn` - - * - ``list``, ``np.ndarray``, :class:`~qiskit.quantum_info.Statevector` - - :class:`~qiskit.opflow.state_fns.VectorStateFn` - - * - :class:`~qiskit.circuit.QuantumCircuit`, :class:`~qiskit.circuit.Instruction` - - :class:`~qiskit.opflow.state_fns.CircuitStateFn` - - * - :class:`~qiskit.opflow.OperatorBase` - - :class:`~qiskit.opflow.state_fns.OperatorStateFn` - -This means that references to :class:`~qiskit.opflow.state_fns.StateFn` in opflow code should be examined to -identify the subclass that is being used, to then look for an alternative. - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.state_fns.StateFn` - - In most cases, :class:`~qiskit.quantum_info.Statevector`. However, please remember that :class:`~qiskit.opflow.state_fns.StateFn` is a factory class. - - * - :class:`~qiskit.opflow.state_fns.CircuitStateFn` - - :class:`~qiskit.quantum_info.Statevector` - - * - :class:`~qiskit.opflow.state_fns.DictStateFn` - - This class was used to store efficient representations of sparse measurement results. The - :class:`~qiskit.primitives.Sampler` now returns the measurements as an instance of - :class:`~qiskit.result.QuasiDistribution` (see example in `Converters`_). - - * - :class:`~qiskit.opflow.state_fns.VectorStateFn` - - This class can be replaced with :class:`~qiskit.quantum_info.Statevector` or - :class:`~qiskit.quantum_info.StabilizerState` (for Clifford-based vectors). - - * - :class:`~qiskit.opflow.state_fns.SparseVectorStateFn` - - No direct replacement. This class was used for sparse statevector representations. - - * - :class:`~qiskit.opflow.state_fns.OperatorStateFn` - - No direct replacement. This class was used to represent measurements against operators. - - * - :class:`~qiskit.opflow.state_fns.CVaRMeasurement` - - Used in :class:`~qiskit.opflow.expectations.CVaRExpectation`. - Functionality now covered by :class:`.SamplingVQE`. See example in `Expectations`_. - - -.. dropdown:: Example 1: Applying an operator to a state - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import StateFn, X, Y - from qiskit import QuantumCircuit - - qc = QuantumCircuit(2) - qc.x(0) - qc.z(1) - op = X ^ Y - state = StateFn(qc) - - comp = ~op @ state - eval = comp.eval() - - print(state) - print(comp) - print(repr(eval)) - - .. testoutput:: - - CircuitStateFn( - ┌───┐ - q_0: ┤ X ├ - ├───┤ - q_1: ┤ Z ├ - └───┘ - ) - CircuitStateFn( - ┌───┐┌────────────┐ - q_0: ┤ X ├┤0 ├ - ├───┤│ Pauli(XY) │ - q_1: ┤ Z ├┤1 ├ - └───┘└────────────┘ - ) - VectorStateFn(Statevector([ 0.0e+00+0.j, 0.0e+00+0.j, -6.1e-17-1.j, 0.0e+00+0.j], - dims=(2, 2)), coeff=1.0, is_measurement=False) - - **Alternative** - - .. testcode:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import SparsePauliOp, Statevector - - qc = QuantumCircuit(2) - qc.x(0) - qc.z(1) - op = SparsePauliOp("XY") - state = Statevector(qc) - - eval = state.evolve(op) - - print(state) - print(eval) - - .. testoutput:: - - Statevector([0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j], - dims=(2, 2)) - Statevector([0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j], - dims=(2, 2)) - -See more applied examples in `Expectations`_ and `Converters`_. - - -Converters -========== - -*Back to* `Contents`_ - -The role of the :class:`qiskit.opflow.converters` submodule was to convert the operators into other opflow operator classes -(:class:`~qiskit.opflow.converters.TwoQubitReduction`, :class:`~qiskit.opflow.converters.PauliBasisChange`...). -In the case of the :class:`~qiskit.opflow.converters.CircuitSampler`, it traversed an operator and outputted -approximations of its state functions using a quantum backend. -Notably, this functionality has been replaced by the :mod:`~qiskit.primitives`. - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.converters.CircuitSampler` - - :class:`~qiskit.primitives.Sampler` or :class:`~qiskit.primitives.Estimator` if used with - :class:`~qiskit.oflow.expectations`. See examples :ref:`below `. - * - :class:`~qiskit.opflow.converters.AbelianGrouper` - - This class allowed a sum a of Pauli operators to be grouped, a similar functionality can be achieved - through the :meth:`~qiskit.quantum_info.SparsePauliOp.group_commuting` method of - :class:`qiskit.quantum_info.SparsePauliOp`, although this is not a 1-1 replacement, as you can see - in the example :ref:`below `. - * - :class:`~qiskit.opflow.converters.DictToCircuitSum` - - No direct replacement. This class was used to convert from :class:`~qiskit.opflow.state_fns.DictStateFn`\s or - :class:`~qiskit.opflow.state_fns.VectorStateFn`\s to equivalent :class:`~qiskit.opflow.state_fns.CircuitStateFn`\s. - * - :class:`~qiskit.opflow.converters.PauliBasisChange` - - No direct replacement. This class was used for changing Paulis into other bases. - * - :class:`~qiskit.opflow.converters.TwoQubitReduction` - - No direct replacement. This class implements a chemistry-specific reduction for the :class:`.ParityMapper` - class in :mod:`qiskit_nature`. - The general symmetry logic this mapper depends on has been refactored to other classes in :mod:`~qiskit.quantum_info`, - so this specific :mod:`~qiskit.opflow` implementation is no longer necessary. - - -.. _example_convert_state: - -.. dropdown:: Example 1: ``CircuitSampler`` for sampling parametrized circuits - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.circuit import QuantumCircuit, Parameter - from qiskit.opflow import ListOp, StateFn, CircuitSampler - from qiskit_aer import AerSimulator - - x, y = Parameter("x"), Parameter("y") - - circuit1 = QuantumCircuit(1) - circuit1.p(0.2, 0) - circuit2 = QuantumCircuit(1) - circuit2.p(x, 0) - circuit3 = QuantumCircuit(1) - circuit3.p(y, 0) - - bindings = {x: -0.4, y: 0.4} - listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) - - sampler = CircuitSampler(AerSimulator()) - sampled = sampler.convert(listop, params=bindings).eval() - - for s in sampled: - print(s) - - .. testoutput:: - - SparseVectorStateFn( (0, 0) 1.0) - SparseVectorStateFn( (0, 0) 1.0) - SparseVectorStateFn( (0, 0) 1.0) - - **Alternative** - - .. testcode:: - - from qiskit.circuit import QuantumCircuit, Parameter - from qiskit.primitives import Sampler - - x, y = Parameter("x"), Parameter("y") - - circuit1 = QuantumCircuit(1) - circuit1.p(0.2, 0) - circuit1.measure_all() # Sampler primitive requires measurement readout - circuit2 = QuantumCircuit(1) - circuit2.p(x, 0) - circuit2.measure_all() - circuit3 = QuantumCircuit(1) - circuit3.p(y, 0) - circuit3.measure_all() - - circuits = [circuit1, circuit2, circuit3] - param_values = [[], [-0.4], [0.4]] - - sampler = Sampler() - sampled = sampler.run(circuits, param_values).result().quasi_dists - - print(sampled) - - .. testoutput:: - - [{0: 1.0}, {0: 1.0}, {0: 1.0}] - - -.. dropdown:: Example 2: ``CircuitSampler`` for computing expectation values - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit import QuantumCircuit - from qiskit.opflow import X, Z, StateFn, CircuitStateFn, CircuitSampler - from qiskit_aer import AerSimulator - - qc = QuantumCircuit(1) - qc.h(0) - state = CircuitStateFn(qc) - hamiltonian = X + Z - - expr = StateFn(hamiltonian, is_measurement=True).compose(state) - backend = AerSimulator(method="statevector") - sampler = CircuitSampler(backend) - expectation = sampler.convert(expr) - expectation_value = expectation.eval().real - - print(expectation_value) - - .. testoutput:: - - 1.0000000000000002 - - **Alternative** - - .. testcode:: - - from qiskit import QuantumCircuit - from qiskit.primitives import Estimator - from qiskit.quantum_info import SparsePauliOp - - state = QuantumCircuit(1) - state.h(0) - hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) - - estimator = Estimator() - expectation_value = estimator.run(state, hamiltonian).result().values.real - - print(expectation_value) - - .. testoutput:: - - [1.] - -.. _example_commuting: - -.. dropdown:: Example 3: ``AbelianGrouper`` for grouping operators - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import PauliSumOp, AbelianGrouper - - op = PauliSumOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) - - grouped_sum = AbelianGrouper.group_subops(op) - - print(repr(grouped_sum)) - - .. testoutput:: - - SummedOp([PauliSumOp(SparsePauliOp(['XX'], - coeffs=[2.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['YY'], - coeffs=[1.+0.j]), coeff=1.0), PauliSumOp(SparsePauliOp(['IZ', 'ZZ'], - coeffs=[0.+2.j, 0.+1.j]), coeff=1.0)], coeff=1.0, abelian=False) - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import SparsePauliOp - - op = SparsePauliOp.from_list([("XX", 2), ("YY", 1), ("IZ",2j), ("ZZ",1j)]) - - grouped = op.group_commuting() - grouped_sum = op.group_commuting(qubit_wise=True) - - print(repr(grouped)) - print(repr(grouped_sum)) - - .. testoutput:: - - [SparsePauliOp(['IZ', 'ZZ'], - coeffs=[0.+2.j, 0.+1.j]), SparsePauliOp(['XX', 'YY'], - coeffs=[2.+0.j, 1.+0.j])] - [SparsePauliOp(['XX'], - coeffs=[2.+0.j]), SparsePauliOp(['YY'], - coeffs=[1.+0.j]), SparsePauliOp(['IZ', 'ZZ'], - coeffs=[0.+2.j, 0.+1.j])] - -Evolutions -========== -*Back to* `Contents`_ - -The :mod:`qiskit.opflow.evolutions` submodule was created to provide building blocks for Hamiltonian simulation algorithms, -including various methods for Trotterization. The original opflow workflow for Hamiltonian simulation did not allow for -delayed synthesis of the gates or efficient transpilation of the circuits, so this functionality was migrated to the -``qiskit.synthesis`` :ref:`Evolution ` module. - -.. note:: - - The :class:`qiskit.opflow.evolutions.PauliTrotterEvolution` class computes evolutions for exponentiated - sums of Paulis by converting to the Z basis, rotating with an RZ, changing back, and Trotterizing. - When calling ``.convert()``, the class follows a recursive strategy that involves creating - :class:`~qiskit.opflow.evolutions.EvolvedOp` placeholders for the operators, - constructing :class:`.PauliEvolutionGate`\s out of the operator primitives, and supplying one of - the desired synthesis methods to perform the Trotterization. The methods can be specified via - ``string``, which is then inputted into a :class:`~qiskit.opflow.evolutions.TrotterizationFactory`, - or by supplying a method instance of :class:`qiskit.opflow.evolutions.Trotter`, - :class:`qiskit.opflow.evolutions.Suzuki` or :class:`qiskit.opflow.evolutions.QDrift`. - - The different Trotterization methods that extend :class:`qiskit.opflow.evolutions.TrotterizationBase` were migrated to - :mod:`qiskit.synthesis`, - and now extend the :class:`qiskit.synthesis.ProductFormula` base class. They no longer contain a ``.convert()`` method for - standalone use, but are now designed to be plugged into the :class:`.PauliEvolutionGate` and called via ``.synthesize()``. - In this context, the job of the :class:`qiskit.opflow.evolutions.PauliTrotterEvolution` class can now be handled directly by the algorithms - (for example, :class:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE`\). - - In a similar manner, the :class:`qiskit.opflow.evolutions.MatrixEvolution` class performs evolution by classical matrix exponentiation, - constructing a circuit with :class:`~.library.UnitaryGate`\s or :class:`~.library.HamiltonianGate`\s containing the exponentiation of the operator. - This class is no longer necessary, as the :class:`~.library.HamiltonianGate`\s can be directly handled by the algorithms. - -Trotterizations ---------------- -*Back to* `Contents`_ - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.evolutions.TrotterizationFactory` - - No direct replacement. This class was used to create instances of one of the classes listed below. - - * - :class:`~qiskit.opflow.evolutions.Trotter` - - :class:`qiskit.synthesis.SuzukiTrotter` or :class:`qiskit.synthesis.LieTrotter` - - * - :class:`~qiskit.opflow.evolutions.Suzuki` - - :class:`qiskit.synthesis.SuzukiTrotter` - - * - :class:`~qiskit.opflow.evolutions.QDrift` - - :class:`qiskit.synthesis.QDrift` - -Other Evolution Classes ------------------------ -*Back to* `Contents`_ - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.evolutions.EvolutionFactory` - - No direct replacement. This class was used to create instances of one of the classes listed below. - - * - :class:`~qiskit.opflow.evolutions.EvolvedOp` - - No direct replacement. The workflow no longer requires a specific operator for evolutions. - - * - :class:`~qiskit.opflow.evolutions.MatrixEvolution` - - :class:`~.library.HamiltonianGate` - - * - :class:`~qiskit.opflow.evolutions.PauliTrotterEvolution` - - :class:`.PauliEvolutionGate` - - -.. dropdown:: Example 1: Trotter evolution - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp - - hamiltonian = PauliSumOp.from_list([('X', 1), ('Z',1)]) - evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=2) - evol_result = evolution.convert(hamiltonian.exp_i()) - evolved_state = evol_result.to_circuit() - - print(evolved_state) - - .. testoutput:: - - ┌─────────────────────┐ - q: ┤ exp(-it (X + Z))(1) ├ - └─────────────────────┘ - - **Alternative** - - .. testcode:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import SparsePauliOp - from qiskit.circuit.library import PauliEvolutionGate - from qiskit.synthesis import SuzukiTrotter - - hamiltonian = SparsePauliOp.from_list([('X', 1), ('Z',1)]) - evol_gate = PauliEvolutionGate(hamiltonian, time=1, synthesis=SuzukiTrotter(reps=2)) - evolved_state = QuantumCircuit(1) - evolved_state.append(evol_gate, [0]) - - print(evolved_state) - - .. testoutput:: - - ┌─────────────────────┐ - q: ┤ exp(-it (X + Z))(1) ├ - └─────────────────────┘ - -.. dropdown:: Example 2: Evolution with time-dependent Hamiltonian - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import Trotter, PauliTrotterEvolution, PauliSumOp - from qiskit.circuit import Parameter - - time = Parameter('t') - hamiltonian = PauliSumOp.from_list([('X', 1), ('Y',1)]) - evolution = PauliTrotterEvolution(trotter_mode=Trotter(), reps=1) - evol_result = evolution.convert((time * hamiltonian).exp_i()) - evolved_state = evol_result.to_circuit() - - print(evolved_state) - - .. testoutput:: - - ┌─────────────────────────┐ - q: ┤ exp(-it (X + Y))(1.0*t) ├ - └─────────────────────────┘ - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import SparsePauliOp - from qiskit.synthesis import LieTrotter - from qiskit.circuit.library import PauliEvolutionGate - from qiskit import QuantumCircuit - from qiskit.circuit import Parameter - - time = Parameter('t') - hamiltonian = SparsePauliOp.from_list([('X', 1), ('Y',1)]) - evol_gate = PauliEvolutionGate(hamiltonian, time=time, synthesis=LieTrotter()) - evolved_state = QuantumCircuit(1) - evolved_state.append(evol_gate, [0]) - - print(evolved_state) - - .. testoutput:: - - ┌─────────────────────┐ - q: ┤ exp(-it (X + Y))(t) ├ - └─────────────────────┘ - - -.. dropdown:: Example 3: Matrix evolution - :animate: fade-in-slide-down - - - **Opflow** - - .. testcode:: - - from qiskit.opflow import MatrixEvolution, MatrixOp - - hamiltonian = MatrixOp([[0, 1], [1, 0]]) - evolution = MatrixEvolution() - evol_result = evolution.convert(hamiltonian.exp_i()) - evolved_state = evol_result.to_circuit() - - print(evolved_state.decompose().decompose()) - - .. testoutput:: - - ┌────────────────┐ - q: ┤ U3(2,-π/2,π/2) ├ - └────────────────┘ - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import SparsePauliOp - from qiskit.extensions import HamiltonianGate - from qiskit import QuantumCircuit - - evol_gate = HamiltonianGate([[0, 1], [1, 0]], 1) - evolved_state = QuantumCircuit(1) - evolved_state.append(evol_gate, [0]) - - print(evolved_state.decompose().decompose()) - - .. testoutput:: - - ┌────────────────┐ - q: ┤ U3(2,-π/2,π/2) ├ - └────────────────┘ - - -Expectations -============ -*Back to* `Contents`_ - -Expectations are converters which enable the computation of the expectation value of an observable with respect to some state function. -This functionality can now be found in the :class:`~qiskit.primitives.Estimator` primitive. Please remember that there -are different ``Estimator`` implementations, as noted :ref:`here ` - -Algorithm-Agnostic Expectations -------------------------------- -*Back to* `Contents`_ - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.expectations.ExpectationFactory` - - No direct replacement. This class was used to create instances of one of the classes listed below. - - * - :class:`~qiskit.opflow.expectations.AerPauliExpectation` - - Use :class:`qiskit_aer.primitives.Estimator` with ``approximation=True``, and then ``shots=None`` as ``run_options``. - See example below. - - * - :class:`~qiskit.opflow.expectations.MatrixExpectation` - - Use :class:`qiskit.primitives.Estimator` primitive (if no shots are set, it performs an exact Statevector calculation). - See example below. - - * - :class:`~qiskit.opflow.expectations.PauliExpectation` - - Use any Estimator primitive (for :class:`qiskit.primitives.Estimator`, set ``shots!=None`` for a shot-based - simulation, for :class:`qiskit_aer.primitives.Estimator` , this is the default). - - -.. _expect_state: - -.. dropdown:: Example 1: Aer Pauli expectation - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import X, Minus, StateFn, AerPauliExpectation, CircuitSampler - from qiskit.utils import QuantumInstance - from qiskit_aer import AerSimulator - - backend = AerSimulator() - q_instance = QuantumInstance(backend) - - sampler = CircuitSampler(q_instance, attach_results=True) - expectation = AerPauliExpectation() - - state = Minus - operator = 1j * X - - converted_meas = expectation.convert(StateFn(operator, is_measurement=True) @ state) - expectation_value = sampler.convert(converted_meas).eval() - - print(expectation_value) - - .. testoutput:: - - -1j - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import SparsePauliOp - from qiskit import QuantumCircuit - from qiskit_aer.primitives import Estimator - - estimator = Estimator(approximation=True, run_options={"shots": None}) - - op = SparsePauliOp.from_list([("X", 1j)]) - states_op = QuantumCircuit(1) - states_op.x(0) - states_op.h(0) - - expectation_value = estimator.run(states_op, op).result().values - - print(expectation_value) - - .. testoutput:: - - [0.-1.j] - - -.. _matrix_state: - -.. dropdown:: Example 2: Matrix expectation - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import X, H, I, MatrixExpectation, ListOp, StateFn - from qiskit.utils import QuantumInstance - from qiskit_aer import AerSimulator - - backend = AerSimulator(method='statevector') - q_instance = QuantumInstance(backend) - sampler = CircuitSampler(q_instance, attach_results=True) - expect = MatrixExpectation() - - mixed_ops = ListOp([X.to_matrix_op(), H]) - converted_meas = expect.convert(~StateFn(mixed_ops)) - - plus_mean = converted_meas @ Plus - values_plus = sampler.convert(plus_mean).eval() - - print(values_plus) - - .. testoutput:: - - [(1+0j), (0.7071067811865476+0j)] - - **Alternative** - - .. testcode:: - - from qiskit.primitives import Estimator - from qiskit.quantum_info import SparsePauliOp - from qiskit.quantum_info import Clifford - - X = SparsePauliOp("X") - - qc = QuantumCircuit(1) - qc.h(0) - H = Clifford(qc).to_operator() - - plus = QuantumCircuit(1) - plus.h(0) - - estimator = Estimator() - values_plus = estimator.run([plus, plus], [X, H]).result().values - - print(values_plus) - - .. testoutput:: - - [1. 0.70710678] - - -CVaRExpectation ---------------- -*Back to* `Contents`_ - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.expectations.CVaRExpectation` - - Functionality migrated into new VQE algorithm: :class:`~qiskit.algorithms.minimum_eigensolvers.SamplingVQE` - -.. _cvar: - -.. dropdown:: Example 1: VQE with CVaR - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.opflow import CVaRExpectation, PauliSumOp - - from qiskit.algorithms import VQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit_aer import AerSimulator - - backend = AerSimulator(method="statevector") - ansatz = TwoLocal(2, 'ry', 'cz') - op = PauliSumOp.from_list([('ZZ',1), ('IZ',1), ('II',1)]) - alpha = 0.2 - cvar_expectation = CVaRExpectation(alpha=alpha) - opt = SLSQP(maxiter=1000) - vqe = VQE(ansatz, expectation=cvar_expectation, optimizer=opt, quantum_instance=backend) - result = vqe.compute_minimum_eigenvalue(op) - - print(result.eigenvalue) - - .. testoutput:: - - (-1+0j) - - **Alternative** - - .. testcode:: - - from qiskit.quantum_info import SparsePauliOp - - from qiskit.algorithms.minimum_eigensolvers import SamplingVQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.circuit.library import TwoLocal - from qiskit.primitives import Sampler - - ansatz = TwoLocal(2, 'ry', 'cz') - op = SparsePauliOp.from_list([('ZZ',1), ('IZ',1), ('II',1)]) - opt = SLSQP(maxiter=1000) - alpha = 0.2 - vqe = SamplingVQE(Sampler(), ansatz, opt, aggregation=alpha) - result = vqe.compute_minimum_eigenvalue(op) - - print(result.eigenvalue) - - .. testoutput:: - - -1.0 - - -Gradients -========= -*Back to* `Contents`_ - -The opflow :mod:`~qiskit.opflow.gradients` framework has been replaced by the new :mod:`qiskit.algorithms.gradients` -module. The new gradients are **primitive-based subroutines** commonly used by algorithms and applications, which -can also be executed in a standalone manner. For this reason, they now reside under :mod:`qiskit.algorithms`. - -The former gradient framework contained base classes, converters and derivatives. The "derivatives" -followed a factory design pattern, where different methods could be provided via string identifiers -to each of these classes. The new gradient framework contains two main families of subroutines: -**Gradients** and **QGT/QFI**. The **Gradients** can either be Sampler or Estimator based, while the current -**QGT/QFI** implementations are Estimator-based. - -This leads to a change in the workflow, where instead of doing: - -.. code-block:: python - - from qiskit.opflow import Gradient - - grad = Gradient(method="param_shift") - - # task based on expectation value computations + gradients - -We now import explicitly the desired class, depending on the target primitive (Sampler/Estimator) and target method: - -.. code-block:: python - - from qiskit.algorithms.gradients import ParamShiftEstimatorGradient - from qiskit.primitives import Estimator - - grad = ParamShiftEstimatorGradient(Estimator()) - - # task based on expectation value computations + gradients - -This works similarly for the QFI class, where instead of doing: - -.. code-block:: python - - from qiskit.opflow import QFI - - qfi = QFI(method="lin_comb_full") - - # task based on expectation value computations + QFI - -You now have a generic QFI implementation that can be initialized with different QGT (Quantum Gradient Tensor) -implementations: - -.. code-block:: python - - from qiskit.algorithms.gradients import LinCombQGT, QFI - from qiskit.primitives import Estimator - - qgt = LinCombQGT(Estimator()) - qfi = QFI(qgt) - - # task based on expectation value computations + QFI - -.. note:: - - Here is a quick guide for migrating the most common gradient settings. Please note that all new gradient - imports follow the format: - - .. code-block:: python - - from qiskit.algorithms.gradients import MethodPrimitiveGradient, QFI - - .. dropdown:: Gradients - :animate: fade-in-slide-down - - .. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - ``Gradient(method="lin_comb")`` - - ``LinCombEstimatorGradient(estimator=estimator)`` or ``LinCombSamplerGradient(sampler=sampler)`` - * - ``Gradient(method="param_shift")`` - - ``ParamShiftEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` - * - ``Gradient(method="fin_diff")`` - - ``FiniteDiffEstimatorGradient(estimator=estimator)`` or ``ParamShiftSamplerGradient(sampler=sampler)`` - - .. dropdown:: QFI/QGT - :animate: fade-in-slide-down - - .. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - ``QFI(method="lin_comb_full")`` - - ``qgt=LinCombQGT(Estimator())`` - ``QFI(qgt=qgt)`` - - -Other auxiliary classes in the legacy gradient framework have now been deprecated. Here is the complete migration -list: - -.. list-table:: - :header-rows: 1 - - * - Opflow - - Alternative - - * - :class:`~qiskit.opflow.gradients.DerivativeBase` - - No replacement. This was the base class for the gradient, hessian and QFI base classes. - * - :class:`.GradientBase` and :class:`~qiskit.opflow.gradients.Gradient` - - :class:`.BaseSamplerGradient` or :class:`.BaseEstimatorGradient`, and specific subclasses per method, - as explained above. - * - :class:`.HessianBase` and :class:`~qiskit.opflow.gradients.Hessian` - - No replacement. The new gradient framework does not work with hessians as independent objects. - * - :class:`.QFIBase` and :class:`~qiskit.opflow.gradients.QFI` - - The new :class:`~qiskit.algorithms.gradients.QFI` class extends :class:`~qiskit.algorithms.gradients.QGT`, so the - corresponding base class is :class:`~qiskit.algorithms.gradients.BaseQGT` - * - :class:`~qiskit.opflow.gradients.CircuitGradient` - - No replacement. This class was used to convert between circuit and gradient - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, and this functionality is no longer necessary. - * - :class:`~qiskit.opflow.gradients.CircuitQFI` - - No replacement. This class was used to convert between circuit and QFI - :class:`~qiskit.opflow.primitive_ops.PrimitiveOp`, and this functionality is no longer necessary. - * - :class:`~qiskit.opflow.gradients.NaturalGradient` - - No replacement. The same functionality can be achieved with the QFI module. - -.. dropdown:: Example 1: Finite Differences Batched Gradient - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.circuit import Parameter, QuantumCircuit - from qiskit.opflow import Gradient, X, Z, StateFn, CircuitStateFn - import numpy as np - - ham = 0.5 * X - 1 * Z - - a = Parameter("a") - b = Parameter("b") - c = Parameter("c") - params = [a,b,c] - - qc = QuantumCircuit(1) - qc.h(0) - qc.u(a, b, c, 0) - qc.h(0) - - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - - # the gradient class acted similarly opflow converters, - # with a .convert() step and an .eval() step - state_grad = Gradient(grad_method="param_shift").convert(operator=op, params=params) - - # the old workflow did not allow for batched evaluation of parameter values - values_dict = [{a: np.pi / 4, b: 0, c: 0}, {a: np.pi / 4, b: np.pi / 4, c: np.pi / 4}] - gradients = [] - for i, value_dict in enumerate(values_dict): - gradients.append(state_grad.assign_parameters(value_dict).eval()) - - print(gradients) - - .. testoutput:: - - [[(0.35355339059327356+0j), (-1.182555756156289e-16+0j), (-1.6675e-16+0j)], [(0.10355339059327384+0j), (0.8535533905932734+0j), (1.103553390593273+0j)]] - - **Alternative** - - .. testcode:: - - from qiskit.circuit import Parameter, QuantumCircuit - from qiskit.primitives import Estimator - from qiskit.algorithms.gradients import ParamShiftEstimatorGradient - from qiskit.quantum_info import SparsePauliOp - import numpy as np - - ham = SparsePauliOp.from_list([("X", 0.5), ("Z", -1)]) - - a = Parameter("a") - b = Parameter("b") - c = Parameter("c") - - qc = QuantumCircuit(1) - qc.h(0) - qc.u(a, b, c, 0) - qc.h(0) - - estimator = Estimator() - gradient = ParamShiftEstimatorGradient(estimator) - - # the new workflow follows an interface close to the primitives' - param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] - - # for batched evaluations, the number of circuits must match the - # number of parameter value sets - gradients = gradient.run([qc] * 2, [ham] * 2, param_list).result().gradients - - print(gradients) - - .. testoutput:: - - [array([ 3.53553391e-01, 0.00000000e+00, -1.80411242e-16]), array([0.10355339, 0.85355339, 1.10355339])] - - -.. dropdown:: Example 2: QFI - :animate: fade-in-slide-down - - **Opflow** - - .. testcode:: - - from qiskit.circuit import Parameter, QuantumCircuit - from qiskit.opflow import QFI, CircuitStateFn - import numpy as np - - # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - # convert the circuit to a QFI object - op = CircuitStateFn(qc) - qfi = QFI(qfi_method="lin_comb_full").convert(operator=op) - - # bind parameters and evaluate - values_dict = {a: np.pi / 4, b: 0.1} - qfi = qfi.bind_parameters(values_dict).eval() - - print(qfi) - - .. testoutput:: - - [[ 1.00000000e+00+0.j -3.63575685e-16+0.j] - [-3.63575685e-16+0.j 5.00000000e-01+0.j]] - - **Alternative** - - .. testcode:: - - from qiskit.circuit import Parameter, QuantumCircuit - from qiskit.primitives import Estimator - from qiskit.algorithms.gradients import LinCombQGT, QFI - import numpy as np - - # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - # initialize QFI - estimator = Estimator() - qgt = LinCombQGT(estimator) - qfi = QFI(qgt) - - # evaluate - values_list = [[np.pi / 4, 0.1]] - qfi = qfi.run(qc, values_list).result().qfis - - print(qfi) - - .. testoutput:: - - [array([[ 1.00000000e+00, -1.50274614e-16], - [-1.50274614e-16, 5.00000000e-01]])] diff --git a/docs/migration_guides/qi_migration.rst b/docs/migration_guides/qi_migration.rst deleted file mode 100644 index 6710dfd68437..000000000000 --- a/docs/migration_guides/qi_migration.rst +++ /dev/null @@ -1,670 +0,0 @@ -################################ -Quantum Instance Migration Guide -################################ - -The :class:`~qiskit.utils.QuantumInstance` is a utility class that allows the joint -configuration of the circuit transpilation and execution steps, and provides functions -at a higher level of abstraction for a more convenient integration with algorithms. -These include measurement error mitigation, splitting/combining execution to -conform to job limits, -and ensuring reliable execution of circuits with additional job management tools. - -The :class:`~qiskit.utils.QuantumInstance` is being deprecated for several reasons: -On one hand, the functionality of :meth:`~qiskit.utils.QuantumInstance.execute` has -now been delegated to the different implementations of the :mod:`~qiskit.primitives` base classes. -On the other hand, with the direct implementation of transpilation at the primitives level, -the algorithms no longer -need to manage that aspect of execution, and thus :meth:`~qiskit.utils.QuantumInstance.transpile` is no longer -required by the workflow. If desired, custom transpilation routines can still be performed at the -user level through the :mod:`~qiskit.transpiler` module (see table below). - - -The following table summarizes the migration alternatives for the :class:`~qiskit.utils.QuantumInstance` class: - -.. list-table:: - :header-rows: 1 - - * - QuantumInstance method - - Alternative - * - :meth:`.QuantumInstance.execute` - - :meth:`qiskit.primitives.Sampler.run` or :meth:`qiskit.primitives.Estimator.run` - * - :meth:`.QuantumInstance.transpile` - - :meth:`qiskit.compiler.transpile` - * - :meth:`.QuantumInstance.assemble` - - :meth:`qiskit.compiler.assemble` - -The remainder of this guide will focus on the :meth:`.QuantumInstance.execute` to -:mod:`~qiskit.primitives` migration path. - -Contents -======== - -* `Choosing the right primitive for your task`_ -* `Choosing the right primitive for your settings`_ -* `Code examples`_ - -.. attention:: - - **Background on the Qiskit Primitives** - - The Qiskit Primitives are algorithmic abstractions that encapsulate the access to backends or simulators - for an easy integration into algorithm workflows. - - The current pool of primitives includes **two** different types of primitives: Sampler and - Estimator. - - Qiskit provides reference implementations in :class:`qiskit.primitives.Sampler` and :class:`qiskit.primitives.Estimator`. Additionally, - :class:`qiskit.primitives.BackendSampler` and a :class:`qiskit.primitives.BackendEstimator` are - wrappers for ``backend.run()`` that follow the primitives interface. - - Providers can implement these primitives as subclasses of :class:`~qiskit.primitives.BaseSampler` and :class:`~qiskit.primitives.BaseEstimator` respectively. - IBM's Qiskit Runtime (:mod:`qiskit_ibm_runtime`) and Aer (:mod:`qiskit_aer.primitives`) are examples of native implementations of primitives. - - This guide uses the following naming convention: - - - *Primitives* - Any Sampler/Estimator implementation using base classes :class:`qiskit.primitives.BackendSampler` and a :class:`qiskit.primitives.BackendEstimator`. - - *Reference Primitives* - :class:`qiskit.primitives.Sampler` and :class:`qiskit.primitives.Estimator` are reference implementations that come with Qiskit. - - *Aer Primitives* - The `Aer `_ primitive implementations :class:`qiskit_aer.primitives.Sampler` and :class:`qiskit_aer.primitives.Estimator`. - - *Qiskit Runtime Primitives* - IBM's Qiskit Runtime primitive implementations :class:`qiskit_ibm_runtime.Sampler` and :class:`qiskit_ibm_runtime.Estimator`. - - *Backend Primitives* - Instances of :class:`qiskit.primitives.BackendSampler` and :class:`qiskit.primitives.BackendEstimator`. These allow any backend to implement primitive interfaces - - For guidelines on which primitives to choose for your task, please continue reading. - -Choosing the right primitive for your task -=========================================== - -The :class:`~qiskit.utils.QuantumInstance` was designed to be an abstraction over transpile/run. -It took inspiration from :func:`~qiskit.execute_function.execute`, but retained config information that could be set -at the algorithm level, to save the user from defining the same parameters for every transpile/execute call. - -The :mod:`qiskit.primitives` share some of these features, but unlike the :class:`~qiskit.utils.QuantumInstance`, -there are multiple primitive classes, and each is optimized for a specific -purpose. Selecting the right primitive (``Sampler`` or ``Estimator``) requires some knowledge about -**what** it is expected to do and **where/how** it is expected to run. - -.. note:: - - The role of the primitives is two-fold. On one hand, they act as access points to backends and simulators. - On the other hand, they are **algorithmic** abstractions with defined tasks: - - * The ``Estimator`` takes in circuits and observables and returns **expectation values**. - * The ``Sampler`` takes in circuits, measures them, and returns their **quasi-probability distributions**. - -In order to know which primitive to use instead of :class:`~qiskit.utils.QuantumInstance`, you should ask -yourself two questions: - -1. What is the minimal unit of information used by your algorithm? - a. **Expectation value** - you will need an ``Estimator`` - b. **Probability distribution** (from sampling the device) - you will need a ``Sampler`` - -2. How do you want to execute your circuits? - - This question is not new. In the legacy algorithm workflow, you would have to decide to set up a - :class:`~qiskit.utils.QuantumInstance` with either a real backend from a provider, or a simulator. - Now, this "backend selection" process is translated to **where** do you import your primitives - from: - - a. Using **local** statevector simulators for quick prototyping: **Reference Primitives** - b. Using **local** noisy simulations for finer algorithm tuning: **Aer Primitives** - c. Accessing **runtime-enabled backends** (or cloud simulators): **Qiskit Runtime Primitives** - d. Accessing **non runtime-enabled backends** : **Backend Primitives** - -Arguably, the ``Sampler`` is the closest primitive to :class:`~qiskit.utils.QuantumInstance`, as they -both execute circuits and provide a result back. However, with the :class:`~qiskit.utils.QuantumInstance`, -the result data was backend dependent (it could be a counts ``dict``, a :class:`numpy.array` for -statevector simulations, etc), while the ``Sampler`` normalizes its ``SamplerResult`` to -return a :class:`~qiskit.result.QuasiDistribution` object with the resulting quasi-probability distribution. - -The ``Estimator`` provides a specific abstraction for the expectation value calculation that can replace -the use of :class:`.QuantumInstance` as well as the associated pre- and post-processing steps, usually performed -with an additional library such as :mod:`qiskit.opflow`. - -Choosing the right primitive for your settings -============================================== - -Certain :class:`~qiskit.utils.QuantumInstance` features are only available in certain primitive implementations. -The following table summarizes the most common :class:`~qiskit.utils.QuantumInstance` settings and which -primitives **expose a similar setting through their interface**: - -.. attention:: - - In some cases, a setting might not be exposed through the interface, but there might an alternative path to make - it work. This is the case for custom transpiler passes, which cannot be set through the primitives interface, - but pre-transpiled circuits can be sent if setting the option ``skip_transpilation=True``. For more information, - please refer to the API reference or source code of the desired primitive implementation. - -.. list-table:: - :header-rows: 1 - - * - QuantumInstance - - Reference Primitives - - Aer Primitives - - Qiskit Runtime Primitives - - Backend Primitives - * - Select ``backend`` - - No - - No - - Yes - - Yes - * - Set ``shots`` - - Yes - - Yes - - Yes - - Yes - * - Simulator settings: ``basis_gates``, ``coupling_map``, ``initial_layout``, ``noise_model``, ``backend_options`` - - No - - Yes - - Yes - - No (inferred from internal ``backend``) - * - Transpiler settings: ``seed_transpiler``, ``optimization_level`` - - No - - No - - Yes (via ``options``) (*) - - Yes (via ``.set_transpile_options()``) - * - Set unbound ``pass_manager`` - - No - - No - - No (but can ``skip_transpilation``) - - No (but can ``skip_transpilation``) - * - Set ``bound_pass_manager`` - - No - - No - - No - - Yes - * - Set ``backend_options``: common ones were ``memory`` and ``meas_level`` - - No - - No - - No (only ``qubit_layout``) - - No - * - Measurement error mitigation: ``measurement_error_mitigation_cls``, ``cals_matrix_refresh_period``, - ``measurement_error_mitigation_shots``, ``mit_pattern`` - - No - - No - - Sampler default -> M3 (*) - - No - * - Job management: ``job_callback``, ``max_job_retries``, ``timeout``, ``wait`` - - Does not apply - - Does not apply - - Sessions, callback (**) - - No - - -(*) For more information on error mitigation and setting options on Qiskit Runtime Primitives, visit -`this link `_. - -(**) For more information on Runtime sessions, visit `this how-to `_. - -Code examples -============= - -.. dropdown:: Example 1: Circuit Sampling with Local Simulation - :animate: fade-in-slide-down - - **Using Quantum Instance** - - The only alternative for local simulations using the quantum instance was using an Aer simulator backend. - If no simulation method is specified, the Aer simulator will default to an exact simulation - (statevector/stabilizer), if shots are specified, it will add shot noise. - Please note that ``QuantumInstance.execute()`` returned the counts in hexadecimal format. - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit_aer import AerSimulator - from qiskit.utils import QuantumInstance - - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() - - simulator = AerSimulator() - qi = QuantumInstance(backend=simulator, shots=200) - result = qi.execute(circuit).results[0] - data = result.data - counts = data.counts - - print("Counts: ", counts) - print("Data: ", data) - print("Result: ", result) - - .. code-block:: text - - Counts: {'0x3': 200} - Data: ExperimentResultData(counts={'0x3': 200}) - Result: ExperimentResult(shots=200, success=True, meas_level=2, data=ExperimentResultData(counts={'0x3': 200}), header=QobjExperimentHeader(clbit_labels=[['meas', 0], ['meas', 1]], creg_sizes=[['meas', 2]], global_phase=0.0, memory_slots=2, metadata={}, n_qubits=2, name='circuit-99', qreg_sizes=[['q', 2]], qubit_labels=[['q', 0], ['q', 1]]), status=DONE, seed_simulator=2846213898, metadata={'parallel_state_update': 16, 'parallel_shots': 1, 'sample_measure_time': 0.00025145, 'noise': 'ideal', 'batched_shots_optimization': False, 'remapped_qubits': False, 'device': 'CPU', 'active_input_qubits': [0, 1], 'measure_sampling': True, 'num_clbits': 2, 'input_qubit_map': [[1, 1], [0, 0]], 'num_qubits': 2, 'method': 'stabilizer', 'fusion': {'enabled': False}}, time_taken=0.000672166) - - **Using Primitives** - - The primitives offer two alternatives for local simulation, one with the Reference primitives - and one with the Aer primitives. As mentioned above the closest alternative to ``QuantumInstance.execute()`` - for sampling is the ``Sampler`` primitive. - - **a. Using the Reference Primitives** - - Basic simulation implemented using the :mod:`qiskit.quantum_info` module. If shots are - specified, the results will include shot noise. Please note that - the resulting quasi-probability distribution does not use bitstrings but **integers** to identify the states. - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.primitives import Sampler - - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() - - sampler = Sampler() - result = sampler.run(circuit, shots=200).result() - quasi_dists = result.quasi_dists - - print("Quasi-dists: ", quasi_dists) - print("Result: ", result) - - .. code-block:: text - - Quasi-dists: [{3: 1.0}] - Result: SamplerResult(quasi_dists=[{3: 1.0}], metadata=[{'shots': 200}]) - - **b. Using the Aer Primitives** - - Aer simulation following the statevector method. This would be the closer replacement of the - :class:`~qiskit.utils.QuantumInstance` - example, as they are both accessing the same simulator. For this reason, the output metadata is - closer to the Quantum Instance's output. Please note that - the resulting quasi-probability distribution does not use bitstrings but **integers** to identify the states. - - .. note:: - - The :class:`qiskit.result.QuasiDistribution` class returned as part of the :class:`qiskit.primitives.SamplerResult` - exposes two methods to convert the result keys from integer to binary strings/hexadecimal: - - - :meth:`qiskit.result.QuasiDistribution.binary_probabilities` - - :meth:`qiskit.result.QuasiDistribution.hex_probabilities` - - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit_aer.primitives import Sampler - - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() - - # if no Noise Model provided, the aer primitives - # perform an exact (statevector) simulation - sampler = Sampler() - result = sampler.run(circuit, shots=200).result() - quasi_dists = result.quasi_dists - # convert keys to binary bitstrings - binary_dist = quasi_dists[0].binary_probabilities() - - print("Quasi-dists: ", quasi_dists) - print("Result: ", result) - print("Binary quasi-dist: ", binary_dist) - - .. code-block:: text - - Quasi-dists: [{3: 1.0}] - Result: SamplerResult(quasi_dists=[{3: 1.0}], metadata=[{'shots': 200, 'simulator_metadata': {'parallel_state_update': 16, 'parallel_shots': 1, 'sample_measure_time': 9.016e-05, 'noise': 'ideal', 'batched_shots_optimization': False, 'remapped_qubits': False, 'device': 'CPU', 'active_input_qubits': [0, 1], 'measure_sampling': True, 'num_clbits': 2, 'input_qubit_map': [[1, 1], [0, 0]], 'num_qubits': 2, 'method': 'statevector', 'fusion': {'applied': False, 'max_fused_qubits': 5, 'threshold': 14, 'enabled': True}}}]) - Binary quasi-dist: {'11': 1.0} - -.. dropdown:: Example 2: Expectation Value Calculation with Local Noisy Simulation - :animate: fade-in-slide-down - - While this example does not include a direct call to ``QuantumInstance.execute()``, it shows - how to migrate from a :class:`~qiskit.utils.QuantumInstance`-based to a :mod:`~qiskit.primitives`-based - workflow. - - **Using Quantum Instance** - - The most common use case for computing expectation values with the Quantum Instance was as in combination with the - :mod:`~qiskit.opflow` library. You can see more information in the `opflow migration guide `_. - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.opflow import StateFn, PauliSumOp, PauliExpectation, CircuitSampler - from qiskit.utils import QuantumInstance - from qiskit_aer import AerSimulator - from qiskit_aer.noise import NoiseModel - from qiskit_ibm_provider import IBMProvider - - # Define problem using opflow - op = PauliSumOp.from_list([("XY",1)]) - qc = QuantumCircuit(2) - qc.x(0) - qc.x(1) - - state = StateFn(qc) - measurable_expression = StateFn(op, is_measurement=True).compose(state) - expectation = PauliExpectation().convert(measurable_expression) - - # Define Quantum Instance with noisy simulator - provider = IBMProvider() - device = provider.get_backend("ibmq_manila") - noise_model = NoiseModel.from_backend(device) - coupling_map = device.configuration().coupling_map - - backend = AerSimulator() - qi = QuantumInstance(backend=backend, shots=1024, - seed_simulator=42, seed_transpiler=42, - coupling_map=coupling_map, noise_model=noise_model) - - # Run - sampler = CircuitSampler(qi).convert(expectation) - expectation_value = sampler.eval().real - - print(expectation_value) - - .. code-block:: text - - -0.04687500000000008 - - **Using Primitives** - - The primitives now allow the combination of the opflow and quantum instance functionality in a single ``Estimator``. - In this case, for local noisy simulation, this will be the Aer Estimator. - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.quantum_info import SparsePauliOp - from qiskit_aer.noise import NoiseModel - from qiskit_aer.primitives import Estimator - from qiskit_ibm_provider import IBMProvider - - # Define problem - op = SparsePauliOp("XY") - qc = QuantumCircuit(2) - qc.x(0) - qc.x(1) - - # Define Aer Estimator with noisy simulator - device = provider.get_backend("ibmq_manila") - noise_model = NoiseModel.from_backend(device) - coupling_map = device.configuration().coupling_map - - # if Noise Model provided, the aer primitives - # perform a "qasm" simulation - estimator = Estimator( - backend_options={ # method chosen automatically to match options - "coupling_map": coupling_map, - "noise_model": noise_model, - }, - run_options={"seed": 42, "shots": 1024}, - transpile_options={"seed_transpiler": 42}, - ) - - # Run - expectation_value = estimator.run(qc, op).result().values - - print(expectation_value) - - .. code-block:: text - - [-0.04101562] - -.. dropdown:: Example 3: Circuit Sampling on IBM Backend with Error Mitigation - :animate: fade-in-slide-down - - **Using Quantum Instance** - - The ``QuantumInstance`` interface allowed the configuration of measurement error mitigation settings such as the method, the - matrix refresh period or the mitigation pattern. This configuration is no longer available in the primitives - interface. - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit.utils import QuantumInstance - from qiskit.utils.mitigation import CompleteMeasFitter - from qiskit_ibm_provider import IBMProvider - - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() - - provider = IBMProvider() - backend = provider.get_backend("ibmq_montreal") - - qi = QuantumInstance( - backend=backend, - shots=4000, - measurement_error_mitigation_cls=CompleteMeasFitter, - cals_matrix_refresh_period=0, - ) - - result = qi.execute(circuit).results[0].data - print(result) - - .. code-block:: text - - ExperimentResultData(counts={'11': 4000}) - - - **Using Primitives** - - The Qiskit Runtime Primitives offer a suite of error mitigation methods that can be easily turned on with the - ``resilience_level`` option. These are, however, not configurable. The sampler's ``resilience_level=1`` - is the closest alternative to the Quantum Instance's measurement error mitigation implementation, but this - is not a 1-1 replacement. - - For more information on the error mitigation options in the Qiskit Runtime Primitives, you can check out the following - `link `_. - - .. code-block:: python - - from qiskit import QuantumCircuit - from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Options - - circuit = QuantumCircuit(2) - circuit.x(0) - circuit.x(1) - circuit.measure_all() - - service = QiskitRuntimeService(channel="ibm_quantum") - backend = service.backend("ibmq_montreal") - - options = Options(resilience_level = 1) # 1 = measurement error mitigation - sampler = Sampler(session=backend, options=options) - - # Run - result = sampler.run(circuit, shots=4000).result() - quasi_dists = result.quasi_dists - - print("Quasi dists: ", quasi_dists) - - .. code-block:: text - - Quasi dists: [{2: 0.0008492371522941081, 3: 0.9968874384378738, 0: -0.0003921227905920063, - 1: 0.002655447200424097}] - -.. dropdown:: Example 4: Circuit Sampling with Custom Bound and Unbound Pass Managers - :animate: fade-in-slide-down - - The management of transpilation is different between the ``QuantumInstance`` and the Primitives. - - The Quantum Instance allowed you to: - - * Define bound and unbound pass managers that will be called during ``.execute()``. - * Explicitly call its ``.transpile()`` method with a specific pass manager. - - However: - - * The Quantum Instance **did not** manage parameter bindings on parametrized quantum circuits. This would - mean that if a ``bound_pass_manager`` was set, the circuit sent to ``QuantumInstance.execute()`` could - not have any free parameters. - - On the other hand, when using the primitives: - - * You cannot explicitly access their transpilation routine. - * The mechanism to apply custom transpilation passes to the Aer, Runtime and Backend primitives is to pre-transpile - locally and set ``skip_transpilation=True`` in the corresponding primitive. - * The only primitives that currently accept a custom **bound** transpiler pass manager are instances of :class:`~qiskit.primitives.BackendSampler` or :class:`~qiskit.primitives.BackendEstimator`. - If a ``bound_pass_manager`` is defined, the ``skip_transpilation=True`` option will **not** skip this bound pass. - - .. attention:: - - Care is needed when setting ``skip_transpilation=True`` with the ``Estimator`` primitive. - Since operator and circuit size need to match for the Estimator, should the custom transpilation change - the circuit size, then the operator must be adapted before sending it - to the Estimator, as there is no currently no mechanism to identify the active qubits it should consider. - - .. - In opflow, the ansatz would always have the basis change and measurement gates added before transpilation, - so if the circuit ended up on more qubits it did not matter. - - Note that the primitives **do** handle parameter bindings, meaning that even if a ``bound_pass_manager`` is defined in a - :class:`~qiskit.primitives.BackendSampler` or :class:`~qiskit.primitives.BackendEstimator`, you do not have to manually assign parameters as expected in the Quantum Instance workflow. - - The use-case that motivated the addition of the two-stage transpilation to the ``QuantumInstance`` was to allow - running pulse-efficient transpilation passes with the :class:`~qiskit.opflow.CircuitSampler` class. The following - example shows to migrate this particular use-case, where the ``QuantumInstance.execute()`` method is called - under the hood by the :class:`~qiskit.opflow.CircuitSampler`. - - **Using Quantum Instance** - - .. code-block:: python - - from qiskit.circuit.library.standard_gates.equivalence_library import StandardEquivalenceLibrary as std_eqlib - from qiskit.circuit.library import RealAmplitudes - from qiskit.opflow import CircuitSampler, StateFn - from qiskit.providers.fake_provider import FakeBelem - from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap - from qiskit.transpiler.preset_passmanagers import level_1_pass_manager - from qiskit.transpiler.passes import ( - Collect2qBlocks, ConsolidateBlocks, Optimize1qGatesDecomposition, - RZXCalibrationBuilderNoEcho, UnrollCustomDefinitions, BasisTranslator - ) - from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import EchoRZXWeylDecomposition - from qiskit.utils import QuantumInstance - - # Define backend - backend = FakeBelem() - - # Build the pass manager for the parameterized circuit - rzx_basis = ['rzx', 'rz', 'x', 'sx'] - coupling_map = CouplingMap(backend.configuration().coupling_map) - config = PassManagerConfig(basis_gates=rzx_basis, coupling_map=coupling_map) - pre = level_1_pass_manager(config) - inst_map = backend.defaults().instruction_schedule_map - - # Build a pass manager for the CX decomposition (works only on bound circuits) - post = PassManager([ - # Consolidate consecutive two-qubit operations. - Collect2qBlocks(), - ConsolidateBlocks(basis_gates=['rz', 'sx', 'x', 'rxx']), - - # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates. - EchoRZXWeylDecomposition(inst_map), - - # Attach scaled CR pulse schedules to the RZX gates. - RZXCalibrationBuilderNoEcho(inst_map), - - # Simplify single-qubit gates. - UnrollCustomDefinitions(std_eqlib, rzx_basis), - BasisTranslator(std_eqlib, rzx_basis), - Optimize1qGatesDecomposition(rzx_basis), - ]) - - # Instantiate qi - quantum_instance = QuantumInstance(backend, pass_manager=pre, bound_pass_manager=post) - - # Define parametrized circuit and parameter values - qc = RealAmplitudes(2) - print(qc.decompose()) - param_dict = {p: 0.5 for p in qc.parameters} - - # Instantiate CircuitSampler - sampler = CircuitSampler(quantum_instance) - - # Run - quasi_dists = sampler.convert(StateFn(qc), params=param_dict).sample() - print("Quasi-dists: ", quasi_dists) - - .. code-block:: text - - ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ - q_0: ┤ Ry(θ[0]) ├──■──┤ Ry(θ[2]) ├──■──┤ Ry(θ[4]) ├──■──┤ Ry(θ[6]) ├ - ├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤ - q_1: ┤ Ry(θ[1]) ├┤ X ├┤ Ry(θ[3]) ├┤ X ├┤ Ry(θ[5]) ├┤ X ├┤ Ry(θ[7]) ├ - └──────────┘└───┘└──────────┘└───┘└──────────┘└───┘└──────────┘ - Quasi-dists: {'11': 0.443359375, '10': 0.21875, '01': 0.189453125, '00': 0.1484375} - - **Using Primitives** - - Let's see how the workflow changes with the Backend Sampler: - - .. code-block:: python - - from qiskit.circuit.library.standard_gates.equivalence_library import StandardEquivalenceLibrary as std_eqlib - from qiskit.circuit.library import RealAmplitudes - from qiskit.primitives import BackendSampler - from qiskit.providers.fake_provider import FakeBelem - from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap - from qiskit.transpiler.preset_passmanagers import level_1_pass_manager - from qiskit.transpiler.passes import ( - Collect2qBlocks, ConsolidateBlocks, Optimize1qGatesDecomposition, - RZXCalibrationBuilderNoEcho, UnrollCustomDefinitions, BasisTranslator - ) - from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import EchoRZXWeylDecomposition - - # Define backend - backend = FakeBelem() - - # Build the pass manager for the parameterized circuit - rzx_basis = ['rzx', 'rz', 'x', 'sx'] - coupling_map = CouplingMap(backend.configuration().coupling_map) - config = PassManagerConfig(basis_gates=rzx_basis, coupling_map=coupling_map) - pre = level_1_pass_manager(config) - - # Build a pass manager for the CX decomposition (works only on bound circuits) - inst_map = backend.defaults().instruction_schedule_map - post = PassManager([ - # Consolidate consecutive two-qubit operations. - Collect2qBlocks(), - ConsolidateBlocks(basis_gates=['rz', 'sx', 'x', 'rxx']), - - # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates. - EchoRZXWeylDecomposition(inst_map), - - # Attach scaled CR pulse schedules to the RZX gates. - RZXCalibrationBuilderNoEcho(inst_map), - - # Simplify single-qubit gates. - UnrollCustomDefinitions(std_eqlib, rzx_basis), - BasisTranslator(std_eqlib, rzx_basis), - Optimize1qGatesDecomposition(rzx_basis), - ]) - - # Define parametrized circuit and parameter values - qc = RealAmplitudes(2) - qc.measure_all() # add measurements! - print(qc.decompose()) - - # Instantiate backend sampler with skip_transpilation - sampler = BackendSampler(backend=backend, skip_transpilation=True, bound_pass_manager=post) - - # Run unbound transpiler pass - transpiled_circuit = pre.run(qc) - - # Run sampler - quasi_dists = sampler.run(transpiled_circuit, [[0.5] * len(qc.parameters)]).result().quasi_dists - print("Quasi-dists: ", quasi_dists) - - .. code-block:: text - - ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ░ ┌─┐ - q_0: ┤ Ry(θ[0]) ├──■──┤ Ry(θ[2]) ├──■──┤ Ry(θ[4]) ├──■──┤ Ry(θ[6]) ├─░─┤M├─── - ├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤┌─┴─┐├──────────┤ ░ └╥┘┌─┐ - q_1: ┤ Ry(θ[1]) ├┤ X ├┤ Ry(θ[3]) ├┤ X ├┤ Ry(θ[5]) ├┤ X ├┤ Ry(θ[7]) ├─░──╫─┤M├ - └──────────┘└───┘└──────────┘└───┘└──────────┘└───┘└──────────┘ ░ ║ └╥┘ - meas: 2/═══════════════════════════════════════════════════════════════════╩══╩═ - 0 1 - Quasi-dists: [{1: 0.18359375, 2: 0.2333984375, 0: 0.1748046875, 3: 0.408203125}] diff --git a/docs/qc_intro.rst b/docs/qc_intro.rst deleted file mode 100644 index 2980416140af..000000000000 --- a/docs/qc_intro.rst +++ /dev/null @@ -1,234 +0,0 @@ - -.. figure:: images/qiskit_nutshell.png - :scale: 50 % - :align: center - -.. _qc-intro: - -=============================== -Quantum computing in a nutshell -=============================== - -Quantum computing represents a new paradigm in computation that utilizes the fundamental -principles of quantum mechanics to perform calculations. If you are reading this then you -have undoubtedly heard that the promise of quantum computation lies in the possibility of -efficiently performing a handful of tasks such as prime factorization, quantum simulation, search, -optimization, and algebraic programs such as machine learning; computations that at size are -beyond the capabilities of even the largest of -classical computers. - -The power of quantum computing rests on two cornerstones of quantum mechanics, namely -:ref:`interference ` and -:ref:`entanglement ` that highlight the wave- and particle-like aspects -of quantum computation, respectively. Qiskit is an SDK for performing quantum computations -that utilize these quantum mechanical principles using the language of -:ref:`quantum circuits `. Comprised of quantum gates, instructions, and -classical control logic, quantum circuits allow for expressing complex algorithms -and applications in a abstract manner that can be executed on a quantum computer. At its -core, Qiskit is a quantum circuit construction, optimization, and execution engine. -Additional algorithm and application layers leverage quantum circuits, often in concert -with classical computing resources, to solve problems in optimization, quantum chemistry, -physics, machine learning, and finance. In what follows, we give a very brief overview -of quantum computing, and how Qiskit is used at each step. Interested readers are -directed to additional in-depth materials for further insights. - - -.. _qc-intro-interference: - -Interference -============ - -Like a classical computer, a quantum computer operates on bits. However, while classical bits can -only be found in the states 0 and 1, a quantum bit, or qubit, can represent the values 0 and 1, -or linear combinations of both. These linear combinations are known as superpositions -(or superposition states). - -To see how this resource is utilized in quantum computation we first turn to a classical -analog: noise cancellation. Noise cancellation, as done in noise cancelling headphones -for example, is performed by employing superposition and the principle of **interference** -to reduce the amplitude of unwanted noise by generating a tone of approximately the same -frequency and amplitude, but out of phase by a value of :math:`\pi` (or any other odd -integer of :math:`\pi`). - -.. figure:: images/noise_cancel.png - :scale: 40 % - :align: center - - Approximate cancellation of a noise signal by a tone of nearly equal amplitude - and offset by a phase of :math:`\sim \pi`. - -As shown above, when the phase difference is close to an odd multiple of :math:`\pi`, -the superposition of the two waves results in interference, and an output that is -significantly reduced compared to the original. The result is the signal of interest -unencumbered by noise. Although this processing is done by digital circuits, the amplitude -and phase are continuous variables that can never be matched perfectly, resulting in -incomplete correction. - -A general computation on a quantum computer proceeds in very much the same way as -noise cancellation. To begin, one prepares a superposition of all possible computation -states. This is then used as an input to a :ref:`quantum circuit ` that -selectively interferes the components of the superposition according to a prescribed algorithm. -What remains after cancelling the relative amplitudes and phases of the input state is the -solution to the computation performed by the quantum circuit. - -.. figure:: images/quantum_interference.png - :align: center - - Quantum computation as an interference generation process. - -.. _qc-intro-entanglement: - -Entanglement -============ - -The second principle of quantum mechanics that quantum computation can utilize is the -phenomena of **entanglement**. Entanglement refers to states of more than one qubit -(or particles in general) in which the combined state of the qubits contains more -information than the qubits do independently. The overwhelming majority of multi-qubit quantum -states are entangled, and represent a valuable resource. For example, entangled states between -qubits can be used for quantum teleportation, where a shared entangled -state of two qubits can be manipulated to transfer information from one qubit to another, -regardless of the relative physical proximity of the qubits. Entangled states, as natural -states of quantum systems, are also of importance in disciplines -such as quantum chemistry and quantum simulation where the solution(s) often take the form -of entangled multi-qubit states. One can also utilize highly-entangled quantum states -of multiple qubits to, for example, generate certifiably random numbers. There is even a `Qiskit -package `_ to do this! - - -.. _qc-intro-circuits: - -Quantum circuits -================ - -Algorithms and applications that utilize quantum mechanical resources can be easily and efficiently -written in the language of **quantum circuits**. A quantum circuit is a -computational routine consisting of coherent quantum operations on quantum data, such as that -held in qubits, and concurrent real-time classical computation. Each horizontal line, or wire -in a circuit represents a qubit, with the left end of the wire being the -initial quantum data, and the right being the final quantum data generated by the quantum -circuit's computation. Operations on qubits can be placed on these wires, and are represented -by boxes. - -.. figure:: images/teleportation_detailed.png - :align: center - - Quantum state teleportation circuit revisited. - -Quantum circuits enable a quantum computer to take in classical information and output a -classical solution, leveraging quantum principles such as -:ref:`interference ` and -:ref:`entanglement ` to perform the computation. - -A typical quantum algorithm workflow consists of: - -- The problem we want to solve, -- A classical algorithm that generates a description of a quantum circuit, -- The quantum circuit that needs to be run on quantum hardware, -- And the output classical solution to the problem that it produces. - -Quantum gates form the primitive operations on quantum data. Quantum gates represent -information preserving, reversible transformations on the quantum data stored in qubits. -These "unitary" transformations represent the quantum mechanical core of a quantum -circuit. Some gates such as :math:`X` (also written as :math:`\oplus`) and :math:`CX` -have classical analogs such as bit-flip and :math:`XOR` operations, respectively, -while others do not. The Hadamard (:math:`H`) gate, along with the parameterized rotates -:math:`rX(\theta)` and :math:`rY(\theta)`, generate superposition states, -while gates such as :math:`Z`, :math:`rZ(\theta)`, :math:`S`, and :math:`T` impart phases that -can be used for interference. Two-qubit gates like the :math:`CX` gate are used -to generate entanglement between pairs of qubits, or to "kick" the phase from -one qubit to another. In contrast to gates, operations like "measurement", represented by -the meter symbol in a box with a line connecting to a "target" wire, extract partial -information about a qubit's state, often losing the phase, to be able to represent it as -a classical bit and write that classical bit onto the target wire (often a fully classical -wire in some readout device). This is the typical way to take information from the -quantum data into a classical device. Note that with only :math:`H`, :math:`rZ(\theta)`, -:math:`CX`, and measurement gates, i.e. a universal gate set, we can construct any quantum circuit, -including those efficiently computing the dynamics of any physical system in nature. - -Some workloads contain an extended sequence of interleaved quantum circuits and classical -computation, for example variational quantum algorithms execute quantum circuits within an -optimization loop. For these workloads, system performance increases substantially if the -quantum circuits are parameterized, and transitions between circuit execution and non-current -classical computation are made efficient. -Consequently, we define **near-time computation** to refer to computations with algorithms that make -repeated use of quantum circuits with hardware developed to speed up the computation time. In -near-time computation, the classical computation occurs on a time scale longer than the coherence -of the quantum computation. Contrast this with **real-time computation**, where the classical -computation occurs within the decoherence time of the quantum device. - -Constructing complex quantum circuits with minimal effort is at the heart of Qiskit, -which supports a rich feature set of operations and can send your circuits to a range of -:ref:`quantum computers ` or classical simulators. -With only a few lines of code, it is possible to construct complex circuits like the -one above. - -.. code-block:: python - - qr = QuantumRegister(3, 'q') - cr = ClassicalRegister(2, 'zx_meas') - qc = QuantumCircuit(qr,cr) - qc.reset(range(3)) - qc.barrier() - qc.h(1) - qc.cx([1,0],[2,1]) - qc.h(0) - qc.barrier() - qc.measure([0,1], [0,1]) - qc.barrier() - qc.z(2).c_if(cr, 1) - qc.x(2).c_if(cr, 2) - -.. _qc-intro-computers: - -Quantum computers -================= - -.. figure:: images/system_one.jpeg - :align: right - :figwidth: 200px - - A view inside the IBM Quantum System One. - -Quantum computers which are programmed using quantum circuits can be constructed out of any -quantum technology that allows for defining qubit elements, and can implement -single- and multi-qubit gate operations with high-fidelity. At present, architectures -based on superconducting circuits, trapped-ions, semiconducting quantum-dots, photons, and -neutral atoms, are actively being developed, and many are accessible to users over the internet. -Qiskit is agnostic with respect to the underlying architecture of a given quantum system, -and can compile a quantum circuit to match the entangling gate topology of a quantum device, -map the circuit instructions into the native gate set of the device, and optimize the resulting -quantum circuit for enhanced fidelity. - -As with the noise cancellation example above, the amplitude and phase of qubits are continuous -degrees of freedom upon which operations can never be done exactly. These gates errors, along -with noise from the environment in which a quantum computer resides, can conspire to ruin a -computation if not accounted for in the compilation process, and may require additional -mitigation procedures in order to obtain a high-fidelity output on present day -quantum systems susceptible to noise. Qiskit is capable of taking into account a wide range of -device calibration metrics (see figure below) in its compilation strategy, and can select an -optimal set of qubits on which to run a given quantum circuit. In addition, Qiskit hosts a -collection of noise mitigation techniques for extracting a faithful representation of a quantum -circuits output. - -.. figure:: images/system_error.png - :align: center - - Topology and error rates for the IBM Quantum *ibmq_manhattan* system. - - -Where to go from here -====================== - -Hopefully we have given the reader a taste of what quantum computation has to offer -and you are hungry for more. If so, there are several resources that may be of -interest: - -- `Getting started with Qiskit `_ - Dive right into Qiskit. - -- `Field guide to quantum computing `_ : A gentle - physics-based introduction written by some of the founders of quantum computation that makes use - of the interactive circuit composer. - -- `Qiskit textbook `_ : A university quantum algorithms/computation - course supplement based on Qiskit. diff --git a/docs/release_notes.rst b/docs/release_notes.rst index a9d629df8507..28398c11a1ff 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -5,8 +5,20 @@ Release Notes ============= This page contains the release notes for Qiskit, starting from the point at which the legacy -"elements" structure was completely removed. For release notes stretching back through the old -"meta-package" structure of Qiskit, see :ref:`legacy-release-notes`. +"elements" structure was completely removed. .. release-notes:: - :earliest-version: 0.25.0rc1 + :earliest-version: 0.45.0rc1 + :branch: main + +.. release-notes:: + :earliest-version: 0.45.0 + :branch: stable/0.46 + +.. release-notes:: + :earliest-version: 0.45.0 + :branch: stable/0.45 + +.. release-notes:: + :earliest-version: 0.25.0 + :branch: stable/0.25 diff --git a/docs/tutorials.rst b/docs/tutorials.rst deleted file mode 100644 index d6189e1ce0b4..000000000000 --- a/docs/tutorials.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _tutorials: - -========= -Tutorials -========= - -.. note:: - The Simulators tutorials have moved to - `Qiskit Aer `_ - and the Algorithms tutorials to - `Qiskit Algorithms `_. - -Introductory -============ - -.. qiskit-card:: - :header: Qiskit warmup - :card_description: An introduction to Qiskit and the primary user workflow. - :image: _static/images/logo.png - :link: intro_tutorial1.html - -Quantum circuits -================ - -.. nbgallery:: - :glob: - - tutorials/circuits/* - -Advanced circuits -================= - -.. nbgallery:: - :glob: - - tutorials/circuits_advanced/* diff --git a/docs/tutorials/circuits/01_circuit_basics.ipynb b/docs/tutorials/circuits/01_circuit_basics.ipynb deleted file mode 100644 index 0fb1a5c57213..000000000000 --- a/docs/tutorials/circuits/01_circuit_basics.ipynb +++ /dev/null @@ -1,824 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Circuit Basics \n", - "\n", - "Here, we provide an overview of working with Qiskit. Qiskit provides the basic building blocks necessary to program quantum computers. The fundamental unit of Qiskit is the [quantum circuit](https://en.wikipedia.org/wiki/Quantum_circuit). A basic workflow using Qiskit consists of two stages: **Build** and **Run**. **Build** allows you to make different quantum circuits that represent the problem you are solving, and **Run** that allows you to run them on different backends. After the jobs have been run, the data is collected and postprocessed depending on the desired output." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:44.387267Z", - "start_time": "2019-08-10T11:37:41.934365Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:12.548953Z", - "iopub.status.busy": "2023-08-25T18:25:12.548700Z", - "iopub.status.idle": "2023-08-25T18:25:13.088168Z", - "shell.execute_reply": "2023-08-25T18:25:13.087422Z" - } - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "from qiskit import QuantumCircuit\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Building the circuit \n", - "\n", - "The basic element needed for your first program is the QuantumCircuit. We begin by creating a `QuantumCircuit` comprised of three qubits." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:44.392806Z", - "start_time": "2019-08-10T11:37:44.389673Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:13.092320Z", - "iopub.status.busy": "2023-08-25T18:25:13.091723Z", - "iopub.status.idle": "2023-08-25T18:25:13.095165Z", - "shell.execute_reply": "2023-08-25T18:25:13.094601Z" - } - }, - "outputs": [], - "source": [ - "# Create a Quantum Circuit acting on a quantum register of three qubits\n", - "circ = QuantumCircuit(3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After you create the circuit with its registers, you can add gates (\"operations\") to manipulate the registers. As you proceed through the tutorials you will find more gates and circuits; below is an example of a quantum circuit that makes a three-qubit GHZ state\n", - "\n", - "$$|\\psi\\rangle = \\left(|000\\rangle+|111\\rangle\\right)/\\sqrt{2}.$$\n", - "\n", - "To create such a state, we start with a three-qubit quantum register. By default, each qubit in the register is initialized to $|0\\rangle$. To make the GHZ state, we apply the following gates:\n", - "- A Hadamard gate $H$ on qubit 0, which puts it into the superposition state $\\left(|0\\rangle+|1\\rangle\\right)/\\sqrt{2}$.\n", - "- A Controlled-NOT operation ($C_{X}$) between qubit 0 and qubit 1.\n", - "- A Controlled-NOT operation between qubit 0 and qubit 2.\n", - "\n", - "On an ideal quantum computer, the state produced by running this circuit would be the GHZ state above.\n", - "\n", - "In Qiskit, operations can be added to the circuit one by one, as shown below." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:44.401502Z", - "start_time": "2019-08-10T11:37:44.395545Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:13.098260Z", - "iopub.status.busy": "2023-08-25T18:25:13.097824Z", - "iopub.status.idle": "2023-08-25T18:25:13.109016Z", - "shell.execute_reply": "2023-08-25T18:25:13.108409Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Add a H gate on qubit 0, putting this qubit in superposition.\n", - "circ.h(0)\n", - "# Add a CX (CNOT) gate on control qubit 0 and target qubit 1, putting\n", - "# the qubits in a Bell state.\n", - "circ.cx(0, 1)\n", - "# Add a CX (CNOT) gate on control qubit 0 and target qubit 2, putting\n", - "# the qubits in a GHZ state.\n", - "circ.cx(0, 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize Circuit \n", - "\n", - "You can visualize your circuit using Qiskit `QuantumCircuit.draw()`, which plots the circuit in the form found in many textbooks." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:44.762773Z", - "start_time": "2019-08-10T11:37:44.403727Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:13.152244Z", - "iopub.status.busy": "2023-08-25T18:25:13.151806Z", - "iopub.status.idle": "2023-08-25T18:25:14.366086Z", - "shell.execute_reply": "2023-08-25T18:25:14.365356Z" - }, - "scrolled": true - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "circ.draw('mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this circuit, the qubits are put in order, with qubit zero at the top and qubit two at the bottom. The circuit is read left to right (meaning that gates that are applied earlier in the circuit show up further to the left)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "\n", - "When representing the state of a multi-qubit system, the tensor order used in Qiskit is different than that used in most physics textbooks. Suppose there are $n$ qubits, and qubit $j$ is labeled as $Q_{j}$. Qiskit uses an ordering in which the $n^{\\mathrm{th}}$ qubit is on the left side of the tensor product, so that the basis vectors are labeled as $Q_{n-1}\\otimes \\cdots \\otimes Q_1\\otimes Q_0$.\n", - "\n", - "For example, if qubit zero is in state 0, qubit 1 is in state 0, and qubit 2 is in state 1, Qiskit would represent this state as $|100\\rangle$, whereas many physics textbooks would represent it as $|001\\rangle$.\n", - "\n", - "This difference in labeling affects the way multi-qubit operations are represented as matrices. For example, Qiskit represents a controlled-X ($C_{X}$) operation with qubit 0 being the control and qubit 1 being the target as\n", - "\n", - "$$C_X = \\begin{pmatrix} 1 & 0 & 0 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 0 & 0 & 1 & 0 \\\\ 0 & 1 & 0 & 0 \\\\\\end{pmatrix}.$$\n", - "\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simulating circuits \n", - "\n", - "To simulate a circuit we use the quant_info module in Qiskit. This simulator returns the quantum state, which is a complex vector of dimensions $2^n$, where $n$ is the number of qubits \n", - "(so be careful using this as it will quickly get too large to run on your machine).\n", - "\n", - "There are two stages to the simulator. The first is to set the input state and the second to evolve the state by the quantum circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:14.371483Z", - "iopub.status.busy": "2023-08-25T18:25:14.370156Z", - "iopub.status.idle": "2023-08-25T18:25:14.782532Z", - "shell.execute_reply": "2023-08-25T18:25:14.781739Z" - } - }, - "outputs": [ - { - "data": { - "text/latex": [ - "$$\\frac{\\sqrt{2}}{2} |000\\rangle+\\frac{\\sqrt{2}}{2} |111\\rangle$$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.quantum_info import Statevector\n", - "\n", - "# Set the initial state of the simulator to the ground state using from_int\n", - "state = Statevector.from_int(0, 2**3)\n", - "\n", - "# Evolve the state by the quantum circuit\n", - "state = state.evolve(circ)\n", - "\n", - "#draw using latex\n", - "state.draw('latex')" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:14.786360Z", - "iopub.status.busy": "2023-08-25T18:25:14.785880Z", - "iopub.status.idle": "2023-08-25T18:25:14.809654Z", - "shell.execute_reply": "2023-08-25T18:25:14.808811Z" - } - }, - "outputs": [ - { - "data": { - "text/latex": [ - "$$\n", - "\n", - "\\begin{bmatrix}\n", - "\\frac{\\sqrt{2}}{2} & 0 & 0 & 0 & 0 & 0 & 0 & \\frac{\\sqrt{2}}{2} \\\\\n", - " \\end{bmatrix}\n", - "$$" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.visualization import array_to_latex\n", - "\n", - "#Alternative way of representing in latex\n", - "array_to_latex(state)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Qiskit also provides a visualization toolbox to allow you to view the state.\n", - "\n", - "Below, we use the visualization function to plot the qsphere and a hinton representing the real and imaginary components of the state density matrix $\\rho$." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:14.812979Z", - "iopub.status.busy": "2023-08-25T18:25:14.812641Z", - "iopub.status.idle": "2023-08-25T18:25:16.221143Z", - "shell.execute_reply": "2023-08-25T18:25:16.220352Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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7Fmam5u59tprWbBgAbW1tQwODrJz5046OztZtWoVO3fuxOf78//v/73vfY+HH36YLVu28NRTT3HllVeycuVKamtrOXr0KDt27MB1XZ577jk2bNhwCb7d7KMAIyIiAjiOg2VZmKZ3913LsrjlllvYvXs3L774Irfffvuf/V5Lly4lGAzyne98h5qaGm666SbP/d///vfcfPPNlJeX09fXRygUmuGVLl+aQhIREQF8Pl9BeAEIBAKsW7cOgGPHjmXHf/3rX2MYxkX/tLS0eF6vo6ODgwcPsmbNGu64446C8AKwYsUKPvvZzzI4OMjBgwcv8TedHbQTr4iIyAU4jsPLL78MwOLFi7PjtbW13HPPPWzfvp1ly5axatWq7L1du3axZ88eNm3axK233up5vR07dgCwZs2aC75vMDh5pEAgoF/V09EUkoiISI50Os0jjzyC67rE43FeffVVjhw5wubNm3nqqac8z/7iF7/gq1/9Kj/5yU+47777suPr16/nN7/5DR0dHbS1tXl+ZuXKlXR2dtLR0THjZ+jq6mLBggVEo1G6u7vx+3WoYz7FOhERkRzpdJqtW7dmrw3D4Jvf/CY//OEPC5595513gMmellz79++nsrKyILzE43Fee+01vvGNb8z4/plMhk2bNpFKpfjxj3+s8DID9cCIiIjkKC8vx3VdbNumu7ubJ598kp///OesXLmSkZERz7MHDhzAMAyWLFmSHRsZGaGjo6Mg1AC89NJL2LY94/SR4zjce++9/O53v+NrX/samzZturRfbhZRgBEREZmGz+ejqamJ+++/n5/97Gfs2bOHH/zgB55nDhw4wLx584hEItmx/fv347oun/jEJwpec8eOHUSjUVasWFFwz3EctmzZwnPPPcfdd9/NT3/600v/pWYRBRgREZGLON+gu3v37uxYV1cXg4ODBUHl7bffBigYTyaTvPLKK9x+++0FjbmO47B582a2b9/Oxo0beeaZZy7JfjOzmf51RERELqK3txeYWhkEF+5/gcIAs2vXLsbHxwumj86Hl2effZYNGzbwy1/+Un0vH4ACjIiICHDo0CESiUTBeCKR4MEHHwRg9erV2fEDBw4AhUHl0KFDAFx11VWe8d/+9reEQiHPsurz00bPPvssd955J7/61a8UXj4grUISEREBnn/+eR5//HFuuOEG2traqKiooKenh507dxKPx1mxYgUPPPBA9vnzFZj8ADMwMADAm2++yZIlS4hGoziOwwsvvJDdXfe8bdu2sX37dsrLy1mwYAEPP/xwwedau3YtV1999YfwjYubAoyIiAjw+c9/nt7eXl5//XX27t3L2NgYlZWVLF26lLvuuostW7Z4elfeeecdKioqCpZKf/nLX+axxx5j3bp1vPTSSyxbtox9+/Zx+vRp1q5d63n2xIkTAIyNjRU0CJ/X1tamADMNbWQnIiLyIXvooYd49NFH6e3tpaGh4aP+OLOCAoyIiMiHrL29nerqavbu3ftRf5RZQwFGREREio5WIYmIiEjRUYARERGRoqMAIyIiIkVHAUZERESKjgKMiIiIFB0FGBERESk6CjAiIiJSdBRgREREpOgowIiIiEjRUYARERGRoqMAIyIiIkVHAUZERESKjgKMiIiIFB0FGBERESk6CjAiIiJSdBRgREREpOgowIiIiEjRUYARERGRoqMAIyIiIkVHAUZERESKzv8DCZhhJf3EnXQAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state.draw('qsphere')" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:16.224440Z", - "iopub.status.busy": "2023-08-25T18:25:16.224082Z", - "iopub.status.idle": "2023-08-25T18:25:16.759515Z", - "shell.execute_reply": "2023-08-25T18:25:16.758825Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAGmCAYAAADh6xL7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA+/0lEQVR4nO3de3RUhbn//8+EkEnITTQIQUPCJVyWoiwOUiw0CSLES71VkYBVQyUErcdWQSEpFFoKiRbFUz1VJCrxQgGlxZ5yNIGaBBNQqIiHeqHhFuMXoqDtDCGQBNi/P/xl6pjrZM9kZmfer7VmLbL3M3s+E1j74Zl9GZthGIYAAAAAwIQQfwcAAAAAYH0MFgAAAABMY7AAAAAAYBqDBQAAAADTGCwAAAAAmMZgAQAAAMA0BgsAAAAApjFYAAAAADCNwQIAAACAaQwWAAAAAExjsAAAAABgGoMF4Adr1qyRzWZzPTIyMkxt79NPP3XbXlJSkneCAgC6FP0BVhbq7wBAoDl8+LAGDhzotiw0NFQXXnihxo8fr0ceeURjxozxymvddNNNGjVqlC699FJT24mLi9PixYslSU8++aQXkgEAWtPUJ9LT0/XWW2/55DXoD7AiBgugFYMHD9aPf/xjSdLJkyf1/vvv67XXXtOmTZu0detWpaSkmH6Nm2++WZmZmaa3ExcXpyVLlkj65tMuAIC10R9gRQwWQCuGDBni2hk3yc/PV05OjhYtWqSysjL/BAMAAAhAXGMBeOCee+6RJL3//vstrt+2bZtuuOEGxcXFyW63Kzk5WQsXLlRdXZ2p13399dc1atQo9erVS2FhYbr88sv1xhtvmNomAMB7SktLZbPZtGTJEm3fvl0TJ05UdHS0+vTpo/vuu0+nTp2SJG3evFlXXnmlIiMj1bdvXz3yyCM6c+ZMp1+X/oBAwmABdEJoaPODfc8884zS0tJUUVGh66+/Xg888IAuvvhiLVu2TJMnT1ZDQ4PHr3P27FnNnDlTU6dO1Xnnnaef/exnmjp1qj766CPdeuut2rlzpzfeDgDAS9577z1NmjRJsbGxys7O1oABA/TMM88oKytL69ev12233abExERlZ2frvPPO029/+1stX77c49ehPyAgGQDcHDp0yJBkpKenN1u3fPlyQ5Jx/fXXuy3/6KOPjNDQUOPyyy83jh8/7rYuLy/PkGSsWLHCtezFF180JBkvvvhim1nmz59vSDJWrVrltnzlypWGJOOOO+5o9pzExEQjMTGxnXcJAOislvpESUmJIcmQZGzatMm1vKGhwbjssssMm81mxMXFGTt37nStczqdxoUXXmicf/75RkNDg2EY9AdYG0csgFbs379fS5Ys0ZIlS/Twww/rqquuUm5urvr27avf/va3brWrVq3SmTNn9NRTT+mCCy5wW/fII4+oT58++sMf/uDR6+/bt09PPPGEbr/9ds2ePdtt3bRp0yRJe/fu7cQ7AwD4ysSJE3XTTTe5fu7Zs6duu+02GYahG264QVdccYVrXXR0tH74wx/q66+/1ueff97h16A/IFBx8TbQigMHDuhXv/qV27J+/frpnXfe0ZAhQ9yWv/vuu5KkoqIi/fWvf222rZ49e+rTTz/16PVfeeUVNTY26uc//3mzdb169ZIk9ejRw6NtAgB8a9SoUc2WxcfHt7vuyJEjzW513hr6AwIVgwXQim/fn/zYsWMqLCzU/PnzdeONN2rnzp2Kiopy1X799deSpGXLlnnt9f/yl7/o/PPP17hx45qta/pka/DgwV57PQCAeTExMc2WNV2X19a6xsbGDr8G/QGBilOhgA7o06eP5s2bp9zcXH3yySdauHCh2/qmZuF0OmUYRquPjqqrq9P//d//6aKLLpLNZmu2fvPmzZKkSZMmmXhXAACroT8gkDFYAB7Izc1V//799fvf/16HDx92Lf/e974n6d+nRJm1Z88enTt3rsXb1NbV1em5555TZGSk61xaAEBwoD8gkDFYAB6IiIjQ/Pnz1djYqKVLl7qW33fffQoNDdV//ud/6rPPPmv2vH/961/64IMPOvw6u3fvlvTNdR7fft7Zs2f105/+1HX9R+/evU28GwCA1dAfEMi4xgLw0OzZs/Xoo4/qpZdeUm5urgYPHqxLL71Uv//973Xvvfdq2LBhuu666zR48GCdOHFCBw8eVFlZmTIzM/Xss8926DWaGsf111+vSZMmacaMGYqIiNDmzZv1ySefaNasWXrooYd8+TYBAAGI/oBAxhELwEPh4eHKycnRmTNn3O4alZWVpR07dujmm2/Wu+++qyeffFKvv/66jh8/rgcffLDFu3e0Zvfu3brgggu0YcMGTZs2TRs2bNDTTz+tyMhIvfTSS1q9enWL59YCALo3+gMCmc3w5IpSAF6x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", - "text/plain": [ - "
" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state.draw('hinton')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Unitary representation of a circuit" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Qiskit's quant_info module also has an operator method which can be used to make a unitary operator for the circuit. This calculates the $2^n \\times 2^n$ matrix representing the quantum circuit. " - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:45.626148Z", - "start_time": "2019-08-10T11:37:45.607840Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:16.762949Z", - "iopub.status.busy": "2023-08-25T18:25:16.762457Z", - "iopub.status.idle": "2023-08-25T18:25:16.769350Z", - "shell.execute_reply": "2023-08-25T18:25:16.768770Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 0.70710678+0.j, 0.70710678+0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0.70710678+0.j, -0.70710678+0.j],\n", - " [ 0. +0.j, 0. +0.j, 0.70710678+0.j,\n", - " 0.70710678+0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0.70710678+0.j, -0.70710678+0.j,\n", - " 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0.70710678+0.j, 0.70710678+0.j,\n", - " 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0.70710678+0.j,\n", - " -0.70710678+0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0.70710678+0.j, 0.70710678+0.j],\n", - " [ 0.70710678+0.j, -0.70710678+0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j]])" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.quantum_info import Operator\n", - "\n", - "U = Operator(circ)\n", - "\n", - "# Show the results\n", - "U.data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## OpenQASM backend" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The simulators above are useful because they provide information about the state output by the ideal circuit and the matrix representation of the circuit. However, a real experiment terminates by _measuring_ each qubit (usually in the computational $|0\\rangle, |1\\rangle$ basis). Without measurement, we cannot gain information about the state. Measurements cause the quantum system to collapse into classical bits. \n", - "\n", - "For example, suppose we make independent measurements on each qubit of the three-qubit GHZ state\n", - "\n", - "$$|\\psi\\rangle = (|000\\rangle +|111\\rangle)/\\sqrt{2},$$\n", - "\n", - "and let $xyz$ denote the bitstring that results. Recall that, under the qubit labeling used by Qiskit, $x$ would correspond to the outcome on qubit 2, $y$ to the outcome on qubit 1, and $z$ to the outcome on qubit 0. \n", - "\n", - "
\n", - "Note: This representation of the bitstring puts the most significant bit (MSB) on the left, and the least significant bit (LSB) on the right. This is the standard ordering of binary bitstrings. We order the qubits in the same way (qubit representing the MSB has index 0), which is why Qiskit uses a non-standard tensor product order.\n", - "
\n", - "\n", - "Recall the probability of obtaining outcome $xyz$ is given by\n", - "\n", - "$$\\mathrm{Pr}(xyz) = |\\langle xyz | \\psi \\rangle |^{2}$$\n", - "\n", - "and as such for the GHZ state probability of obtaining 000 or 111 are both 1/2.\n", - "\n", - "To simulate a circuit that includes measurement, we need to add measurements to the original circuit above, and use a different Aer backend." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:45.840681Z", - "start_time": "2019-08-10T11:37:45.627937Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:16.772517Z", - "iopub.status.busy": "2023-08-25T18:25:16.772049Z", - "iopub.status.idle": "2023-08-25T18:25:17.172077Z", - "shell.execute_reply": "2023-08-25T18:25:17.171227Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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RExkTyQMcNxeqebCenRD8rBbWjznLnPD3Q7SKyX+V2BB5eXlGDduHB588EH897//hUwmw8CBA6HT6fDtt9/iscceQ0VFhSC1NWi12PvTMQDAmKGD4ObGG9GIyHiRvUIwbHBs0+OEAX0RGxkuXEE2JnNzxT2T/uyd8FR44M7xjt8r1dxtwwcjJLBr0+PUscPh5+MlYEWWwzDTzKxZs7Bv3z6EhIRg7969UKlU+PXXX1FUVAS1Wo1XXnkFbm5ugtTW2Cvj5alAooMO1CMi65owZii6+vmgi48XJv9lmNDl2Fxoj25IHm7onbgzZRQ8lTdfssHRuLj8eft5TGQ44vtHCV2SxfBupj8cOXIECQkJcHV1xbFjx9C/f3+Ltf3Ov7eiQlPdiRb0qKishl6vh9xdBplAgYqIxE+r1UIPwNVJp33Q6/Wor2+ATOa8v0fr6hvg6urS5hgaIXl5euDvD91p1rG8VvGHbdu2AQAmTZpk0SADABWaapRrKi3SVk1tXYdzRhAR0c3V1Dn571EHuymWYeYP2dmGe/CHDbN816uXZ2e6MtkrQ0REjq8zfysZZv5QXm5YLt0aUzCb220GAIeO5+DLbw7Ay1OBtNkzOfCXiIjoBvzL+Advb8M8A2VlZRZv2/wxM4ZeGQCor2/A6x9utGxhREREdoJjZiwgNjYWW7duxc8//2zxti0xZoZjZYiIiNrGMPOHadOm4R//+Ad27dqF7OxsxMS0PVukOcy7DsixMkRE5Dw6M2aGt2Y3c88992DTpk0ICwvDf/7zHyQl/bmex++//461a9di/vz5UCqVVq+FY2WIiIiMwzDTTHl5OaZOndq0dHtwcDB69OgBtVoNlUoFvV6P69evw9fX16p1NGi1WP3hRpSWa5D6l+EYkWDZW8WJiIgcCWcAbsbb2xvfffcdPvroI4wZMwZVVVU4ceIEpFIpxo8fj48++gheXtaf+vnIqbMoLddwtl8iIiIjsGfGDh07fQ679/+KpKED2StDRETUAYYZO1Xf0AAJJHB1dc4px4mIiIzFMENERESixjEzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkagwzREREJGoMM0RERCRqDDNEREQkav8PZHu7I2+42HgAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Create a Quantum Circuit\n", - "meas = QuantumCircuit(3, 3)\n", - "meas.barrier(range(3))\n", - "# map the quantum measurement to the classical bits\n", - "meas.measure(range(3), range(3))\n", - "\n", - "# The Qiskit circuit object supports composition.\n", - "# Here the meas has to be first and front=True (putting it before) \n", - "# as compose must put a smaller circuit into a larger one.\n", - "qc = meas.compose(circ, range(3), front=True)\n", - "\n", - "#drawing the circuit\n", - "qc.draw('mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This circuit adds a classical register, and three measurements that are used to map the outcome of qubits to the classical bits. \n", - "\n", - "To simulate this circuit, we use the ``qasm_simulator`` in Qiskit Aer. Each run of this circuit will yield either the bitstring 000 or 111. To build up statistics about the distribution of the bitstrings (to, e.g., estimate $\\mathrm{Pr}(000)$), we need to repeat the circuit many times. The number of times the circuit is repeated can be specified in the ``execute`` function, via the ``shots`` keyword." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:45.868074Z", - "start_time": "2019-08-10T11:37:45.842666Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:17.175675Z", - "iopub.status.busy": "2023-08-25T18:25:17.175320Z", - "iopub.status.idle": "2023-08-25T18:25:17.265782Z", - "shell.execute_reply": "2023-08-25T18:25:17.264859Z" - } - }, - "outputs": [], - "source": [ - "# Adding the transpiler to reduce the circuit to QASM instructions\n", - "# supported by the backend\n", - "from qiskit import transpile \n", - "\n", - "# Use AerSimulator\n", - "from qiskit_aer import AerSimulator\n", - "\n", - "backend = AerSimulator()\n", - "\n", - "# First we have to transpile the quantum circuit \n", - "# to the low-level QASM instructions used by the \n", - "# backend\n", - "qc_compiled = transpile(qc, backend)\n", - "\n", - "# Execute the circuit on the qasm simulator.\n", - "# We've set the number of repeats of the circuit\n", - "# to be 1024, which is the default.\n", - "job_sim = backend.run(qc_compiled, shots=1024)\n", - "\n", - "# Grab the results from the job.\n", - "result_sim = job_sim.result()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once you have a result object, you can access the counts via the function `get_counts(circuit)`. This gives you the _aggregated_ binary outcomes of the circuit you submitted." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:45.873600Z", - "start_time": "2019-08-10T11:37:45.869929Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:17.269811Z", - "iopub.status.busy": "2023-08-25T18:25:17.269329Z", - "iopub.status.idle": "2023-08-25T18:25:17.275494Z", - "shell.execute_reply": "2023-08-25T18:25:17.273234Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'111': 525, '000': 499}\n" - ] - } - ], - "source": [ - "counts = result_sim.get_counts(qc_compiled)\n", - "print(counts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Approximately 50 percent of the time, the output bitstring is 000. Qiskit also provides a function `plot_histogram`, which allows you to view the outcomes. " - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:37:45.991815Z", - "start_time": "2019-08-10T11:37:45.875518Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:17.278795Z", - "iopub.status.busy": "2023-08-25T18:25:17.278544Z", - "iopub.status.idle": "2023-08-25T18:25:17.378422Z", - "shell.execute_reply": "2023-08-25T18:25:17.377664Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnkAAAHICAYAAAAyd/zsAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA0XElEQVR4nO3dd3hUdd7//9dMGiGkAAklgnQIvQkCKhBKEFBcvBFWEQgqrtJULIguBEVABBQU3aUsgV1Uiq6sciMEgYBShBukiF8hSEsIICGQRggp8/uDX2YdkkD6JB+ej+vyunbe5zNn3icX5+xrPnOKxWaz2QQAAACjWJ3dAAAAAIofIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADOTq7AbKu6ysLMXGxsrb21sWi8XZ7QAAAMPZbDYlJSUpMDBQVmve83WEvCKKjY1V7dq1nd0GAAC4w0RHR6tWrVp5LifkFZG3t7ekG39oHx8fJ3cDAABMl5iYqNq1a9szSF4IeUWU/ROtj48PIQ8AAJSa250mxoUXAAAABiLkAQAAGIiQBwCAoaZOnSqLxeLwX1BQkCQpPj5e48aNU5MmTeTp6am7775b48ePV0JCgsM6bn6/xWLRypUrnbE5KCDOyQMAwGDNmzfXd999Z3/t6nrj//pjY2MVGxurOXPmqFmzZjp9+rSee+45xcbG6osvvnBYR3h4uB588EH7az8/v1LpHUVDyAMAwGCurq6qUaNGjnqLFi305Zdf2l83aNBA06dP15NPPqmMjAx7GJRuhLrc1oGyjZ9rAQAwWFRUlAIDA1W/fn0NHTpUZ86cyXNsQkKCfHx8HAKeJI0ZM0b+/v7q2LGjli5dKpvNVtJtoxgwkwcAgKHuvfdeLVu2TE2aNNG5c+f01ltv6YEHHtDPP/+c4x5rcXFxmjZtmp599lmH+ttvv60ePXqoYsWKioiI0OjRo5WcnKzx48eX5qagECw24niRJCYmytfX1/7tBwCAsurKlSuqU6eO3n//fT399NP2emJionr37q0qVaro66+/lpubW57rmDJlisLDwxUdHV0aLSMX+c0e/FwLAMAdws/PT40bN9bx48fttaSkJD344IPy9vbWV199dcuAJ92YHYyJiVFaWlpJt4siIuQBAHCHSE5O1m+//aaaNWtKujEjFBISInd3d3399deqUKHCbddx4MABVa5cWR4eHiXdLoqIc/IAADDUK6+8oocfflh16tRRbGyswsLC5OLioscff9we8K5evaoVK1YoMTFRiYmJkqSAgAC5uLjom2++0YULF9SpUydVqFBBmzZt0owZM/TKK684ecuQH4Q8AAAMFRMTo8cff1yXLl1SQECA7r//fu3evVsBAQGKjIzUjz/+KElq2LChw/tOnjypunXrys3NTR9//LFeeukl2Ww2NWzYUO+//75GjRrljM1BAXHhRRFx4QUAAChNXHgBAABwByPkAQAAGIiQBwAAYCBCHgAAgIEIeQAAAAYi5AEAABiIkAcAAGAgQh6M8u6778pisejFF1+013777TcNHDhQAQEB8vHx0eDBg3XhwgWH9+3fv1+9e/eWn5+fqlatqmeffVbJycml3D0AAMWHkAdj7N27VwsXLlSrVq3stZSUFIWEhMhisWjLli3asWOHrl+/rocfflhZWVmSpNjYWPXq1UsNGzbUjz/+qA0bNujIkSMKDQ110pYAAFB0PNYMRkhOTtbQoUO1ePFivfPOO/b6jh07dOrUKf3000/2u4IvX75clStX1pYtW9SrVy+tW7fO/ugeq/XG956///3vatWqlY4fP57jcT8AAJQHzOTBCGPGjFH//v3Vq1cvh3paWposFos8PDzstQoVKshqteqHH36wj3F3d7cHPEny9PSUJPsYAADKG0Ieyr2VK1dq//79mjlzZo5lnTp1kpeXlyZOnKirV68qJSVFr7zyijIzM3Xu3DlJUo8ePXT+/HnNnj1b169f1+XLl/X6669Lkn0MAADlDSEP5Vp0dLReeOEFffrpp6pQoUKO5QEBAVqzZo2++eYbVapUSb6+vrpy5YratWtnn7lr3ry5li9frrlz56pixYqqUaOG6tWrp+rVqzvM7gEAUJ5YbDabzdlNlGeJiYny9fVVQkKC/ZwvlJ61a9dq4MCBcnFxsdcyMzNlsVhktVqVlpZmXxYXFydXV1f5+fmpRo0aevnll/Xqq686rO/ChQvy8vKSxWKRj4+PVq5cqccee6xUtwkAgFvJb/bgwguUaz179tThw4cdaiNHjlRQUJAmTpzoEP78/f0lSVu2bNHvv/+uAQMG5Fhf9erVJUlLly5VhQoV1Lt37xLsHgCAkkPIQ7nm7e2tFi1aONS8vLxUtWpVez08PFxN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Qy8vL23btk1Tp07Vl19+qa1bt6pmzZp6+eWXFRYWliPgSdLChQvVsmVLLVq0SPPnz1elSpU0cOBATZ8+XQ0aNCjydgMAADhbic7kPfroo9q0aZPDPZBMw0we7kTM5KGs4RiJssTYmbysrCydPXtWy5Yt03/+8x/70ykAAABQegod8qxW6y1voWKz2VS5cmXNnTu3sB8BAACAQip0yOvatWuuIc9qtapy5crq0KGDRo4cqWrVqhWpQQAAABRcoUNe9g2JAQAAUPbkfLYGAAAAyr1iufBix44dOnDggBITE+Xj46M2bdrovvvuK45VAwAAoBCKFPJ27typkSNH6vjx45JuXGyRfZ5eo0aNFB4ers6dOxe9SwAAABRIoUPekSNHFBISoqtXr6p3794KDg5WzZo1df78eW3dulURERHq06ePdu/erWbNmhVnzwAAALiNQoe8t99+W9evX9f69ev14IMPOiybOHGiNmzYoAEDBujtt9/WypUri9woAAAA8q/QF15ERkZq0KBBOQJetgcffFCDBg3S1q1bC90cAAAACqfQIS8hIUH16tW75Zh69eopISGhsB8BAACAQip0yAsMDNTu3btvOebHH39UYGBgYT8CAAAAhVTokDdgwABFRkZq8uTJunbtmsOya9euKSwsTFu3btUjjzxS5CYBAABQMIW+8GLy5Mlat26dZsyYoYULF6pjx46qXr26Lly4oL179+rixYuqX7++Jk+eXJz9AgAAIB8KHfKqVq2q3bt367XXXtPKlSu1fv16+7IKFSpo5MiRmjVrlqpUqVIsjQIAACD/inQzZH9/fy1dulQLFy7Ur7/+an/iRVBQkNzc3IqrRwAAABRQgUPe9OnTlZKSorfeesse5Nzc3NSyZUv7mOvXr+vNN9+Ut7e3Xn/99eLrFgAAAPlSoAsvvvvuO02ZMkVVq1a95Uydu7u7qlatqjfffJP75AEAADhBgULeP//5T1WuXFljx4697dgxY8aoSpUqCg8PL3RzAAAAKJwChbydO3eqV69e8vDwuO1YDw8P9erVSzt27Ch0cwAAACicAoW82NhY1a9fP9/j69Wrp3PnzhW4KQAAABRNgUKe1WpVenp6vsenp6fLai30/ZYBAABQSAVKYIGBgfr555/zPf7nn3/WXXfdVeCmAAAAUDQFCnkPPPCAtmzZolOnTt127KlTp7Rl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", - "text/plain": [ - "
" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.visualization import plot_histogram\n", - "plot_histogram(counts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The estimated outcome probabilities $\\mathrm{Pr}(000)$ and $\\mathrm{Pr}(111)$ are computed by taking the aggregate counts and dividing by the number of shots (times the circuit was repeated). Try changing the ``shots`` keyword in the ``execute`` function and see how the estimated probabilities change." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-10T11:38:18.277518Z", - "start_time": "2019-08-10T11:38:18.224481Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:17.382076Z", - "iopub.status.busy": "2023-08-25T18:25:17.381707Z", - "iopub.status.idle": "2023-08-25T18:25:17.488954Z", - "shell.execute_reply": "2023-08-25T18:25:17.488260Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:17 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "2d89022b9e3f402aba878be49088655e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "35e136f679014307b8bf82c85c927a5c": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "397b50563d9a458ca4ea4a217d5ebb23": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_35e136f679014307b8bf82c85c927a5c", - "placeholder": "​", - "style": "IPY_MODEL_2d89022b9e3f402aba878be49088655e", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/tutorials/circuits/1_getting_started_with_qiskit.ipynb b/docs/tutorials/circuits/1_getting_started_with_qiskit.ipynb deleted file mode 100644 index 6c11f7750b4c..000000000000 --- a/docs/tutorials/circuits/1_getting_started_with_qiskit.ipynb +++ /dev/null @@ -1,872 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Getting Started with Qiskit\n", - "\n", - "Here, we provide an overview of working with Qiskit. The fundamental package of Qiskit is Terra that provides the basic building blocks necessary to program quantum computers. The fundamental unit of Qiskit is the [quantum circuit](https://en.wikipedia.org/wiki/Quantum_circuit). A basic workflow using Qiskit consists of two stages: **Build** and **Execute**. **Build** allows you to make different quantum circuits that represent the problem you are solving, and **Execute** that allows you to run them on different backends. After the jobs have been run, the data is collected and postprocessed depending on the desired output." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:39.026081Z", - "start_time": "2021-07-31T05:04:36.903090Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:19.667525Z", - "iopub.status.busy": "2023-08-25T18:25:19.666946Z", - "iopub.status.idle": "2023-08-25T18:25:20.115340Z", - "shell.execute_reply": "2023-08-25T18:25:20.114540Z" - } - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "from qiskit import *\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Circuit Basics \n", - "\n", - "\n", - "### Building the circuit\n", - "\n", - "The basic element needed for your first program is the QuantumCircuit. We begin by creating a `QuantumCircuit` comprised of three qubits." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:39.030564Z", - "start_time": "2021-07-31T05:04:39.028024Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:20.119293Z", - "iopub.status.busy": "2023-08-25T18:25:20.118740Z", - "iopub.status.idle": "2023-08-25T18:25:20.122171Z", - "shell.execute_reply": "2023-08-25T18:25:20.121599Z" - } - }, - "outputs": [], - "source": [ - "# Create a Quantum Circuit acting on a quantum register of three qubits\n", - "circ = QuantumCircuit(3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After you create the circuit with its registers, you can add gates (\"operations\") to manipulate the registers. As you proceed through the tutorials you will find more gates and circuits; below is an example of a quantum circuit that makes a three-qubit GHZ state\n", - "\n", - "$$|\\psi\\rangle = \\left(|000\\rangle+|111\\rangle\\right)/\\sqrt{2}.$$\n", - "\n", - "To create such a state, we start with a three-qubit quantum register. By default, each qubit in the register is initialized to $|0\\rangle$. To make the GHZ state, we apply the following gates:\n", - "- A Hadamard gate $H$ on qubit $q_{0}$, which puts it into the superposition state $\\left(|0\\rangle+|1\\rangle\\right)/\\sqrt{2}$.\n", - "- A controlled-Not operation ($C_{X}$) between qubit $q_{0}$ and qubit $q_{1}$.\n", - "- A controlled-Not operation between qubit $q_{0}$ and qubit $q_{2}$.\n", - "\n", - "On an ideal quantum computer, the state produced by running this circuit would be the GHZ state above.\n", - "\n", - "In Qiskit, operations can be added to the circuit one by one, as shown below." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:39.584692Z", - "start_time": "2021-07-31T05:04:39.573755Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:20.129462Z", - "iopub.status.busy": "2023-08-25T18:25:20.128288Z", - "iopub.status.idle": "2023-08-25T18:25:20.139072Z", - "shell.execute_reply": "2023-08-25T18:25:20.138471Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Add a H gate on qubit $q_{0}$, putting this qubit in superposition.\n", - "circ.h(0)\n", - "# Add a CX (CNOT) gate on control qubit $q_{0}$ and target qubit $q_{1}$, putting\n", - "# the qubits in a Bell state.\n", - "circ.cx(0, 1)\n", - "# Add a CX (CNOT) gate on control qubit $q_{0}$ and target qubit $q_{2}$, putting\n", - "# the qubits in a GHZ state.\n", - "circ.cx(0, 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Visualize Circuit \n", - "\n", - "You can visualize your circuit using Qiskit `QuantumCircuit.draw()`, which plots the circuit in the form found in many textbooks." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:40.867948Z", - "start_time": "2021-07-31T05:04:40.271129Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:20.143931Z", - "iopub.status.busy": "2023-08-25T18:25:20.142444Z", - "iopub.status.idle": "2023-08-25T18:25:20.780071Z", - "shell.execute_reply": "2023-08-25T18:25:20.779353Z" - }, - "scrolled": true, - "tags": [ - "nbsphinx-thumbnail" - ] - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "circ.draw('mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this circuit, the qubits are put in order, with qubit $q_{0}$ at the top and qubit $q_{2}$ at the bottom. The circuit is read left to right (meaning that gates that are applied earlier in the circuit show up further to the left)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "\n", - "When representing the state of a multi-qubit system, the tensor order used in Qiskit is different than that used in most physics textbooks. Suppose there are $n$ qubits, and qubit $j$ is labeled as $Q_{j}$. Qiskit uses an ordering in which the $n^{\\mathrm{th}}$ qubit is on the left side of the tensor product, so that the basis vectors are labeled as $Q_{n-1}\\otimes \\cdots \\otimes Q_1\\otimes Q_0$.\n", - "\n", - "For example, if qubit $Q_{0}$ is in state 0, qubit $Q_{1}$ is in state 0, and qubit $Q_{2}$ is in state 1, Qiskit would represent this state as $|100\\rangle$, whereas many physics textbooks would represent it as $|001\\rangle$.\n", - "\n", - "This difference in labeling affects the way multi-qubit operations are represented as matrices. For example, Qiskit represents a controlled-X ($C_{X}$) operation with qubit $Q_{0}$ being the control and qubit $Q_{1}$ being the target as\n", - "\n", - "$$C_X = \\begin{pmatrix} 1 & 0 & 0 & 0 \\\\ 0 & 0 & 0 & 1 \\\\ 0 & 0 & 1 & 0 \\\\ 0 & 1 & 0 & 0 \\\\\\end{pmatrix}.$$\n", - "\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simulating circuits using Qiskit Aer \n", - "\n", - "Qiskit Aer is our package for simulating quantum circuits. It provides many different backends for doing a simulation. There is also a basic, Python only, implementation called `BasicAer` in Terra that can be used as a drop-in replacement for `Aer` in the examples below.\n", - "\n", - "### Statevector backend\n", - "\n", - "The most common backend in Qiskit Aer is the `statevector_simulator`. This simulator returns the quantum \n", - "state, which is a complex vector of dimensions $2^n$, where $n$ is the number of qubits \n", - "(so be careful using this as it will quickly get too large to run on your machine)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To run the above circuit using the statevector simulator, first you need to import Aer and then set the backend to `statevector_simulator`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:43.436320Z", - "start_time": "2021-07-31T05:04:43.290274Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:20.785546Z", - "iopub.status.busy": "2023-08-25T18:25:20.784076Z", - "iopub.status.idle": "2023-08-25T18:25:20.987525Z", - "shell.execute_reply": "2023-08-25T18:25:20.986782Z" - } - }, - "outputs": [], - "source": [ - "# Import Aer\n", - "from qiskit import Aer\n", - "\n", - "# Run the quantum circuit on a statevector simulator backend\n", - "backend = Aer.get_backend('statevector_simulator')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have chosen the backend, it's time to compile and run the quantum circuit. In Qiskit we provide the `run` method for this. `run` returns a `job` object that encapsulates information about the job submitted to the backend.\n", - "\n", - "\n", - "
\n", - "Tip: You can obtain the above parameters in Jupyter. Simply place the text cursor on a function and press Shift+Tab.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:44.311305Z", - "start_time": "2021-07-31T05:04:44.306416Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:20.992078Z", - "iopub.status.busy": "2023-08-25T18:25:20.991551Z", - "iopub.status.idle": "2023-08-25T18:25:20.996095Z", - "shell.execute_reply": "2023-08-25T18:25:20.995455Z" - } - }, - "outputs": [], - "source": [ - "# Create a Quantum Program for execution \n", - "job = backend.run(circ)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you run a program, a job object is made that has the following two useful methods: \n", - "`job.status()` and `job.result()`, which return the status of the job and a result object, respectively.\n", - "\n", - "
\n", - "Note: Jobs run asynchronously, but when the result method is called, it switches to synchronous and waits for it to finish before moving on to another task.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:45.848031Z", - "start_time": "2021-07-31T05:04:45.844758Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:20.999118Z", - "iopub.status.busy": "2023-08-25T18:25:20.998876Z", - "iopub.status.idle": "2023-08-25T18:25:21.002106Z", - "shell.execute_reply": "2023-08-25T18:25:21.001528Z" - } - }, - "outputs": [], - "source": [ - "result = job.result()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The results object contains the data and Qiskit provides the method \n", - "`result.get_statevector(circ)` to return the state vector for the quantum circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:46.702758Z", - "start_time": "2021-07-31T05:04:46.697846Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.005152Z", - "iopub.status.busy": "2023-08-25T18:25:21.004723Z", - "iopub.status.idle": "2023-08-25T18:25:21.009123Z", - "shell.execute_reply": "2023-08-25T18:25:21.008565Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Statevector([0.707+0.j, 0. +0.j, 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0.707+0.j],\n", - " dims=(2, 2, 2))\n" - ] - } - ], - "source": [ - "outputstate = result.get_statevector(circ, decimals=3)\n", - "print(outputstate)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Qiskit also provides a visualization toolbox to allow you to view these results.\n", - "\n", - "Below, we use the visualization function to plot the real and imaginary components of the state density matrix $\\rho$.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:48.212557Z", - "start_time": "2021-07-31T05:04:47.532387Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.012129Z", - "iopub.status.busy": "2023-08-25T18:25:21.011684Z", - "iopub.status.idle": "2023-08-25T18:25:21.829854Z", - "shell.execute_reply": "2023-08-25T18:25:21.829167Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.visualization import plot_state_city\n", - "plot_state_city(outputstate)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unitary backend" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Qiskit Aer also includes a `unitary_simulator` that works _provided all the elements in the circuit are unitary operations_. This backend calculates the $2^n \\times 2^n$ matrix representing the gates in the quantum circuit. " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:04:49.855040Z", - "start_time": "2021-07-31T05:04:49.843822Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.838113Z", - "iopub.status.busy": "2023-08-25T18:25:21.837808Z", - "iopub.status.idle": "2023-08-25T18:25:21.845711Z", - "shell.execute_reply": "2023-08-25T18:25:21.845116Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.707+0.j, 0.707-0.j, 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0.707+0.j, -0.707+0.j],\n", - " [ 0. +0.j, 0. +0.j, 0.707+0.j, 0.707-0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, 0. +0.j, 0.707+0.j,\n", - " -0.707+0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, 0. +0.j, 0.707+0.j,\n", - " 0.707-0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0.707+0.j, -0.707+0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0.707+0.j, 0.707-0.j],\n", - " [ 0.707+0.j, -0.707+0.j, 0. +0.j, 0. +0.j, 0. +0.j,\n", - " 0. +0.j, 0. +0.j, 0. +0.j]],\n", - " input_dims=(2, 2, 2), output_dims=(2, 2, 2))\n" - ] - } - ], - "source": [ - "# Run the quantum circuit on a unitary simulator backend\n", - "backend = Aer.get_backend('unitary_simulator')\n", - "job = backend.run(circ)\n", - "result = job.result()\n", - "\n", - "# Show the results\n", - "print(result.get_unitary(circ, decimals=3))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### OpenQASM backend" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The simulators above are useful because they provide information about the state output by the ideal circuit and the matrix representation of the circuit. However, a real experiment terminates by _measuring_ each qubit (usually in the computational $|0\\rangle, |1\\rangle$ basis). Without measurement, we cannot gain information about the state. Measurements cause the quantum system to collapse into classical bits. \n", - "\n", - "For example, suppose we make independent measurements on each qubit of the three-qubit GHZ state\n", - "\n", - "$$|\\psi\\rangle = (|000\\rangle +|111\\rangle)/\\sqrt{2},$$\n", - "\n", - "and let $xyz$ denote the bitstring that results. Recall that, under the qubit labeling used by Qiskit, $x$ would correspond to the outcome on qubit $q_{2}$, $y$ to the outcome on qubit $q_{1}$, and $z$ to the outcome on qubit $q_{0}$. \n", - "\n", - "
\n", - "Note: This representation of the bitstring puts the most significant bit (MSB) on the left, and the least significant bit (LSB) on the right. This is the standard ordering of binary bitstrings. We order the qubits in the same way (qubit representing the MSB has index 0), which is why Qiskit uses a non-standard tensor product order.\n", - "
\n", - "\n", - "Recall the probability of obtaining outcome $xyz$ is given by\n", - "\n", - "$$\\mathrm{Pr}(xyz) = |\\langle xyz | \\psi \\rangle |^{2}$$\n", - "\n", - "and as such for the GHZ state probability of obtaining 000 or 111 are both 1/2.\n", - "\n", - "To simulate a circuit that includes measurement, we need to add measurements to the original circuit above, and use a different Aer backend." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:05:39.326486Z", - "start_time": "2021-07-31T05:05:39.315781Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.848889Z", - "iopub.status.busy": "2023-08-25T18:25:21.848366Z", - "iopub.status.idle": "2023-08-25T18:25:21.859858Z", - "shell.execute_reply": "2023-08-25T18:25:21.859264Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌───┐           ░ ┌─┐      \n",
-       "q_0: ┤ H ├──■────■───░─┤M├──────\n",
-       "     └───┘┌─┴─┐  │   ░ └╥┘┌─┐   \n",
-       "q_1: ─────┤ X ├──┼───░──╫─┤M├───\n",
-       "          └───┘┌─┴─┐ ░  ║ └╥┘┌─┐\n",
-       "q_2: ──────────┤ X ├─░──╫──╫─┤M├\n",
-       "               └───┘ ░  ║  ║ └╥┘\n",
-       "c: 3/═══════════════════╩══╩══╩═\n",
-       "                        0  1  2 
" - ], - "text/plain": [ - " ┌───┐ ░ ┌─┐ \n", - "q_0: ┤ H ├──■────■───░─┤M├──────\n", - " └───┘┌─┴─┐ │ ░ └╥┘┌─┐ \n", - "q_1: ─────┤ X ├──┼───░──╫─┤M├───\n", - " └───┘┌─┴─┐ ░ ║ └╥┘┌─┐\n", - "q_2: ──────────┤ X ├─░──╫──╫─┤M├\n", - " └───┘ ░ ║ ║ └╥┘\n", - "c: 3/═══════════════════╩══╩══╩═\n", - " 0 1 2 " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Create a Quantum Circuit\n", - "meas = QuantumCircuit(3, 3)\n", - "meas.barrier(range(3))\n", - "# map the quantum measurement to the classical bits\n", - "meas.measure(range(3), range(3))\n", - "\n", - "# The Qiskit circuit object supports composition using\n", - "# the compose method.\n", - "circ.add_register(meas.cregs[0])\n", - "qc = circ.compose(meas)\n", - "\n", - "#drawing the circuit\n", - "qc.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This circuit adds a classical register, and three measurements that are used to map the outcome of qubits to the classical bits. \n", - "\n", - "To simulate this circuit, we use the `qasm_simulator` in Qiskit Aer. Each run of this circuit will yield either the bitstring 000 or 111. To build up statistics about the distribution of the bitstrings (to, e.g., estimate $\\mathrm{Pr}(000)$), we need to repeat the circuit many times. The number of times the circuit is repeated can be specified in the `run` method, via the `shots` keyword." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:06:23.358780Z", - "start_time": "2021-07-31T05:06:23.338865Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.863010Z", - "iopub.status.busy": "2023-08-25T18:25:21.862551Z", - "iopub.status.idle": "2023-08-25T18:25:21.900204Z", - "shell.execute_reply": "2023-08-25T18:25:21.899453Z" - } - }, - "outputs": [], - "source": [ - "# Use Aer's qasm_simulator\n", - "backend_sim = Aer.get_backend('qasm_simulator')\n", - "\n", - "# Execute the circuit on the qasm simulator.\n", - "# We've set the number of repeats of the circuit\n", - "# to be 1024, which is the default.\n", - "job_sim = backend_sim.run(transpile(qc, backend_sim), shots=1024)\n", - "\n", - "# Grab the results from the job.\n", - "result_sim = job_sim.result()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once you have a result object, you can access the counts via the function `get_counts(circuit)`. This gives you the _aggregated_ binary outcomes of the circuit you submitted." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:06:24.587309Z", - "start_time": "2021-07-31T05:06:24.583432Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.904233Z", - "iopub.status.busy": "2023-08-25T18:25:21.903719Z", - "iopub.status.idle": "2023-08-25T18:25:21.907737Z", - "shell.execute_reply": "2023-08-25T18:25:21.907084Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'111': 520, '000': 504}\n" - ] - } - ], - "source": [ - "counts = result_sim.get_counts(qc)\n", - "print(counts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Approximately 50 percent of the time, the output bitstring is 000. Qiskit also provides a function `plot_histogram`, which allows you to view the outcomes. " - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:06:26.146850Z", - "start_time": "2021-07-31T05:06:26.028680Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.913010Z", - "iopub.status.busy": "2023-08-25T18:25:21.912571Z", - "iopub.status.idle": "2023-08-25T18:25:22.015255Z", - "shell.execute_reply": "2023-08-25T18:25:22.014511Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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G/KsVqvKly+vdu3a6emnn1blypULVCAAAADyLt8hL3NpMQAAAJQ8pWrtWgAAAOROodx4sXPnTh08eFDx8fHy8fFRq1atdP/99xfGrgEAAJAPBQp5u3bt0tNPP62TJ09KunGzReY8vYCAAC1btkwdO3YseJUAAADIk3yHvKNHjyooKEhXr15Vz549FRgYqGrVqik6Olrbtm1TaGioevXqpT179qhJkyaFWTMAAABuI98h780339T169e1YcMGPfjggw7bJk2apE2bNqlfv3568803tXLlygIXCgAAgNzL940X27dv14ABA7IEvEwPPvigBgwYoG3btuW7OAAAAORPvkNeXFyc6tate8s+devWVVxcXH4/AgAAAPmU75BXvXp17dmz55Z9fv75Z1WvXj2/HwEAAIB8ynfI69evn7Zv364pU6bo2rVrDtuuXbumkJAQbdu2TY888kiBiwQAAEDe5PvGiylTpmj9+vWaOXOmFi1apPbt26tKlSq6cOGC9u3bp4sXL6pevXqaMmVKYdYLAACAXMh3yKtYsaL27Nmjl19+WStXrtSGDRvs28qUKaOnn35as2fPVoUKFQqlUAAAAORegR6G7O/vr6VLl2rRokU6duyYfcWLRo0ayc3NrbBqBAAAQB7lOeTNmDFDSUlJeuONN+xBzs3NTc2bN7f3uX79ul577TV5e3vrlVdeKbxqAQAAkCt5uvHihx9+0NSpU1WxYsVbjtS5u7urYsWKeu2113hOHgAAgBPkKeR9+umnKl++vMaNG3fbvmPHjlWFChW0bNmyfBcHAACA/MlTyNu1a5d69OghDw+P2/b18PBQjx49tHPnznwXBwAAgPzJU8iLiopSvXr1ct2/bt26On/+fJ6LAgAAQMHkKeRZrValpqbmun9qaqqs1nw/bxkAAAD5lKcEVr16df3666+57v/rr7/qrrvuynNRAAAAKJg8hbzOnTtr69atOnPmzG37njlzRlu3btUDDzyQ39oAAACQT3kKeWPHjlVqaqoGDBigmJiYHPtdunRJjz/+uNLS0jR69OgCFwkAAIC8ydPDkNu0aaPnn39e8+fPV5MmTTRq1CgFBgaqRo0akqQ//vhDW7Zs0eLFi3Xx4kVNnDhRbdq0KZLCAQAAkLM8r3gxb948lSlTRnPmzNGMGTM0Y8YMh+02m00uLi6aPHmy3nrrrUIrFAAAALmX55BnsVg0c+ZMDR8+XMuWLdOuXbsUHR0tSapataruv/9+BQcHq379+oVeLAAAAHInzyEvU/369RmpAwAAKKF4iB0AAICBCHkAAAAGIuQBAAAYiJAHAABgIEIeAACAgQh5AAAABiLkAQAAGIiQBwAAYCBCHgAAgIEIeQAAAAYi5AEAABiIkAcAAGAgQh4AAICBCHkAAAAGIuQBAAAYiJAHAABgIEIeAACAgQh5AAAABiLkAQAAGIiQBwAAYCBCHgAAgIEIeQAAAAYi5AEAABiIkAcAAGAgQh4AAICBCHkAAAAGIuQBAAAYiJAHAABgIEIeAACAgQh5AAAABiLkAQAAGIiQBwAAYCBCHgAAgIEIeQAAAAYi5AEAABiIkAcAAGAgQh4AAICBCHkAAAAGIuQBAAAYiJAHAABgIEIeAACAgQh5AAAABiLkAQAAGIiQBwAAYCBCHgAAgIEIeQAAAAYi5AEAABiIkAcAAGAgQh4AAICBCHkAAAAGIuQBAAAYiJAHAABgIEIeAACAgQh5AAAABiLkAQAAGKhEhrzPPvtM//M//6N77rlHHh4eslgsWr58ebZ9p02bJovFkuO/M2fOZPu+zZs3q0uXLvL29paPj48CAwO1ZcuWojsoAACAYuTq7AKy8/rrr+vs2bPy9/dXtWrVdPbs2du+Z9iwYapTp06Wdj8/vyxtn332mYYMGaJKlSopODhYkrRq1Sr17NlTq1ev1oABAwp4BAAAAM5VIkPekiVLFBAQoNq1a+vtt9/W5MmTb/ue4OBgde3a9bb9Ll++rPHjx8vf318HDhxQjRo1JEmTJk1S69atNXr0aPXq1Uve3t4FPQwAAACnKZFf1/bo0UO1a9cukn2vWbNGV65c0fjx4+0BT5Jq1KihcePGKSYmRl9//XWRfDYAAEBxKZEhLz/CwsI0e/ZszZkzR+vWrVNiYmK2/bZv3y5JCgoKyrKtV69ekqQdO3YUWZ0AAADFoUR+XZsfISEhDq/9/Py0YMECDR061KE9PDxckhQQEJBlH5ltmX2yk5KSopSUFPvr+Ph4SVJqaqpSU1MlSVarVS4uLkpPT1dGRoa9b2Z7WlqabDabvd3FxUVWqzXH9hv7dbvl8QPFKfN3PZOr641LSVpamkO7m5ubMjIylJ6ebm+zWCxydXXNsT2n86Zwz6fb184xla5jkiwCSoriOJ9yo9SHvJYtW2rp0qXq2rWrqlWrpujoaK1fv15Tp05VcHCw/Pz81K9fP3v/uLg4SZKvr2+Wffn4+Dj0yc6sWbP0xhtvZGkPDQ1V2bJlJUm1atVS69atdfjwYZ07d87ep2HDhmrUqJH27t2rixcv2ttbtWql2rVrKywsTAkJCfb2jh07qnLlygoNDZXUN5c/EaDobdiwweF1nz59lJycrG3bttnbXF1d1bdvX8XExGj37t32dm9vb3Xr1k0RERE6ePCgvb1SpUq67777FB4eruPHj9vbi+J8+usFMjAwUJ6enhxTKT8myUdASVHU59P+/ftzVYfF9tc/oUqgzBsvli1bZr8TNje2bNminj17qlmzZjp8+LC9vUGDBgoPD1dqaupf/gK8ITU1Ve7u7mrRooUOHTqU7X6zG8mrWbOmYmJi7CGxKP6iHfMBI3koOT4cy0gex1SyjunZBYzkoeRYNKFoz6fY2FhVrFhRcXFx9uyRnVI/kpeT7t27q379+jpy5Iji4+PtP4TMEby4uDhVrFjR4T2ZX71mN8qXycPDQx4eHlna3dzc5ObmGMRcXFzk4uKSpe/N4fJ27TfvF3C2nH4ns2u3Wq2yWrNO/82pPafzpqjPJ46pdB8TUJI463zK8nm56lVK+fv7S5KuXr1qb7vVvLtbzdcDAAAoTYwNeUlJSTp69Ki8vLzsYU+SunTpIkn//zw3R5s3b3boAwAAUFqV6pCXkJCgEydOZGlPTk7WyJEjlZCQoIEDBzoMaw4cOFC+vr56//33FRkZaW+PjIzUwoUL5e/vr/79+xdL/QAAAEWlRM7JW7JkiX766SdJ0pEjR+xtmc+469Spk0aMGKFLly6pUaNGateunRo3bqyqVavqwoUL+uGHHxQZGanmzZtrzpw5DvsuX768Fi5cqCFDhqhNmzYaNGiQpBvLml26dEmrVq1itQsAAFDqlciQ99NPP2nFihUObTt37tTOnTvtr0eMGKEKFSpozJgx2rt3rzZs2KDLly/L09NTjRs31oQJEzRu3Dh5enpm2f/gwYPl7++vmTNnatmyZbJYLGrbtq1ef/119ejRo8iPDwAAoKiV+EeolHTx8fHy9fW97W3MBTVyfpHtGsizj593dgWAI66RKEmK+hqZ2+xRqufkAQAAIHuEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAPd0SFv37596tOnj/z8/OTl5aUOHTpo9erVzi4LAACgwFydXYCzbNu2Tb169VKZMmX097//Xd7e3lq7dq0GDRqkiIgIvfjii84uEQAAIN/uyJG8tLQ0jRw5UlarVWFhYVq8eLHmzZunQ4cOqUGDBnr11Vd19uxZZ5cJAACQb3dkyNu6dat+//13Pfnkk2rVqpW93dfXV6+++qquX7+uFStWOK9AAACAArojQ9727dslSUFBQVm29erVS5K0Y8eO4iwJAACgUN2Rc/LCw8MlSQEBAVm2Va1aVeXKlbP3uVlKSopSUlLsr+Pi4iRJsbGxSk1NlSRZrVa5uLgoPT1dGRkZ9r6Z7WlpabLZbPZ2FxcXWa3WHNtTU1N1/ZpbAY4YKFyXLqU6vHZ1vXEpSUtLc2h3c3NTRkaG0tPT7W0Wi0Wurq45tud03hTm+ZSb2jmm0nVM169ZBJQUV64U7fkUGxsrSQ7nTnbuyJCXGcx8fX2z3e7j42Pvc7NZs2bpjTfeyNJet27dwisQKOE+nezsCgCg5Cqua2RCQkKOWUa6Q0NeQUyePFkTJ060v87IyFBsbKwqVqwoi4W/JEuy+Ph41axZUxEREfLx8XF2OQBQonCNLD1sNpsSEhJUvXr1W/a7I0NeZurNabQuPj5e5cuXz3abh4eHPDw8HNr8/PwKtT4ULR8fHy5gAJADrpGlw61G8DLdkTdeZM7Fy27eXXR0tBITE7OdrwcAAFBa3JEhr0uXLpKk0NDQLNs2b97s0AcAAKA0uiNDXvfu3VWvXj198cUXOnjwoL09Li5OM2fOlLu7u4YOHeq8AlEkPDw8FBISkuXrdgAA10gTWWy3u//WUDkta3b27FnNnTuXZc0AAECpdseGPEnau3evQkJCtGvXLqWmpqp58+aaOHGiBg0a5OzSAAAACuSODnkAAACmuiPn5AEAAJiOkAcAAGAgQh4AAICBCHkAAAAGIuThjpDT/UXcdwQAMBUhD3cEi8WiP/74Q5J0/fp1Xb161d4OAICJXJ1dAFCUbDab1q9fr08++URHjhxRYmKiWrRooebNm6tt27Zq1aqV7r77bnl4eMhmsxH6AADG4Dl5MNrUqVM1d+5clS1bVjVr1lRqaqquX7+uiIgI2Ww2tWzZUgMGDNDQoUNVpUoVZ5cLAEChIeTBWGfOnFHTpk3VtWtXzZs3T40aNVJMTIwiIiL0+++/KywsTJs3b1Z4eLhatmypmTNnqnfv3srIyJDVykwGAEDpRsiDsaZPn6758+dr9erV6t69u9LS0uTq+t8ZCvHx8Tp69KhWr16tBQsWqEqVKtq4caNatWrlvKIBoBhdvnxZhw8fVocOHeTh4eHsclDIGK6AsU6ePCkPDw81a9ZMkuTi4iLpv3fU+vj4qGPHjnr33Xf15ZdfKjk5WaNHj3ZavQBQ3GbOnKnAwEDdd999euutt3T06NEc+2ZeO8PDw3Xo0CGlpqYWV5nIJ0IejNWiRQtFR0frxx9/lHTjTtqMjAyHmysyL1qDBg3So48+qpMnT+r48eNOqRcAitsPP/wgi8WiM2fOaOrUqWrevLm6deumxYsX259IkMlisSgpKUkhISEaNWqUkypGXhDyYKz27dvLy8tLU6ZM0X/+8x9Jss+1s9ls9sCXkZEhSQoICFBycrLi4+OdVjMAFJczZ87ozz//VPv27bV161a9+eab6ty5s3bv3q1Ro0apcePGGjRokNatW6fY2FhJ0m+//abNmzfL09NTbm5uTj4C3A5z8mCkzMehLFmyRKNGjVJGRoZGjhypgQMHqn379vL29nbon5ycrOHDhys0NFQxMTFOqhoAis+2bdvUo0cPjRs3TgsWLJAkJSYmateuXdq0aZO2bNmiI0eOSJKqV6+uxx9/XMnJyVq8eLG+++479e3b15nlIxcIeTBaUlKS/v3vfyskJEQXL15UpUqV1LFjR917771q37692rRpo/DwcC1dulTLli3T+PHjNXfuXGeXDQBF7tChQ3r88cc1fvx4jR8/Xunp6fa5y5IUHR2t7du3a9OmTQoLC9OZM2ckSX5+fvaRPZRshDwY6eYHGyclJWnJkiVatWqV9u3bp/T0dEk35pi4uroqNTVVwcHBmj59uu666y5nlQ0AxSohIUFpaWkqX768vS27x0hFRUVp1qxZ+uCDDzR69Gh98MEHxV0q8oEVL2Ckm1eu8PLy0nPPPaennnpKJ06c0J49e/Tjjz8qPT1dDRo0UOPGjTV8+HAnVQsAznHz1BUp69xlFxcXVa9eXRUqVJAkPfPMM8VaI/KPkTwY588//9SRI0d04sQJJSYmqn379mrUqJH8/f0dvoqQpJSUFIdnQ7G0GQBkderUKf3tb39TQkKCTp8+7exykEuM5MEoGzdu1FtvvaXdu3c7tFeoUEHdu3fXoEGD9PDDD9vvCvPw8HD4aoKABwBZWSwWVa1aVYMGDXJ2KcgDRvJgjIiICHXt2lVJSUkKDg5WYGCgTp06pV9++UWHDh3S4cOHlZKSoiZNmujVV1/VgAED5O7uzugdgDvOzTdZFPX74ByM5MEYixYt0uXLl7VkyRI9+uijDtsiIyO1a9cuffvtt/riiy80ePBgRUZG6uWXXybgAbhjZH5z8dcVgGw2223X675+/brc3d0JeKUMI3kwRocOHeTp6ak1a9bI399faWlpslgsWS5K27Zt04svvqjffvtNH374IZOIAdwxPvroI23fvl1Dhw5Vly5dVK5cOfu2zAfD3y7wofQg5MEIiYmJ6t+/vyIjI7V//36VLVvWYa7dzX+t/vLLL+revbs6d+6sb775hq9sAdwR6tatq7Nnz8rDw0MtW7ZUUFCQ+vTpo3vvvdfhGpiWliZXV1ddvXpVixcvVsuWLRUYGOjEypEfxHUYoVy5cmrbtq2OHz+ulStXSsr612jm64yMDLVu3VoPPPCAjh07prNnzxLwABjv6NGjOnv2rNq2bavOnTtr7969euutt9SzZ0/17t1b7733no4dOyZJcnW9MZvrwIEDmjhxoqZMmeLM0pFPhDwYY/z48WrWrJlGjBihCRMm6MCBA7p27Zqk/941m5aWJqvVqvj4eLm7u+vatWuqXbu2M8sGgGKRuUTZk08+qdDQUB07dkxvv/227r77boWGhur5559Xt27d9OSTT+rf//63Ll++rL1790qSJk+e7MzSkU98XQujrFu3ThMnTtSZM2fUtm1bPfLII+rUqZPq1q2rKlWqqEyZMpKkzz//XC+88IIeffRR/etf/3Jy1QBQ9BYvXqxRo0bp//7v/9S7d2+Hbfv27dOXX36pr776SpGRkZKkgIAAxcfHKzk5WVeuXHFCxSgoQh5KvZvn08XGxmrWrFlavXq1IiIiVKlSJTVr1kzVq1dX2bJllZycrNWrV6tu3bpat26dGjZs6MTqAaDo2Ww2/fzzz1q9erXGjh2r+vXr29v/ev28du2atmzZojVr1mjdunWKj4/X2LFj9f777zurdBQAIQ9GyLxQRUZGqnr16rJarfr111+1fv16bd++Xf/v//0/RURESJLKly+vVq1a6b333lPTpk2dXDkAFJ/ExES5u7vL3d09y7abA9+4ceP04Ycf6sCBA2rVqlUxVonCQshDqZaWlqadO3dq6dKlOnHihCwWi8qWLat27dpp4MCBat26tWw2myIiIpScnKxTp06pUaNGqlmzplxdXbmrFgD+IvOa+Pvvv2vQoEGKi4tTeHi4s8tCPhHyUKrNnTtX06dPV0JCgu6++265uLjo+PHj9u1NmjTRmDFjNGDAAFWuXNmJlQJA6bF+/Xr169dP//jHPzR79mxnl4N8IuSh1Dp9+rSaN2+uNm3aaMWKFXJ3d1eVKlUUHR2t7777TmvWrNH27dslSYGBgZo9e7buuece5xYNAE6U228vLly4oE2bNunhhx9WhQoViqEyFAVCHkqtqVOnatGiRfriiy/UvXt3SVkvYEeOHNHcuXO1evVq1a5dW59//rnatm3rrJIBoFglJyfr3LlzqlWrljw9PfP0XtapLf14Th5KraNHj6pcuXIKCAiQJPsyZjabTenp6ZKk5s2ba8WKFXr77bd14sQJLVy40JklA0CxWrBggQYPHqz58+dr27ZtioqKsl8fc3Lx4kWlpaUR8Azg6uwCgPxq3bq1vv76ayUmJkr67xPa/7pebebI3nPPPacff/xRW7du1alTp1SvXj2n1Q0AxWXhwoWKiorSwYMH5evrq/vuu09BQUG69957Va9ePVWsWNGhf1JSkt555x1dunRJH3/8MUGvlGMkD6VW5jqKTz31lLZu3arr169n2y/zr9aGDRsqJibGHgoBwGQnTpxQXFycOnbsqPnz56tTp076+eefNWHCBD3++ON68cUX9dlnn+no0aP2hx3/+uuv+vjjjxUbG0vAMwAjeSi1OnTooIkTJ+qf//ynxo0bp7Fjx2rAgAGqUqWKvU/mqN7ly5cVGRkpLy8vtWjRwolVA0DxOHHihK5du6agoCCNHTtWDz30kI4fP67du3dr69atWrt2rT7//HM1adJE3bp104MPPqgtW7YoPj5eI0eOdHb5KATceIFSb9GiRZozZ45OnTql6tWrq3///urdu7dq1qwpFxcX+fn56f3339f8+fM1ZswYzZs3z9klA0CR++qrrzRw4ECtXLlSAwcOtLenpqbq7NmzOnTokH788Uf7A+Pd3Nxks9nk4eGh2NhYJ1aOwkLIQ6lns9l08uRJffzxx1q5cqV93cXKlSvLzc1N58+fV0ZGhp544gnNnj1bNWrUcHLFAFD0bDabjh07pjJlyqhu3brZPj4lKSlJJ06c0PHjx7Vs2TJ9//33GjdunN577z0nVY3CRMiDUZKSkrR37159++23ioqK0p9//ikfHx8NHDhQjz32mMqUKePsEgHA6bILfBMmTNDChQu1f/9+tW7d2kmVoTAR8mCs1NRUubm5ObsMACixMjIyZLVadebMGT3yyCO6fPmyzp075+yyUEi4uxbGIuABwK1ZrTdiwB9//KHU1FSNGTPGyRWhMDGSBwDAHc5msykyMlIVKlSQl5eXs8tBISHkAQAAGIivawEAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADPT/AZEvi+WY23ucAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.visualization import plot_histogram\n", - "plot_histogram(counts)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The estimated outcome probabilities $\\mathrm{Pr}(000)$ and $\\mathrm{Pr}(111)$ are computed by taking the aggregate counts and dividing by the number of shots (times the circuit was repeated). Try changing the `shots` keyword in the `run` method and see how the estimated probabilities change." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:06:28.643646Z", - "start_time": "2021-07-31T05:06:27.732825Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.022028Z", - "iopub.status.busy": "2023-08-25T18:25:22.021745Z", - "iopub.status.idle": "2023-08-25T18:25:22.173530Z", - "shell.execute_reply": "2023-08-25T18:25:22.172772Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:22 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "687b3dadafbd4045a78efe6306adb9a9": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "c134416612174084a976174deea42290": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "f12cb5d361304538b0df27dec1026210": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_687b3dadafbd4045a78efe6306adb9a9", - "placeholder": "​", - "style": "IPY_MODEL_c134416612174084a976174deea42290", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/tutorials/circuits/2_plotting_data_in_qiskit.ipynb b/docs/tutorials/circuits/2_plotting_data_in_qiskit.ipynb deleted file mode 100644 index 28b42d64a543..000000000000 --- a/docs/tutorials/circuits/2_plotting_data_in_qiskit.ipynb +++ /dev/null @@ -1,985 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Qiskit Visualizations" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:07:56.673595Z", - "start_time": "2021-07-31T05:07:56.670504Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:12.506650Z", - "iopub.status.busy": "2023-08-25T18:25:12.506365Z", - "iopub.status.idle": "2023-08-25T18:25:13.114975Z", - "shell.execute_reply": "2023-08-25T18:25:13.114242Z" - } - }, - "outputs": [], - "source": [ - "from qiskit import *\n", - "from qiskit.visualization import plot_histogram" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Plot Histogram \n", - "\n", - "To visualize the data from a quantum circuit run on a real device or `qasm_simulator` we have made a simple function \n", - "\n", - "`plot_histogram(data)`\n", - "\n", - "As an example we make a 2-qubit Bell state" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:03.168385Z", - "start_time": "2021-07-31T05:08:03.152732Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:13.119830Z", - "iopub.status.busy": "2023-08-25T18:25:13.119057Z", - "iopub.status.idle": "2023-08-25T18:25:13.173736Z", - "shell.execute_reply": "2023-08-25T18:25:13.172985Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'11': 492, '00': 508}\n" - ] - } - ], - "source": [ - "# quantum circuit to make a Bell state \n", - "bell = QuantumCircuit(2, 2)\n", - "bell.h(0)\n", - "bell.cx(0, 1)\n", - "\n", - "meas = QuantumCircuit(2, 2)\n", - "meas.measure([0,1], [0,1])\n", - "\n", - "# execute the quantum circuit \n", - "backend = BasicAer.get_backend('qasm_simulator') # the device to run on\n", - "circ = bell.compose(meas)\n", - "result = backend.run(transpile(circ, backend), shots=1000).result()\n", - "counts = result.get_counts(circ)\n", - "print(counts)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:04.672500Z", - "start_time": "2021-07-31T05:08:04.216126Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:13.212273Z", - "iopub.status.busy": "2023-08-25T18:25:13.211898Z", - "iopub.status.idle": "2023-08-25T18:25:14.140756Z", - "shell.execute_reply": "2023-08-25T18:25:14.140029Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnkAAAG8CAYAAACixLM7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAw70lEQVR4nO3deXjM9/7//8dMEhGRTRJF7YpQVKjWVsSSWHqcj9ahG6Kt01O0VV1UW0VbVNHW4fR8LG04n1aV008XPpa0JBxbuaj1HMSeCGrNhkgy8/vDL/PtNAnJZJnk5X67LtfVeb1f857na668333M671Z7Ha7XQAAADCK1d0FAAAAoOQR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQJ7uLqCis9lsSk5Olp+fnywWi7vLAQAAhrPb7UpLS1OtWrVktRY8X0fIK6bk5GTVqVPH3WUAAIA7TGJiomrXrl3gckJeMfn5+Um6+UX7+/u7uRoAAGC61NRU1alTx5FBCkLIK6bcQ7T+/v6EPAAAUGZud5oYF14AAAAYiJAHAABgIEIeKrRJkybJYrE4/QsLC3Msv379ukaNGqXg4GBVrVpVjz76qM6dO+e0jh07dqhHjx4KDAxUUFCQoqKitGfPnrIeCgAAJYqQhwrv3nvv1ZkzZxz/Nm3a5Fj28ssva8WKFVq+fLk2bNig5ORkPfLII47l6enp6t27t+rWrauff/5ZmzZtkp+fn6KiopSVleWO4QAAUCK48AIVnqenp2rUqJGnPSUlRZ999pmWLFmi7t27S5JiYmLUrFkzbdu2Te3bt9fBgwd16dIlvfvuu45b4UycOFGtWrXSyZMndc8995TpWAAAKCnM5KHCS0hIUK1atdSwYUM9+eSTOnXqlCRp586dysrKUs+ePR19w8LCVLduXW3dulWS1LRpUwUHB+uzzz7TjRs3dO3aNX322Wdq1qyZ6tev747hAABQIgh5qNAefPBBLVq0SGvWrNHf//53HT9+XA899JDS0tJ09uxZVapUSYGBgU7vueuuu3T27FlJN+9zGB8fry+++EI+Pj6qWrWq1qxZo9WrV8vTk4luAEDFxf/FUKH16dPH8d+tWrXSgw8+qHr16mnZsmXy8fG57fuvXbumZ555Rp06ddJXX32lnJwczZw5U/369dOOHTsKtQ4AAMojQh6MEhgYqCZNmujIkSPq1auXbty4oStXrjjN5p07d85xDt+SJUt04sQJbd261fH8vyVLligoKEjff/+9HnvsMXcMAwCAYuNwLYySnp6uo0ePqmbNmmrbtq28vLy0bt06x/JDhw7p1KlT6tChgyTp6tWrslqtTncNz31ts9nKvH4AAEoKIQ8V2quvvqoNGzboxIkT2rJliwYMGCAPDw89/vjjCggI0DPPPKOxY8cqLi5OO3fu1PDhw9WhQwe1b99ektSrVy9dvnxZo0aN0n/+8x8dOHBAw4cPl6enpyIiItw8OgAAXMfhWlRoSUlJevzxx3Xx4kWFhoaqc+fO2rZtm0JDQyVJH3/8saxWqx599FFlZmYqKipKn376qeP9YWFhWrFihSZPnqwOHTrIarUqPDxca9asUc2aNd01LAAAis1it9vt7i6iIktNTVVAQIBSUlLk7+/v7nIAAIDhCps9OFwLAMAd4oMPPpDFYtGYMWMcbUePHtWAAQMUGhoqf39/DRo0yOnxjydOnNAzzzyjBg0ayMfHR40aNdLEiRN148YNN4wARUHIAwDgDrBjxw7NmzdPrVq1crRlZGQoMjJSFotF69ev1+bNm3Xjxg394Q9/cFx8dvDgQdlsNs2bN08HDhzQxx9/rP/+7//Wm2++6a6hoJA4Jw8AAMOlp6frySef1IIFC/T+++872jdv3qwTJ07ol19+cRz2W7x4sYKCgrR+/Xr17NlTvXv3Vu/evR3vadiwoQ4dOqS///3vmjlzZpmPBYXHTB4AAIYbNWqU+vXr5/SYR0nKzMyUxWKRt7e3o61y5cqyWq3atGlTgetLSUlRtWrVSq1elAxCHgAABlu6dKl27dqladOm5VnWvn17+fr6aty4cbp69aoyMjL06quvKicnR2fOnMl3fUeOHNGcOXP03HPPlXbpKCZCHgAAhkpMTNRLL72kL7/8UpUrV86zPDQ0VMuXL9eKFStUtWpVBQQE6MqVK2rTpo3jKUC/dfr0afXu3Vt/+tOfNGLEiLIYAoqBc/IAADDUzp079euvv6pNmzaOtpycHG3cuFFz585VZmamIiMjdfToUV24cEGenp4KDAxUjRo11LBhQ6d1JScnKyIiQh07dtT8+fPLeihwASEPAABD9ejRQ/v27XNqGz58uMLCwjRu3Dh5eHg42kNCQiRJ69ev16+//qr+/fs7lp0+fVoRERFq27atYmJi8p3lQ/lDyAMAwFB+fn5q0aKFU5uvr6+Cg4Md7TExMWrWrJlCQ0O1detWvfTSS3r55ZfVtGlTSTcDXrdu3VSvXj3NnDlT58+fd6yrRo0aZTcYFBkhDwCAO9ihQ4c0fvx4Xbp0SfXr19dbb72ll19+2bH8xx9/1JEjR3TkyBHVrl3b6b08NKt847FmxcRjzQAAQFnisWYAAAB3MEIeAACAgQh5AAAABiLkAQAAGIiQBwAAYCBCHgAAgIEIeQAAAAYi5AEAABiIJ15UECM+cXcFwP+zYIy7KwAA3A4zeQAAAAYi5AEAABiIkAcAAGAgQh4AAICBCHkAAAAGIuQBAAAYiJAHAABgIEIeAACAgQh5AAAABiLkAQAAGIjHmgEAKjwe/YjypLw8+pGZPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAA1WIkDd9+nRZLBZZLBZt27bNadmkSZMcy/L7d+LEiXzXuXbtWnXt2lV+fn7y9/dXRESE1q1bVwajAQAAKH2e7i7gdvbv36+JEyfK19dXGRkZBfYbNmyY6tevn6c9MDAwT9sXX3yhIUOGKDQ0VNHR0ZKkr7/+Wr169dKyZcs0cODAEqoeAADAPcp1yMvKytKwYcPUunVrNW7cWF988UWBfaOjo9WtW7fbrvPy5ct64YUXFBISol27dql27dqSpHHjxik8PFzPP/+8oqKi5OfnV1LDAAAAKHPl+nDtlClTdODAAX3++efy8PAokXUuX75cV65c0QsvvOAIeJJUu3ZtjR49WhcuXNC3335bIp8FAADgLuU25O3atUtTpkzRxIkT1bx589v237hxo6ZPn64ZM2bou+++U3p6er794uPjJUmRkZF5lkVFRUmSNmzY4HrhAAAA5UC5PFybmZmpoUOHqnXr1nr99dcL9Z6JEyc6vQ4MDNTs2bM1dOhQp/aEhARJUuPGjfOsI7ctt09BtWVmZjpep6amSrp5aDkrK0uSZLVa5eHhoZycHNlsNkff3Pbs7GzZ7XZHu4eHh6xWa4HtN9frdcvxA2Up9289l6fnzV1Jdna2U7uXl5dsNptycnIcbRaLRZ6engW2F7TdlOz2dPvaGVPFGpNkEVBelMX2VBjlMuS98847SkhI0M6dO297mPa+++7T559/rm7duqlmzZo6e/asVq5cqXfeeUfR0dEKDAxU//79Hf1TUlIkSQEBAXnW5e/v79QnP9OmTdPkyZPztMfGxqpKlSqSpLp16yo8PFx79+7VqVOnHH2aNm2qsLAwbd++XefPn3e0t27dWvXq1dPGjRuVlpbmaO/QoYOqV6+u2NhYSf1u+T0AZWnVqlVOr/v27atr164pLi7O0ebp6al+/frpwoUL2rp1q6Pdz89P3bt3V2Jionbv3u1oDw0NVceOHZWQkKBDhw452ktje/rtDjIiIkI+Pj6MqYKPSfIXUF6U9va0c+fOQtVhsf/2J1Q5sHXrVnXu3FmTJk3ShAkTHO3R0dFavHixtm7dqvbt2992PevWrVOvXr3UokUL7d2719HepEkTJSQkKCsr6ze/AG/KyspSpUqV1KpVK+3Zsyff9eY3k1enTh1duHDBERJL4xftyL8xk4fy49NRzOQxpvI1pj/PZiYP5ce8F0t3e7p06ZKCg4OVkpLiyB75KVczednZ2Ro2bJhatWqlN954o1jr6tGjhxo1aqR9+/YpNTXV8SXkzuClpKQoODjY6T25h17zm+XL5e3tLW9v7zztXl5e8vJyDmIeHh75zkT+Plzerv336wXcraC/yfzarVarrNa8p/8W1F7QdlPa2xNjqthjAsoTd21PeT6vUL3KSHp6uhISErR7925VqlTJ6abGixcvlnRzKt9isei777677fpCQkIkSVevXnW03eq8u1udrwcAAFCRlKuZPG9vbz3zzDP5Ltu4caMSEhLUv39/hYaG5nvj49/KyMjQgQMH5Ovr6wh7ktS1a1d99dVXio2NzXPYd+3atY4+AAAAFVm5Cnk+Pj5auHBhvsuio6OVkJCg8ePHO8JZWlqazpw5oyZNmjj1vXbtmkaMGKG0tDQNHz7caVpz0KBBGjdunObMmaOnn37aca+8pKQkzZ07VyEhIRowYEApjRAAAKBslKuQV1QXL15UWFiY2rVrp2bNmqlGjRo6d+6cfvrpJyUlJally5aaMWOG03uCgoI0d+5cDRkyRG3atNHgwYMl3Xys2cWLF/X111/ztAsAAFDhVeiQV61aNY0cOVLbt2/XqlWrdPnyZfn4+KhZs2Z68cUXNXr0aPn4+OR531NPPaWQkBBNnTpVMTExslgsatu2rd5++2317NnTDSMBAAAoWeXuFioVTWpqqgICAm57GXNxjfik1FYNFNmCMe6uAHDGPhLlSWnvIwubPcrV1bUAAAAoGYQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAM5HLI27hxo06dOnXLPomJidq4caOrHwEAAAAXuRzyIiIitGjRolv2+cc//qGIiAhXPwIAAAAucjnk2e322/ax2WyyWCyufgQAAABcVKrn5CUkJCggIKA0PwIAAAD58CxK56efftrp9XfffacTJ07k6ZeTk+M4H69Pnz7FKhAAAABFV6SQ99tz8CwWi3bv3q3du3fn29disahdu3b6+OOPi1MfAAAAXFCkw7XHjx/X8ePHdezYMdntdo0ZM8bR9tt/p06dUmpqqrZt26Z77rmnSAVdv35dY8eOVZcuXVSrVi1VrlxZNWrUUKdOnRQTE6OsrKw870lNTdXYsWNVr149eXt7q379+nrttdeUnp6e72fYbDbNmTNHLVu2lI+Pj0JDQ/X444/r2LFjRaoVAACgvLLYC3MFRT4WL16s8PBwtWrVqkQLunDhgurUqaMHHnhATZo0UWhoqC5fvqzVq1fr5MmTioyM1OrVq2W13synGRkZ6ty5s3bv3q3IyEiFh4frl19+UWxsrNq1a6eNGzeqcuXKTp8xYsQILVy4UPfee6/69eun5ORkLVu2TFWrVtW2bdvUuHHjQtebmpqqgIAApaSkyN/fv0S/C6eaPym1VQNFtmCMuysAnLGPRHlS2vvIwmaPIh2u/a1hw4a5+tZbqlatmlJSUlSpUiWn9uzsbPXq1UuxsbFavXq1+vXrJ0n68MMPtXv3bo0bN04ffPCBo/8bb7yh6dOn6+OPP9b48eMd7XFxcVq4cKG6dOmiH3/80fE5TzzxhPr27avRo0dr7dq1pTI2AACAsuJyyMu1fft27dixQ1euXFFOTk6e5RaLRRMmTCj0+qxWa56AJ0menp4aMGCA4uPjdeTIEUk3b+OycOFCVa1aNc9nTJgwQX/729+0cOFCp5C3YMECSdJ7773n9Dl9+vRRt27dFBsbq1OnTqlu3bqFrhkAAKC8cTnkXbp0Sf/1X/+lzZs33/KeeUUNeQWx2Wxas2aNJKlFixaSbt6iJTk5WVFRUfL19XXq7+vrq06dOmnt2rVKTExUnTp1JEnx8fGOZb8XFRWl+Ph4bdiwQUOGDCl2zQAAAO7icsgbO3asNm3apG7dumnYsGGqXbu2PD2LPTHocOPGDU2dOlV2u10XL17UunXrdPDgQQ0fPlw9evSQdDPkSSrwHLrGjRtr7dq1SkhIUJ06dZSRkaEzZ86oRYsW8vDwyLf/b9ebn8zMTGVmZjpep6amSpKysrIcF4VYrVZ5eHgoJydHNpvN0Te3PTs72ykYe3h4yGq1Fth+c71et/y+gLL0+wugcrf97Oxsp3YvLy/ZbDanWX6LxSJPT88C2wvabkp2e7p97YypYo1J4sb7KD/KYnsqDJdT2cqVK/XAAw9o3bp1pfJUixs3bmjy5MmO1xaLRa+++qqmTZvmaEtJSZGkAm+4nHsyYm6/ovbPz7Rp05zqyhUbG6sqVapIkurWravw8HDt3bvX6fm+TZs2VVhYmLZv367z58872lu3bq169epp48aNSktLc7R36NBB1atXV2xsrKR+BdYElLVVq1Y5ve7bt6+uXbumuLg4R5unp6f69eunCxcuaOvWrY52Pz8/de/eXYmJiU63YAoNDVXHjh2VkJCgQ4cOOdpLY3v67Q4yIiJCPj4+jKmCj0kqvQvfgKIq7e1p586dharD5atrfX19NWrUKH344YeuvL3QbDabkpOTtWLFCr355pu69957tWrVKvn7+2vJkiV68skn9dZbb+n999/P89633npLU6dO1f/+7/9qwIABSk5O1t13361OnTpp06ZNefr/+OOPioyM1IsvvqjZs2fnW09+M3l16tTRhQsXHCGxNH7RjvwbM3koPz4dxUweYypfY/rzbGbyUH7Me7F0t6dLly4pODi49K6ubd26db5PuyhpVqtVtWvX1vPPP6+QkBANGjRIU6ZM0fTp0x0zcgXNvOUeSs3tV9T++fH29pa3t3eedi8vL3l5OQcxDw+PfA8LF3RYu6D2368XcLeC/ibza7darY5bHhWmvaDtprS3J8ZUsccElCfu2p7yfF6heuVj4sSJ+uGHH7Rt2zZXV1FkkZGRkm5ePCHd/hy635+z5+vrq5o1a+r48eP5Xgl8u3P8AAAAKgqXZ/LOnj2rfv36qWvXrnryySfVpk2bAqcMhw4d6nKBv5WcnCzp//2Sa9y4sWrVqqXNmzcrIyPD6QrbjIwMbd68WQ0aNHBcWStJXbt21dKlS7V582Z16dLFaf2598f7fTsAAEBF43LIi46OlsVikd1u16JFi7Ro0aI8F2DY7XZZLJYihbx///vfql+/vuMihlxXr17V2LFjJd080Va6eSz72Wef1bvvvqv33nvP6WbI7733ntLT0/Xmm286refPf/6zli5dqgkTJjjdDHn16tWKj49XZGSk6tWrV/gvAgAAoBxyOeTFxMSUZB0Oy5Yt00cffaTOnTurfv368vf31+nTp7V69WpdvHhRDz30kF5++WVH/9dff13ff/+9pk+frl9++UVt2rTRrl27HI81GzNmjNP6IyIi9Oyzz2rhwoVq06aN+vXrpzNnzujrr79WtWrVNGfOnFIZFwAAQFkqd481e/jhh5WcnKwtW7Zo69atSk9PV0BAgFq1aqXHHntMTz/9tNMJh76+vtqwYYMmTZqkb775RnFxcapZs6ZeeeUVTZw4UT4+Pnk+Y968eWrZsqXmz5+v2bNnq2rVqhowYICmTJmiRo0alcq4AAAAypLLt1DBTYV9SHBx8fBtlCel/fBtoKjYR6I8Ke19ZGGzh8szeb+9Od/t8BxYAACAsuVyyKtfv36hnnRhsVgK/fgNAAAAlAyXQ97QoUPzDXkpKSnas2ePjh8/rq5du6p+/frFqQ8AAAAucDnkLVq0qMBldrtds2bN0ocffqjPPvvM1Y8AAACAi1x+4sWtWCwWvfrqq7r33nv12muvlcZHAAAA4BZKJeTluv/++7V+/frS/AgAAADko1RD3tGjR7noAgAAwA1cPievIDabTadPn9aiRYv0/fffq0ePHiX9EQAAALgNl0Oe1Wq95S1U7Ha7goKCNGvWLFc/AgAAAC5yOeR16dIl35BntVoVFBSkdu3aafjw4apevXqxCgQAAEDRuRzy4uPjS7AMAAAAlKRSvfACAAAA7lEiF15s3rxZu3fvVmpqqvz9/dW6dWt16tSpJFYNAAAAFxQr5G3ZskXDhw/XkSNHJN282CL3PL3GjRsrJiZGHTp0KH6VAAAAKBKXQ96BAwcUGRmpq1evqlevXoqIiFDNmjV19uxZxcXFKTY2VlFRUdq2bZuaN29ekjUDAADgNlwOee+++65u3LihVatWqXfv3k7Lxo0bpzVr1qh///569913tXTp0mIXCgAAgMJz+cKL+Ph4DRw4ME/Ay9W7d28NHDhQcXFxLhcHAAAA17gc8lJSUtSgQYNb9mnQoIFSUlJc/QgAAAC4yOWQV6tWLW3btu2WfX7++WfVqlXL1Y8AAACAi1wOef3791d8fLwmTJig69evOy27fv26Jk6cqLi4OP3xj38sdpEAAAAoGpcvvJgwYYJWrlypqVOnat68eXrggQd011136dy5c9qxY4fOnz+vhg0basKECSVZLwAAAArB5ZAXHBysbdu26fXXX9fSpUu1atUqx7LKlStr+PDhmj59uqpVq1YihQIAAKDwinUz5JCQEH3++eeaN2+eDh486HjiRVhYmLy8vEqqRgAAABRRkUPelClTlJGRocmTJzuCnJeXl1q2bOnoc+PGDb311lvy8/PTG2+8UXLVAgAAoFCKdOHFTz/9pHfeeUfBwcG3nKmrVKmSgoOD9dZbb3GfPAAAADcoUsj7xz/+oaCgII0ePfq2fUeNGqVq1aopJibG5eIAAADgmiKFvC1btqhnz57y9va+bV9vb2/17NlTmzdvdrk4AAAAuKZIIS85OVkNGzYsdP8GDRrozJkzRS4KAAAAxVOkkGe1WpWVlVXo/llZWbJaXb7fMgAAAFxUpARWq1Yt7d+/v9D99+/fr7vvvrvIRQEAAKB4ihTyHnroIa1fv14nTpy4bd8TJ05o/fr16tKli6u1AQAAwEVFCnmjRo1SVlaWBg4cqAsXLhTY7+LFi/rTn/6k7OxsPf/888UuEgAAAEVTpJsht2nTRmPGjNEnn3yi5s2b6y9/+YsiIiJUu3ZtSdLp06e1bt06zZ8/X+fPn9fYsWPVpk2bUikcAAAABSvyEy9mzZqlypUra8aMGZoyZYqmTJnitNxut8vDw0Pjx4/X+++/X2KFAgAAoPCKHPIsFoumTp2qZ555RjExMdqyZYvOnj0rSapRo4Y6deqk6OhoNWrUqMSLBQAAQOEUOeTlatSoETN1AAAA5RQ3sQMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMVC5D3hdffKHnnntO999/v7y9vWWxWLRo0aJ8+06aNEkWi6XAfydOnMj3fWvXrlXXrl3l5+cnf39/RUREaN26daU3KAAAgDLk6e4C8vP222/r5MmTCgkJUc2aNXXy5MnbvmfYsGGqX79+nvbAwMA8bV988YWGDBmi0NBQRUdHS5K+/vpr9erVS8uWLdPAgQOLOQIAAAD3Kpchb+HChWrcuLHq1aunDz74QOPHj7/te6Kjo9WtW7fb9rt8+bJeeOEFhYSEaNeuXapdu7Ykady4cQoPD9fzzz+vqKgo+fn5FXcYAAAAblMuD9f27NlT9erVK5V1L1++XFeuXNELL7zgCHiSVLt2bY0ePVoXLlzQt99+WyqfDQAAUFbKZchzxcaNGzV9+nTNmDFD3333ndLT0/PtFx8fL0mKjIzMsywqKkqStGHDhlKrEwAAoCyUy8O1rpg4caLT68DAQM2ePVtDhw51ak9ISJAkNW7cOM86ctty++QnMzNTmZmZjtepqamSpKysLGVlZUmSrFarPDw8lJOTI5vN5uib256dnS273e5o9/DwkNVqLbD95nq9bjl+oCzl/q3n8vS8uSvJzs52avfy8pLNZlNOTo6jzWKxyNPTs8D2grabkt2ebl87Y6pYY5IsAsqLstieCqPCh7z77rtPn3/+ubp166aaNWvq7NmzWrlypd555x1FR0crMDBQ/fv3d/RPSUmRJAUEBORZl7+/v1Of/EybNk2TJ0/O0x4bG6sqVapIkurWravw8HDt3btXp06dcvRp2rSpwsLCtH37dp0/f97R3rp1a9WrV08bN25UWlqao71Dhw6qXr26YmNjJfUr5DcClL5Vq1Y5ve7bt6+uXbumuLg4R5unp6f69eunCxcuaOvWrY52Pz8/de/eXYmJidq9e7ejPTQ0VB07dlRCQoIOHTrkaC+N7em3O8iIiAj5+Pgwpgo+JslfQHlR2tvTzp07C1WHxf7bn1DlUO6FFzExMY4rYQtj3bp16tWrl1q0aKG9e/c62ps0aaKEhARlZWX95hfgTVlZWapUqZJatWqlPXv25Lve/Gby6tSpowsXLjhCYmn8oh35N2byUH58OoqZPMZUvsb059nM5KH8mPdi6W5Ply5dUnBwsFJSUhzZIz8VfiavID169FCjRo20b98+paamOr6E3Bm8lJQUBQcHO70n99BrfrN8uby9veXt7Z2n3cvLS15ezkHMw8NDHh4eefr+Plzerv336wXcraC/yfzarVarrNa8p/8W1F7QdlPa2xNjqthjAsoTd21PeT6vUL0qqJCQEEnS1atXHW23Ou/uVufrAQAAVCTGhryMjAwdOHBAvr6+jrAnSV27dpWk//88N2dr16516gMAAFBRVeiQl5aWpsOHD+dpv3btmkaMGKG0tDQNGjTIaVpz0KBBCggI0Jw5c5SUlORoT0pK0ty5cxUSEqIBAwaUSf0AAAClpVyek7dw4UJt2rRJkrRv3z5HW+497jp37qxnn31WFy9eVFhYmNq1a6dmzZqpRo0aOnfunH766SclJSWpZcuWmjFjhtO6g4KCNHfuXA0ZMkRt2rTR4MGDJd18rNnFixf19ddf87QLAABQ4ZXLkLdp0yYtXrzYqW3z5s3avHmz4/Wzzz6ratWqaeTIkdq+fbtWrVqly5cvy8fHR82aNdOLL76o0aNHy8fHJ8/6n3rqKYWEhGjq1KmKiYmRxWJR27Zt9fbbb6tnz56lPj4AAIDSVu5voVLepaamKiAg4LaXMRfXiE9KbdVAkS0Y4+4KAGfsI1GelPY+srDZo0KfkwcAAID8EfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQIQ8AAAAAxHyAAAADETIAwAAMBAhDwAAwECEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQHd0yNuxY4f69u2rwMBA+fr6qn379lq2bJm7ywIAACg2T3cX4C5xcXGKiopS5cqV9dhjj8nPz0/ffPONBg8erMTERL3yyivuLhEAAMBld+RMXnZ2tkaMGCGr1aqNGzdq/vz5mjVrlvbs2aMmTZrozTff1MmTJ91dJgAAgMvuyJC3fv16HT16VE888YRat27taA8ICNCbb76pGzduaPHixe4rEAAAoJjuyJAXHx8vSYqMjMyzLCoqSpK0YcOGsiwJAACgRN2RIS8hIUGS1Lhx4zzLatSooapVqzr6AAAAVER35IUXKSkpkm4ens2Pv7+/o8/vZWZmKjMzM8+6Ll26pKysLEmS1WqVh4eHcnJyZLPZHH1z27Ozs2W32x3tHh4eslqtBbZnZWXpxnUvF0cLlLyLF7OcXnt63tyVZGdnO7V7eXnJZrMpJyfH0WaxWOTp6Vlge0HbTUluT4WpnTFVrDHduG4RUF5cuVK629OlS5ckyWnbyc8dGfKKY9q0aZo8eXKe9gYNGrihGsA9/jHe3RUAQPlVVvvItLS0AiespDs05OV+IQXN1qWmpiooKCjfZePHj9fYsWMdr202my5duqTg4GBZLPySLM9SU1NVp04dJSYmyt/f393lAEC5wj6y4rDb7UpLS1OtWrVu2e+ODHm55+IlJCSobdu2TsvOnj2r9PR0PfDAA/m+19vbW97e3k5tgYGBpVInSoe/vz87MAAoAPvIiuFWM3i57sgLL7p27SpJio2NzbNs7dq1Tn0AAAAqIov9dmftGSg7O1tNmzbV6dOntW3bNse98lJSUvTAAw/oxIkTOnTokOrXr+/WOlGyUlNTFRAQoJSUFH6lAsDvsI80zx15uNbT01MLFy5UVFSUunTp4vRYs5MnT2rmzJkEPAN5e3tr4sSJeQ63AwDYR5rojpzJy7V9+3ZNnDhRW7ZsUVZWllq2bKmxY8dq8ODB7i4NAACgWO7okAcAAGCqO/LCCwAAANMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfJgtNyLx+12u7iQHABwJ+EWKgAAAAa6I594gTvDr7/+qn379ikhIUFpaWl68MEH1bRpUwUHB8vT8+afvs1mk9XKhDYAwDyEPBhp9erVmjJlirZs2eLUHhwcrB49emjw4MF6+OGH5eXl5aYKAcD9cnJy5OHh4e4yUEo4XAvjJCYmqlu3bsrIyFB0dLQiIiJ07Ngx/fLLL9qzZ4/27t2rzMxMNWvWTG+99ZYeffRReXt7y263y2KxuLt8ACh1vz+KkXve8u2ObLCfrFiYyYNx5s2bp8uXL2vhwoV65JFHnJYlJSVpy5Yt+uGHH7RkyRI99dRTSkpK0uuvv86OC8AdY968eYqPj9fQoUPVtWtXVa1a1bEPtNlskpRv4GM/WbEwkwfjtG/fXj4+Plq+fLlCQkKUnZ0ti8WS55BEXFycXnnlFf373//Wp59+qqefftpNFQNA2WrQoIFOnjwpb29v3XfffYqMjFTfvn314IMPOgW57OxseXp66urVq5o/f77uu+8+RUREuLFyFAUhD0ZJT0/XgAEDlJSUpJ07d6pKlSpOhyV+f0jil19+UY8ePfTQQw/p+++/51AEAOMdOHBALVu2VNu2bRUUFKSffvpJkuTr66tOnTqpb9++ioyMVFhYmOM9mzZtUpcuXdSxY0dt2rTJXaWjiLisEEapWrWq2rZtq0OHDmnp0qWS8h5yyH1ts9kUHh6uLl266ODBgzp58iQBD4Dx9u3bJ0l64oknFBsbq4MHD+qDDz7QPffco9jYWI0ZM0bdu3fXE088of/5n//R5cuXtX37dknS+PHj3Vk6ioiZPBjn9OnT6tOnj/bv36/Ro0crOjpazZs3V+XKlR19cg9BpKam6tlnn9XPP/+skydPurFqACgb8+fP11/+8hf93//9n/r06eO0bMeOHfrqq6/0z3/+U0lJSZKkxo0bKzU1VdeuXdOVK1fcUDFcxUwejHP33Xfr3XffVf369TV37lw999xzmjlzpuLj43Xy5Eldv37dcZ+8FStWKD4+Ps+ODgBMZLfb1apVK40ZM0ZNmjRxapekdu3a6aOPPtLhw4e1YsUKDR06VOfOndO5c+c0ZMgQd5UNFzGTB2P8/ny6S5cuadq0aVq2bJkSExMVGhqqFi1aqFatWqpSpYquXbumZcuWqUGDBvruu+/UtGlTN1YPAGUnPT1dlSpVUqVKlfIs+/2+dPTo0fr000+1a9cutW7dugyrRHER8mCU3J1TUlKSatWqJavVqv3792vlypWKj4/Xf/7zHyUmJkqSgoKC1Lp1a/31r3/Vvffe6+bKAaD8yN2XHj16VIMHD1ZKSooSEhLcXRaKiJAHI2RnZ2vz5s36/PPPdfjwYVksFlWpUkXt2rXToEGDFB4eLrvdrsTERF27dk3Hjh1TWFiY6tSpI09PT66qBYB8rFy5Uv3799drr72m6dOnu7scFBEhD0aYOXOm3nvvPaWlpemee+6Rh4eHDh065FjevHlzjRw5UgMHDlT16tXdWCkAuF9hf9ieO3dOa9as0R/+8AdVq1atDCpDSSLkocI7fvy4WrZsqTZt2mjx4sWqVKmS7rrrLp09e1YrVqzQ8uXLFR8fL0mKiIjQ9OnTdf/997u3aAAoQ9euXdOpU6dUt25d+fj4FOm9PN+24iLkocJ75513NG/ePC1ZskQ9evSQlPdX6r59+zRz5kwtW7ZM9erV05dffqm2bdu6q2QAKFMffPCBvvnmGz3yyCNq3769mjZtqrvuuuuW4e38+fMKCgpy3I0AFQ8hDxXeo48+qt27dysuLk5169Z13APPbrfLZrM57cRmz56tl19+WcOGDVNMTIwbqwaAslO7dm0lJyfLw8NDAQEB6tixoyIjI/Xggw+qYcOGCg4OduqfkZGhSZMm6eLFi1qwYAEzeRUU8RwVXnh4uL799lulp6dLkuNX52+fV5s7s/fSSy/pX//6l9avX69jx46pYcOGbqsbAMrC4cOHlZKSog4dOuiJJ57Qjz/+qK1bt2rlypWqW7euunXrpp49eyo8PFx33323AgMDtX//fi1YsEDdunUj4FVghDxUeLkPy37yySc1a9Ysde7cOd97P+WeV9K0aVOtXr3aEQoBwGSHDx/W9evXFRkZqVGjRunhhx/WoUOHtHXrVq1fv17ffPONvvzySzVv3lzdu3dX7969tW7dOqWmpmrEiBHuLh/FwOFaVHg5OTkaN26cPvroI4WFhWnUqFEaOHCg7rrrrjx9L1++rDFjxmj16tX69ddf3VAtAJStf/7znxo0aJCWLl2qQYMGOdqzsrJ08uRJ7dmzR//6178c9xL18vKS3W6Xt7e3Ll265MbKUVyEPBhj3rx5mjFjho4dO6ZatWppwIAB6tOnj+rUqSMPDw8FBgZqzpw5+uSTTzRy5EjNmjXL3SUDQKmz2+06ePCgKleurAYNGuR7+5SMjAwdPnxYhw4dUkxMjH788UeNHj1af/3rX91UNUoCIQ/GsNvtOnLkiBYsWKClS5c6Hq5dvXp1eXl56cyZM7LZbHr88cc1ffp01a5d280VA4B75Rf4XnzxRc2dO1c7d+5UeHi4mypDSSDkwUgZGRnavn27fvjhByUnJ+vXX3+Vv7+/Bg0apEcffVSVK1d2d4kAUG7YbDZZrVadOHFCf/zjH3X58mWdOnXK3WWhmLjwAkby9fVVRESEIiIilJWVJS8vL3eXBADlltVqlSSdPn1aWVlZGjlypJsrQklgJg8AAEi6efg2KSlJ1apVk6+vr7vLQTER8gAAAAxkdXcBAAAAKHmEPAAAAAMR8gAAAAxEyAMAADAQIQ8AAMBAhDwAAAADEfIAAAAMRMgDAAAwECEPAADAQP8fmiUzVxzAK60AAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_histogram(counts)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Options when plotting a histogram\n", - "\n", - "The `plot_histogram()` has a few options to adjust the output graph. The first option is the `legend` kwarg. This is used to provide a label for the executions. It takes a list of strings used to label each execution's results. This is mostly useful when plotting multiple execution results in the same histogram. The `sort` kwarg is used to adjust the order the bars in the histogram are rendered. It can be set to either ascending order with `asc` or descending order with `desc`. The `number_to_keep` kwarg takes an integer for the number of terms to show, the rest are grouped together in a single bar called rest. You can adjust the color of the bars with the `color` kwarg which either takes a string or a list of strings for the colors to use for the bars for each execution. You can adjust whether labels are printed above the bars or not with the `bar_labels` kwarg. The last option available is the `figsize` kwarg which takes a tuple of the size in inches to make the output figure." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:30.989035Z", - "start_time": "2021-07-31T05:08:30.821801Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:14.144536Z", - "iopub.status.busy": "2023-08-25T18:25:14.144009Z", - "iopub.status.idle": "2023-08-25T18:25:14.340822Z", - "shell.execute_reply": "2023-08-25T18:25:14.340126Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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+kgPPfSQ+vfvryZNmlgvwzh79qx69eqlgIAAffnll7p586YGDhyobt266cknn8yTX4AAAEDR5eLiYjMikmHy5MmKiIjQt99+q6ioKA0ZMkTjx4/Xtm3bMgWtu5Genq5atWppypQpWS6vWLGizevffvvNGqb27t2rbt262WxLunXPUdmyZTNtK7tnnGUnu9GntLS0HG3nXmQ3I5/RDycFDcEJ98RkMql27dqZ2k+cOKEDBw5o2LBh+sc//iFJKlmypDp27Kg5c+aoT58+ioqKksVi0aJFi9S4cWNJt27i7NKli7Zu3ao2bdrc130BAABFR61atVSrVi2988472rJli5o2bar/+Z//0QcffCApZ5e7BQQEaPfu3WrTpo3heomJierVq5dq1KihJk2a6MMPP1Tnzp3VsGFD67YkqUyZMlne05WhSpUqkqR9+/bd8f28vb0zzXQnKdOokHT3++zn5ydJOnTokLUOSbp586aOHj16x7ofZExHjnty48YNmc1mOTk5qXLlytq6daukW9f6StKQIUOsfTt06CCz2axVq1ZJunWDp8lk0ogRI3Tt2jVdunRJH3zwgZydndW0adP7vzMAAKDQS0hIUGpqqk1brVq15ODgYDNTo5ubW5aBIythYWE6ffq0Zs+enWlZUlKSEhMTra9HjBihEydOaMGCBZoyZYoqV66s8PBw63uHhobKw8ND48aNU0pKSqbtnT9/XtKtCVRatGihzz//XCdOnLDp8/eRnICAAMXHx9tcFnfmzBmtWLEi07bvdp/btm0rZ2dnffLJJzbvNXfuXMXHx6tjx46G23gQMeKEXGvdurXq1aunJk2a6NChQ5owYYKaN2+uEydOWK+7zfhFIkOxYsWsz93w9fXV8uXLFRYWZn16vLOzszZs2HBX09ECAADk1IYNGzRo0CA9//zzqlatmlJTU7Vw4UKZzWY999xz1n7169fXunXrNGXKFPn6+srf3996hcztevTooaVLl6pfv36Kjo5W06ZNlZaWpoMHD2rp0qVau3atGjRooA0bNmjGjBmKjIxUvXr1JEnz5s1Tq1atNGrUKH344Yfy8PDQzJkz1aNHD9WrV09du3aVj4+PTpw4oR9++EFNmzbV9OnTJUmffPKJmjVrpnr16umVV16Rv7+/jh07ph9++EExMTGSpK5du2rEiBHq3LmzhgwZYp3avFq1atq1a5fNftztPvv4+GjkyJEaM2aM2rVrp06dOunQoUOaMWOGGjZseFczET6QLLgn8fHxFkmW+Ph4e5did8eOHbNIsoSHh1sGDBhgyer/Xm5ubpZGjRpZLBaL5eLFixY3NzdLlSpVLPPnz7fMnj3bUr58eYuLi4vl4sWL97t8AAAeCFmdeyQlJVkOHDhgSUpKsmNl+WfevHkWSZYdO3Zkufzo0aMWSZZ58+ZZ28LDwy1ubm6Z+v7555+W3r17WwICAizFihWzlCxZ0hIcHGxZt26dTb+DBw9aWrRoYXF1dbWe39zJzZs3LRMnTrQ88sgjFhcXF4u3t7elfv36ljFjxlji4+MtCQkJFj8/P0u9evUsKSkpNuu+/vrrFgcHB8vWrVutbdHR0ZbQ0FCLp6enpVixYpaAgABLRESE5T//+Y/Nuvv27bN07tzZ4uXlZSlWrJglMDDQMmrUKJs+UVFRlpo1a1qcnZ0tgYGBlkWLFlkiIyMznatlt88Zn//Ro0dt+k+fPt0SFBRkcXJysjz00EOW/v37Wy5fvmzTp2XLlpZHHnkk0+cVHh5u8fPzu+Nner/c7ffHZLE8YHdlFTAJCQny9PRUfHy8dZaToszNzU21a9dWly5d9MYbb+jYsWM2o06Ojo568skntXLlSkVEROiLL77QzZs3rTc6Xrt2Te7u7ho8eLA++eQTe+0GAAAFVlbnHjdu3NDRo0fl7+/PVRtADt3t94d7nJBnzp49q6SkJPn6+ur555+XJOtQsiStWbNGaWlpevLJJyXdCkkmk8lmtpuMAHU/Z3oBAAAAjHCPE3KtQYMG6tGjh+rXr6/9+/frrbfekiR98MEHqlSpkmrUqKGpU6eqXLlyKlOmjPr3768SJUqoT58+kqTevXtr+fLlql27tiZMmKC0tDS9/vrrkm49QA0AAAAoKAhOyLVz585p2LBhSk9Pl4ODg8qXL299crYkbd68Wc2bN9cbb7whi8WicuXK6ccff7Su36FDB40fP17jxo3TU089JZPJJC8vL82ePVt16tSx014BAAAAmXGP0z3iHicAAHA/cY8TkLe4xwkAgP/Xrl07mUwm1a1b19q2YcMG+fr6ysHBQSaTSRUrVrR5kOQvv/yiatWqycnJSSaTSU5OTmrZsqWuXbtmj10AANgZwQkAUKgtWLBA69evt/kV8dy5cwoNDZV064HdX3/9tVJTU/XYY49ZH4y5ceNGWSwWvffee4qOjtbw4cP173//W61atbLHbgAA7Ix7nAAAhdbZs2fVt29fjR07VuPGjbO2z507V6mpqdq+fbsqVKggSWrYsKH8/Pw0ZcoUDR8+XO+8847eeecd6zqtWrVSTEyMoqKi7vt+AADsjxEnAECh1bRpU9WsWVPDhw+3ab9+/bok2dyb6uXlJUlavXp1ttuLj4+Xs7Nz3hcKACjwCE4AgEJpyJAhOn36tDZs2JBpWffu3SVJLVu21IULF3Tu3Dk1a9ZMkvTXX39lub3169dr8+bNeu655/KvaABAgUVwAgAUOr/++qumT5+uRYsWWUeS/q569eoaN26c9u3bJx8fHz300EO6du2aXF1dbR7KneE///mP2rVrp2rVqumLL764D3sAAChoCE4AgEJn5cqVslgsev7552UymWQymRQfH6+YmBiZTCbdvHlTI0eOVEpKig4ePKhjx47pzz//VHJysipXrmyzrV27dqlJkyaqVKmS9u/fb58dAgDYHcEJAFDoDB48WN98843NX/HixeXv769vvvnG5j6lwMBA+fn5afLkyUpPT9egQYOsy/7zn//oscceU7ly5fT777/L0ZE5lQDkD5PJpNGjR9u7DLuaP3++TCaTjh07Zu9SssS/AACAQsfX11edO3e2aXNycpKnp6e1vXfv3mrSpImqVKmiZcuWadasWapfv77at28v6VZoatKkiUqUKKHly5fr4MGD1m3Vrl37/u0MAEnS3r17NWbMGO3YsUN//fWXSpUqpRo1aqhTp04aPHiwvctDDowbN041atTQM888Y+9ScoTgBAAokg4cOKAFCxYoPT1dTk5Oeuqpp7RixQrr8v/5n/9RSkqKLl++rIYNG9qsa7FY7ne5d+VI4Gh7lwBJAYdG27uETArC/zfu5XPZsmWLgoODValSJfXt21dly5bVyZMntW3bNk2bNo3g9IAZN26cunTpkik49ejRQ127dpWLi4t9CjNAcAIAFAlXrlyxeb1t27Y79p8zZ47mzJmTjxUBuFtjx46Vp6enduzYkWnCl3PnztmnKOQ5s9kss9ls7zKyxT1OQCE0YcIEmUwmDR06VJJ07Ngx6w3yt/8tW7ZM0n+vK87qj3+UAAD2dOTIET3yyCNZzpJZpkyZTG2LFi1S/fr15erqqpIlS6pr1646efJk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efO0ZcsWnT17VpJUtmxZNW3aVBEREQoICMjzYgEAAADAHnIcnDIEBAQwogQAAACgSOAhSwAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABggOAEAAAAAAYITgAAAABgoEAGp0WLFunVV19VgwYN5OLiIpPJpPnz52fZd/To0TKZTNn+HTt2LMv11q5dq5YtW8rd3V0eHh4KDg7W+vXr82+nAAAAADywHO1dQFbeeecdHT9+XKVLl1a5cuV0/Phxw3XCw8NVuXLlTO1eXl6Z2hYtWqQePXrIx8dHERERkqQlS5boiSee0NKlS9WlS5d73AMAAAAAhUmBDE5z5sxR1apV5efnpwkTJmjkyJGG60RERKhVq1aG/S5fvqzBgwerdOnS2rVrlypUqCBJGjFihOrWrav+/fsrNDRU7u7u97obAAAAAAqJAnmpXtu2beXn55cv2162bJmuXLmiwYMHW0OTJFWoUEGDBg3ShQsXtGLFinx5bwAAAAAPpgIZnHJj06ZNmjhxoj766COtXLlS165dy7Lfxo0bJUkhISGZloWGhkqSfv7553yrEwAAAMCDp0BeqpcbkZGRNq+9vLw0bdo09ezZ06Y9NjZWklS1atVM28hoy+iTleTkZCUnJ1tfJyQkSJJSUlKUkpIiSXJwcJDZbFZaWprS09OtfTPaU1NTZbFYrO1ms1kODg7Ztt/artMd9x8oatLS0u7h+/Rfjo63DoOpqal31e7k5KT09HSlpaVZ20wmkxwdHbNtz+5YkLfHCHvuE8cn4O/uxzECwP33wAenRx99VJ9//rlatWqlcuXK6ezZs1q1apXeffddRUREyMvLS506dbL2j4+PlyR5enpm2paHh4dNn6yMHz9eY8aMydQeFRWl4sWLS5IqVaqkunXras+ePTpx4oS1T2BgoIKCgrR9+3adP3/e2l6nTh35+flp06ZNunr1qrX98ccfV5kyZRQVFSWp411+IkDREBsbm+vv099POoKDg+Xq6qrVq1fbbL9Dhw5KSkpSdHS0tc3R0VEdO3bUhQsXtHXrVmu7u7u7WrdurZMnTyomJsba7uPjoyZNmig2NlaHDh2ytufHMcK++/S0APxXfh8jdu7ceV/2A4Atk+XvP18WQBmTQ8ybN886A97dWL9+vZ544gnVrFlTe/bssbZXq1ZNsbGxSklJsf76miElJUXOzs6qXbu2du/eneV2sxpxqlixoi5cuGANXvnxa/KAf/GLbkHx5szR9i4BkiofGPWAjs4UvhEnjk8FB8engsH/93fz9Rhx6dIllSpVSvHx8dZzDwD574EfccpOmzZtFBAQoL179yohIcF6YMkYaYqPj1epUqVs1sm47C6r0agMLi4ucnFxydTu5OQkJyfbkwez2Syz2Zyp7+2Bzaj99u0CkPW7lVffp5y0Ozg4yMEh8y2i2bVndyzI72OEPfYJgP2OEQDyV6GZHCIrpUuXliRdv37d2nan+5judP8TAAAAgKKr0AanxMRE7d+/X25ubtYAJUktW7aUpP+/b8jW2rVrbfoAAAAAgPSAB6erV6/qjz/+yNSelJSkvn376urVqwoLC7MZ0g4LC5Onp6c+/fRTnTp1ytp+6tQpTZ8+XaVLl1bnzp3vS/0AAAAAHgwF8iLZOXPm6JdffpEk7d2719qW8QymZs2aqU+fPrp48aKCgoLUsGFDVa9eXWXLltVff/2ldevW6dSpU6pVq5Y++ugjm217e3tr+vTp6tGjh+rVq6cXXnhBkrRkyRJdvHhRS5Yskbu7+/3bWQAAAAAFXoEMTr/88osWLFhg07Z582Zt3rzZ+rpPnz4qWbKkBgwYoO3bt2v16tW6fPmyXF1dVb16dQ0ZMkSDBg2Sq6trpu2/9NJLKl26tMaNG6d58+bJZDKpfv36euedd9S2bdt83z8AAAAAD5YCGZzmz5+v+fPnG/bz8PDQ9OnTc/Ue7dq1U7t27XK1LgAAAICi5YG+xwkAAAAA7geCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgAGCEwAAAAAYIDgBAAAAgIEiHZx27NihDh06yMvLS25ubnrssce0dOlSe5cFAAAAoIBxtHcB9hIdHa3Q0FAVK1ZMXbt2lbu7u5YvX64XXnhBJ0+e1D/+8Q97lwgAAACggCiSI06pqanq27evHBwctGnTJn322WeaPHmydu/erWrVqumtt97S8ePH7V0mAAAAgAKiSAanDRs26MiRI+revbvq1Kljbff09NRbb72lmzdvasGCBfYrEAAAAECBUiSD08aNGyVJISEhmZaFhoZKkn7++ef7WRIAAACAAqxIBqfY2FhJUtWqVTMtK1u2rEqUKGHtAwAAAABFcnKI+Ph4SbcuzcuKh4eHtc/tkpOTlZycnGlbly5dUkpKiiTJwcFBZrNZaWlpSk9Pt/bNaE9NTZXFYrG2m81mOTg4ZNuekpKimzeccrm3yGtX05KNOyHfXb58Odffp79zdLx1GExNTb2rdicnJ6WnpystLc3aZjKZ5OjomG17dseCvDxG2HOfOD4VHByfCoYrV67k6zHi0qVLkmRzPACQ/4pkcLoX48eP15gxYzK1+/v726Ea2MMX9i4At5ScYO8KgAKH41MB4X1/jk9Xr17N9kdgAHmvSAanjINMdqNKCQkJ8vb2znLZyJEjNWzYMOvr9PR0Xbp0SaVKlZLJZMr7YlGgJCQkqGLFijp58qQ8PDzsXQ4AWHF8KjosFouuXr0qX19fe5cCFClFMjhl3NsUGxur+vXr2yw7e/asrl27pkaNGmW5rouLi1xcXGzavLy88qVOFFweHh6cmAAokDg+FQ2MNAH3X5GcHKJly5aSpKioqEzL1q5da9MHAAAAAEyWInhnYWpqqgIDA3X69Glt27bN+iyn+Ph4NWrUSMeOHdOhQ4dUuXJlu9aJgichIUGenp6Kj4/nF10ABQrHJwDIX0XyUj1HR0fNmTNHoaGhatGihbp27Sp3d3ctX75cx48f16RJkwhNyJKLi4siIyMzXa4JAPbG8QkA8leRHHHKsH37dkVGRmrLli1KSUlRrVq1NGzYML3wwgv2Lg0AAABAAVKkgxMAAAAA3I0iOTkEAAAAAOQEwQkAAAAADBCcAAAAAMAAwQkAAAAADBCcAAAAAMAAwQkwkDHxpMViEZNQAgAAFE1MRw4AAAAABhztXQBQkJ07d0579+5VbGysrl69qsaNGyswMFClSpWSo+Otr096erocHBi8BQAAKMwITkA21qxZo7Fjx2rLli027aVKlVKbNm30wgsv6Mknn5STk5OdKgRQ1KWlpclsNtu7DAAoErhUD8jCyZMn1apVKyUmJioiIkLBwcH6888/9dtvv2n37t3as2ePkpOTVb16db399tt67rnn5OLiIovFIpPJZO/yARRyt490Z9yDaTT6zTEKAHKPEScgC7NmzdLly5c1Z84cPfvsszbLTp06pS1btui7777TV199pZdeekmnTp3S8OHDOSEBcF/MmjVLGzduVM+ePdWyZUuVKFHCevxJT0+XpCxDFMcoAMg9RpyALDz22GNydXXVsmXLVLp0aaWmpspkMmW6JCY6Olr/+Mc/dODAAc2YMUO9e/e2U8UAihJ/f38dP35cLi4uevTRRxUSEqIOHTqocePGNuEoNTVVjo6Oun79uj777DM9+uijCg4OtmPlAPDgIjgBt7l27Zo6d+6sU6dOaefOnSpevLjNZTG3XxLz22+/qU2bNmrevLm+/fZbLoUBkK/279+vWrVqqX79+vL29ta6deskSW5ubmratKk6dOigkJAQBQUFWdf55Zdf1KJFCzVp0kS//PKLvUoHgAcaU4EBtylRooTq16+vQ4cOafHixZIyX/KS8To9PV1169ZVixYtdPDgQR0/fpzQBCBf7d27V5LUvXt3RUVF6eDBg5owYYIefvhhRUVFaejQoWrdurW6d++uhQsX6vLly9q+fbskaeTIkfYsHQAeaIw4AVk4ffq02rdvr3379mnQoEGKiIhQjRo1VKxYMWufjEtgEhIS1KdPH/366686fvy4HasGUBR89tln6tevn3744Qe1b9/eZtmOHTv0v//7v/r666916tQpSVLVqlWVkJCgpKQkXblyxQ4VA0DhwIgTkIXy5cvrvffeU+XKlTV9+nS9+uqrmjRpkjZu3Kjjx4/rxo0b1uc4ff/999q4cWOmExgAyGsWi0W1a9fW0KFDVa1aNZt2SWrYsKGmTJmiP/74Q99//7169uypv/76S3/99Zd69Ohhr7IBoFBgxAn4m9vvT7p06ZLGjx+vpUuX6uTJk/Lx8VHNmjXl6+ur4sWLKykpSUuXLpW/v79WrlypwMBAO1YPoKi4du2anJ2d5ezsnGnZ7cexQYMGacaMGdq1a5fq1KlzH6sEgMKF4ATcJuOk49SpU/L19ZWDg4P27dunVatWaePGjfr999918uRJSZK3t7fq1KmjTz75RI888oidKweAWzKOY0eOHNELL7yg+Ph4xcbG2rssAHigEZyA/5eamqrNmzfr888/1x9//CGTyaTixYurYcOGCgsLU926dWWxWHTy5EklJSXpzz//VFBQkCpWrChHR0dm0wNQ4KxatUqdOnXSP//5T02cONHe5QDAA43gBPy/SZMm6f3339fVq1f18MMPy2w269ChQ9blNWrU0IABA9SlSxeVKVPGjpUCKOru9oeav/76Sz/++KOeeuoplSxZ8j5UBgCFF8EJkHT06FHVqlVL9erV04IFC+Ts7KyHHnpIZ8+e1ffff69ly5Zp48aNkqTg4GBNnDhRDRo0sG/RAIqMpKQknThxQpUqVZKrq2uO1k1LS8v08G4AQM4RnABJ7777rmbNmqWvvvpKbdq0kZT5F929e/dq0qRJWrp0qfz8/PTll1+qfv369ioZQBEyYcIELV++XM8++6wee+wxBQYG6qGHHrpjIDp//ry8vb2tM4ACAO4NwQmQ9NxzzykmJkbR0dGqVKmS9RlNFotF6enpNicn06ZN0+uvv67w8HDNmzfPjlUDKCoqVKiguLg4mc1meXp6qkmTJgoJCVHjxo1VpUoVlSpVyqZ/YmKiRo8erYsXL2r27NmMOAFAHuBnKEBS3bp1tWLFCl27dk2SrL/Qmkwm6wlHxgjUa6+9pn//+9/asGGD/vzzT1WpUsVudQMo/P744w/Fx8fr8ccfV/fu3fXTTz9p69atWrVqlSpVqqRWrVqpbdu2qlu3rsqXLy8vLy/t27dPs2fPVqtWrQhNAJBHCE6Abt23JEkvvviiJk+erGbNmmX5fJSMewUCAwO1Zs0aa9ACgPzyxx9/6MaNGwoJCdHAgQP15JNP6tChQ9q6das2bNig5cuX68svv1SNGjXUunVrtWvXTuvXr1dCQoL69u1r7/IBoNDgUj1AtwLRiBEjNGXKFAUFBWngwIHq0qWLHnrooUx9L1++rKFDh2rNmjU6d+6cHaoFUJR8/fXXCgsL0+LFixUWFmZtT0lJ0fHjx7V79279+9//tj5nzsnJSRaLRS4uLrp06ZIdKweAwoXgBPzNrFmz9NFHH+nPP/+Ur6+vOnfurPbt26tixYoym83y8vLSp59+qo8//lgDBgzQ5MmT7V0ygELOYrHo4MGDKlasmPz9/bOcijwxMVF//PGHDh06pHnz5umnn37SoEGD9Mknn9ipagAofAhOwN9YLBYdPnxYs2fP1uLFi3Xq1ClJUpkyZeTk5KQzZ84oPT1d3bp108SJE1WhQgU7VwygKMsqRA0ZMkTTp0/Xzp07VbduXTtVBgCFD8EJyEZiYqK2b9+u7777TnFxcTp37pw8PDwUFham5557TsWKFbN3iQAgSUpPT5eDg4OOHTump59+WpcvX9aJEyfsXRYAFCpMDgFkw83NTcHBwQoODlZKSoqcnJzsXRIAZMnBwUGSdPr0aaWkpGjAgAF2rggACh9GnAAAKCQsFotOnTqlkiVLys3Nzd7lAEChQnACAAAAAAMO9i4AAAAAAAo6ghMAAAAAGCA4AQAAAIABghMAAAAAGCA4AQAAAIABghMAAAAAGCA4AQAAAIABghMAAAAAGCA4AQAAAICB/wMIl0qQBnPUdAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Execute 2-qubit Bell state again\n", - "second_result = backend.run(transpile(circ, backend), shots=1000).result()\n", - "second_counts = second_result.get_counts(circ)\n", - "# Plot results with legend\n", - "legend = ['First execution', 'Second execution']\n", - "plot_histogram([counts, second_counts], legend=legend)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:33.681069Z", - "start_time": "2021-07-31T05:08:33.494469Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:14.344321Z", - "iopub.status.busy": "2023-08-25T18:25:14.343863Z", - "iopub.status.idle": "2023-08-25T18:25:14.576584Z", - "shell.execute_reply": "2023-08-25T18:25:14.575703Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABcAAAAPXCAYAAAAMsOj7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABcZklEQVR4nOzde5zVZaHv8e+a4SoyAwpsRQnQVMxLSpIlXlPBZOfWMlNTAVPLUjNra20zsBRTUzM9uu0imtZOO6XtUjd4QT3etp1teE2jAu+WmM4gIAywzh++WMdxGIVxcODx/X69eOU861m/9fyGF6xfH37zrEq1Wq0GAAAAAAAKU9fVCwAAAAAAgNVBAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAInXr6gWs7ZYtW5bnnnsuffv2TaVS6erlAAAAANCFqtVq5s2bl8GDB6eubtXuPV26dGlaWlpW08qgDN26dUt9ff1Kt1gB/B167rnnMmTIkK5eBgAAAABrkKeffjobb7zxSs2tVqt54YUX8sorr6zeRUEh6uvrM2jQoDQ2Nr5tCBfA36G+ffsmef0vtYaGhi5eDQAAAABdqbm5OUOGDKk1o5WxPH4PGjQo66yzjl0GoB3VajVLlixJc3Nznn/++SxcuDAbbrjhWz5HAH+Hlv+F1NDQIIADAAAAkCQrHbGXLl1ai9/rr7/+al4VlKFv377p2bNn5s6dm0GDBqW+vr7duT4EEwAAAAC6yPI9v9dZZ50uXgmsXfr06ZNqtfq2++YL4AAAAADQxWx7AqtmZf/MCOAAAAAAABRJAAcAAAAAoEgCOAAAAADwrpszZ04qlUquuOKKrl5Kka644opUKpXMmTOnq5fSpQRwAAAAAKDTLQ+wK/r19a9/fbW85pQpU3L99devlmOvqd6L57wqunX1AgAAAACAFfj5GvLBmIdW39HTv/3tb2f48OGtxrbeeusMHTo0CxcuTPfu3d/R8d9oypQpOfDAA7P//vt32jHXdO2d8+GHH56DDz44PXv27JqFrSEEcAAAAABgtfn4xz+eHXbYYYWP9erV622fP3/+/PTp06ezl1W8+vr61NfXd/UyupwtUAAAAACAd92K9gCfMGFC1l133fzlL3/Jvvvum759++azn/1skmTWrFn51Kc+lQ022CC9evXKxhtvnIMPPjhNTU1Jkkqlkvnz5+fKK6+sbbUyYcKEt1zDokWLMmnSpLz//e9Pz549M2TIkJx88slZtGhRbc748ePTq1ev/PGPf2z13LFjx6Z///557rnnamM33XRTdtlll/Tp0yd9+/bNuHHj8uijj7Z53ccffzwHHXRQBg4cmN69e2eLLbbIqaee2ur7MGzYsDbPmzx5ciqV//+TAW91zu3tAX7JJZdkq622Ss+ePTN48OB86UtfyiuvvNJqzu67756tt946jz32WPbYY4+ss8462WijjXLOOee85fdzTeQOcAAAAABgtWlqasrcuXNbjQ0YMKDd+UuWLMnYsWOz884753vf+17WWWedLF68OGPHjs2iRYty/PHHZ4MNNsizzz6b3/3ud3nllVfS2NiYq666KkcddVQ+/OEP55hjjkmSbLrppu2+zrJly7LffvvlrrvuyjHHHJMtt9wyDz/8cC644IL86U9/qu2rfeGFF+a2227L+PHjc++996a+vj6XXXZZpk+fnquuuiqDBw9Oklx11VUZP358xo4dm7PPPjsLFizIpZdemp133jl/+MMfakH7oYceyi677JLu3bvnmGOOybBhw/KXv/wlv/3tb3PmmWeu0vd2Vc958uTJOf3007PXXnvl2GOPzRNPPJFLL700v//973P33Xe32o7m5Zdfzj777JNPfvKTOeigg/K///f/zimnnJJtttkmH//4x1dpnV1JAAcAAAAAVpu99tqrzVi12v6+4osWLcqnP/3pnHXWWbWxmTNnZvbs2fnlL3+ZAw88sDb+rW99q/bfhx12WL7whS9kk002yWGHHfa26/r5z3+eW265JXfccUd23nnn2vjWW2+dL3zhC7nnnnuy0047pV+/fvnJT36SsWPH5rvf/W4OPfTQfO1rX8v+++9fe51XX301J5xwQo466qj88Ic/rB1r/Pjx2WKLLTJlypTa+PHHH59qtZoHHngg73vf+2pzv/vd777tmt9sVc75xRdfzFlnnZUxY8bkpptuSl3d65uDjBgxIscdd1yuvvrqTJw4sTb/ueeey09/+tMcfvjhSZLPfe5zGTp0aH7yk5+sVQHcFigAAAAAwGrzv/7X/8rNN9/c6tfbOfbYY1t93djYmCSZNm1aFixY0Cnr+uUvf5ktt9wyI0aMyNy5c2u/PvaxjyVJZsyYUZs7ZsyYfP7zn8+3v/3tfPKTn0yvXr1y2WWX1R6/+eab88orr+SQQw5pdaz6+vrsuOOOtWO9+OKLufPOO3PkkUe2it9JWm1tsjrccsstWbx4cU488cRa/E6So48+Og0NDbnhhhtazV933XVbRfUePXrkwx/+cP7617+u1nV2NneAAwAAAACrzYc//OF2PwRzRbp165aNN9641djw4cNz0kkn5fzzz8/Pfvaz7LLLLtlvv/1y2GGH1eL4qpo1a1b++Mc/ZuDAgSt8/O9//3urr7/3ve/lN7/5TWbOnJmf//znGTRoUKtjJanF8zdraGhIklo83nrrrTu05nfiySefTJJsscUWrcZ79OiRTTbZpPb4chtvvHGbKN+/f/889NBDq3ehnUwABwAAAADWGD179mx1h/Jy5513XiZMmJDf/OY3mT59ek444YScddZZue+++9oE85WxbNmybLPNNjn//PNX+PiQIUNaff2HP/yhFsUffvjhHHLIIa2Olby+J/cGG2zQ5ljduq1ahm3vbvClS5eu0nHeifr6+hWOv9X2NWsiARwAAAAAWCtss8022WabbfLNb34z99xzT0aPHp1///d/zxlnnJFk1bYR2XTTTfPggw9mzz33fNvnzZ8/PxMnTswHPvCB7LTTTjnnnHNywAEHZNSoUbVjJcmgQYNWuOf5cptsskmS5JFHHnnL1+vfv39eeeWVNuNvvks7WflzHjp0aJLkiSeeqK0jSRYvXpzZs2e/5brXZvYABwAAAADWaM3NzVmyZEmrsW222SZ1dXVZtGhRbaxPnz4rDMcrctBBB+XZZ5/Nj370ozaPLVy4MPPnz699fcopp+Spp57KlVdemfPPPz/Dhg3L+PHja689duzYNDQ0ZMqUKWlpaWlzvBdffDFJMnDgwOy66665/PLL89RTT7Wa88Y7qzfddNM0NTW12m7k+eefz3XXXdfm2Ct7znvttVd69OiRH/zgB61e6yc/+Umampoybty4tz3G2sgd4AAAAADAGu22227Lcccdl09/+tPZfPPNs2TJklx11VWpr6/Ppz71qdq8D33oQ7nlllty/vnnZ/DgwRk+fHh23HHHFR7z8MMPz7XXXpsvfOELmTFjRkaPHp2lS5fm8ccfz7XXXptp06Zlhx12yG233ZZLLrkkkyZNysiRI5MkU6dOze67757TTjst55xzThoaGnLppZfm8MMPz8iRI3PwwQdn4MCBeeqpp3LDDTdk9OjRufjii5MkP/jBD7Lzzjtn5MiROeaYYzJ8+PDMmTMnN9xwQ2bOnJkkOfjgg3PKKafkgAMOyAknnJAFCxbk0ksvzeabb54HHnig1Xms7DkPHDgw3/jGN3L66adnn332yX777Zcnnngil1xySUaNGtXqAy9LIoADAAAAwJro0LVrr+XV6YMf/GDGjh2b3/72t3n22Wezzjrr5IMf/GBuuummfOQjH6nNO//883PMMcfkm9/8ZhYuXJjx48e3G8Dr6upy/fXX54ILLshPf/rTXHfddVlnnXWyySab5Mtf/nI233zzzJs3L0ceeWS23377nHrqqbXn7rLLLvnyl7+c8847L5/85CfzkY98JIceemgGDx6c7373uzn33HOzaNGibLTRRtlll10yceLEVudy33335bTTTsull16a1157LUOHDs1BBx1Um7P++uvnuuuuy0knnZSTTz45w4cPz1lnnZVZs2a1CeCrcs6TJ0/OwIEDc/HFF+crX/lK1ltvvRxzzDGZMmVKunfv3qHfmzVdpbq27Vq+hmlubk5jY2Oamppqn+YKAAAAwHvTqrai1157LbNnz87w4cPTq1evd2GFUIaV/bNjD3AAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAHVCpVDJ58uSuXkaXuuKKK1KpVDJnzpyuXsoKCeAAAAAAwGrx8MMP58ADD8zQoUPTq1evbLTRRtl7771z0UUXdfXSWEVTpkzJ9ddf39XLWGUCOAAAAACsgSqVyhrxq6Puueee7LDDDnnwwQdz9NFH5+KLL85RRx2Vurq6XHjhhZ34neLd0F4AP/zww7Nw4cIMHTr03V/USujW1QsAAAAAAMpz5plnprGxMb///e/Tr1+/Vo/9/e9/75pF0enq6+tTX1/f1ctolzvAAQAAAIBO95e//CVbbbVVm/idJIMGDWozdvXVV+dDH/pQevfunfXWWy8HH3xwnn766Tbz/vu//zv77rtv+vfvnz59+mTbbbdtc0f5bbfdll122SV9+vRJv3798i//8i/54x//2GrO5MmTU6lU8uc//zkTJkxIv3790tjYmIkTJ2bBggWt5i5atChf+cpXMnDgwPTt2zf77bdfnnnmmZX+XixatCiTJk3K+9///vTs2TNDhgzJySefnEWLFtXmjB8/Pr169WqzzrFjx6Z///557rnnamM33XRT7fz69u2bcePG5dFHH23zuo8//ngOOuigDBw4ML17984WW2yRU089tfb4hAkTMmzYsDbPW/69Wa5SqWT+/Pm58soraz8ZMGHChCTt7wF+ySWXZKuttkrPnj0zePDgfOlLX8orr7zSas7uu++erbfeOo899lj22GOPrLPOOtloo41yzjnnvN23dKUJ4AAAAABApxs6dGj+53/+J4888sjbzj3zzDNzxBFHZLPNNsv555+fE088Mbfeemt23XXXVtH05ptvzq677prHHnssX/7yl3Peeedljz32yO9+97vanFtuuSVjx47N3//+90yePDknnXRS7rnnnowePXqFH9R40EEHZd68eTnrrLNy0EEH5Yorrsjpp5/eas5RRx2V73//+xkzZky++93vpnv37hk3btxKfR+WLVuW/fbbL9/73vfyiU98IhdddFH233//XHDBBfnMZz5Tm3fhhRdm4MCBGT9+fJYuXZokueyyyzJ9+vRcdNFFGTx4cJLkqquuyrhx47Luuuvm7LPPzmmnnZbHHnssO++8c6vze+ihh7Ljjjvmtttuy9FHH50LL7ww+++/f37729+u1Lrf6KqrrkrPnj2zyy675KqrrspVV12Vz3/+8+3Onzx5cr70pS9l8ODBOe+88/KpT30ql112WcaMGZOWlpZWc19++eXss88++eAHP5jzzjsvI0aMyCmnnJKbbrpplde5QlXekaampmqSalNTU1cvBQAAAIAutqqtaOHChdXHHnusunDhwjaPJVkjfnXU9OnTq/X19dX6+vrqRz/60erJJ59cnTZtWnXx4sWt5s2ZM6daX19fPfPMM1uNP/zww9Vu3brVxpcsWVIdPnx4dejQodWXX3651dxly5bV/nu77barDho0qPrSSy/Vxh588MFqXV1d9YgjjqiNTZo0qZqkeuSRR7Y61gEHHFBdf/31a1/PnDmzmqT6xS9+sdW8Qw89tJqkOmnSpLf8Plx11VXVurq66v/5P/+n1fi///u/V5NU77777trYtGnTqkmqZ5xxRvWvf/1rdd11163uv//+tcfnzZtX7devX/Xoo49udawXXnih2tjY2Gp81113rfbt27f65JNPtpr7xu/V+PHjq0OHDm2z5uXfmzfq06dPdfz48W3mTp06tZqkOnv27Gq1Wq3+/e9/r/bo0aM6ZsyY6tKlS2vzLr744mqS6uWXX14b22233apJqj/96U9rY4sWLapusMEG1U996lNtXuuN3urPzhu5AxwAAAAA6HR777137r333uy333558MEHc84552Ts2LHZaKON8p//+Z+1eb/+9a+zbNmyHHTQQZk7d27t1wYbbJDNNtssM2bMSJL84Q9/yOzZs3PiiSe22VZl+XYdzz//fGbOnJkJEyZkvfXWqz2+7bbbZu+9986NN97YZp1f+MIXWn29yy675KWXXkpzc3OS1J5zwgkntJp34oknrtT34Ze//GW23HLLjBgxotX5fexjH0uS2vklyZgxY/L5z38+3/72t/PJT34yvXr1ymWXXVZ7/Oabb84rr7ySQw45pNWx6uvrs+OOO9aO9eKLL+bOO+/MkUcemfe9730r/F6tLrfccksWL16cE088MXV1/z8/H3300WloaMgNN9zQav66666bww47rPZ1jx498uEPfzh//etfO2U9PgQTAAAAAFgtRo0alV//+tdZvHhxHnzwwVx33XW54IILcuCBB2bmzJn5wAc+kFmzZqVarWazzTZb4TG6d++e5PU9xZNk6623bvf1nnzyySTJFlts0eaxLbfcMtOmTcv8+fPTp0+f2vibA3H//v2TvL41R0NDQ5588snU1dVl0003bTVvRa+xIrNmzcof//jHDBw4cIWPv/kDQb/3ve/lN7/5TWbOnJmf//znrfZLnzVrVpLU4vmbNTQ0JEktHr/V92p1ae/3oEePHtlkk01qjy+38cYbt4ny/fv3z0MPPdQp6xHAAQAAAIDVqkePHhk1alRGjRqVzTffPBMnTswvf/nLTJo0KcuWLUulUslNN92U+vr6Ns9dd911V+vaVvSaSVKtVjvl+MuWLcs222yT888/f4WPDxkypNXXf/jDH2pR/OGHH84hhxzS6ljJ63tyb7DBBm2O1a3bquXe9u4GX74H+bthdX//BXAAAAAA4F2zww47JHl9u5Ik2XTTTVOtVjN8+PBsvvnm7T5v+R3YjzzySPbaa68Vzhk6dGiS5Iknnmjz2OOPP54BAwa0uvt7ZQwdOjTLli3LX/7yl1Z3Na/oNdpb94MPPpg999zzbbcfmT9/fiZOnJgPfOAD2WmnnXLOOefkgAMOyKhRo2rHSpJBgwa1+z1Ikk022SRJ3vYDSPv379/qQ0aXe/Nd2snKb53yxt+D5etIksWLF2f27Nlvue7VwR7gAAAAAECnmzFjxgrv4l2+p/bymPzJT34y9fX1Of3009vMr1areemll5IkI0eOzPDhw/P973+/TbRd/rwNN9ww2223Xa688spWcx555JFMnz49++677yqfx8c//vEkyQ9+8INW49///vdX6vkHHXRQnn322fzoRz9q89jChQszf/782tennHJKnnrqqVx55ZU5//zzM2zYsIwfPz6LFi1KkowdOzYNDQ2ZMmVKWlpa2hzvxRdfTJIMHDgwu+66ay6//PI89dRTrea88Xu86aabpqmpqdV2I88//3yuu+66Nsfu06fPCmP5m+21117p0aNHfvCDH7R6rZ/85CdpamrKuHHj3vYYnckd4AAAAABApzv++OOzYMGCHHDAARkxYkQWL16ce+65J9dcc02GDRuWiRMnJnk9wp5xxhn5xje+kTlz5mT//fdP3759M3v27Fx33XU55phj8rWvfS11dXW59NJL84lPfCLbbbddJk6cmA033DCPP/54Hn300UybNi1Jcu655+bjH/94PvrRj+Zzn/tcFi5cmIsuuiiNjY2ZPHnyKp/Hdtttl0MOOSSXXHJJmpqastNOO+XWW2/Nn//855V6/uGHH55rr702X/jCFzJjxoyMHj06S5cuzeOPP55rr70206ZNyw477JDbbrstl1xySSZNmpSRI0cmSaZOnZrdd989p512Ws4555w0NDTk0ksvzeGHH56RI0fm4IMPzsCBA/PUU0/lhhtuyOjRo3PxxRcneT3Y77zzzhk5cmSOOeaYDB8+PHPmzMkNN9yQmTNnJkkOPvjgnHLKKTnggANywgknZMGCBbn00kuz+eab54EHHmh1Hh/60Idyyy235Pzzz8/gwYMzfPjw7Ljjjm3Od+DAgfnGN76R008/Pfvss0/222+/PPHEE7nkkksyatSoVh94+a6o8o40NTVVk1Sbmpq6eikAAAAAdLFVbUULFy6sPvbYY9WFCxe2eSzJGvGro2666abqkUceWR0xYkR13XXXrfbo0aP6/ve/v3r88cdX//a3v7WZ/6tf/aq68847V/v06VPt06dPdcSIEdUvfelL1SeeeKLVvLvuuqu69957V/v27Vvt06dPddttt61edNFFrebccsst1dGjR1d79+5dbWhoqH7iE5+oPvbYY63mTJo0qZqk+uKLL7Yanzp1ajVJdfbs2bWxhQsXVk844YTq+uuvX+3Tp0/1E5/4RPXpp5+uJqlOmjTpbb8Xixcvrp599tnVrbbaqtqzZ89q//79qx/60Ieqp59+erWpqana3NxcHTp0aHXkyJHVlpaWVs/9yle+Uq2rq6vee++9tbEZM2ZUx44dW21sbKz26tWruummm1YnTJhQ/b//9/+2eu4jjzxSPeCAA6r9+vWr9urVq7rFFltUTzvttFZzpk+fXt16662rPXr0qG6xxRbVq6++uva9eaPHH3+8uuuuu1Z79+5dTVIdP358u9+varVavfjii6sjRoyodu/evfpP//RP1WOPPbb68ssvt5qz2267Vbfaaqs236/x48dXhw4d+pbf07f6s/NGlWq1k3YTf49qbm5OY2Njmpqaap+yCgAAAMB706q2otdeey2zZ8/O8OHD06tXr3dhhVCGlf2zYw9wAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAoItVq9WuXgKsVVb2z4wADgAAAABdpHv37kmSBQsWdPFKYO0yf/78VCqV2p+h9nR7l9YDAAAAALxJfX19+vXrl7///e9JknXWWSeVSqWLVwVrpmq1miVLlqS5uTnNzc3p169f6uvr3/I5AjgAAAAAdKENNtggSWoRHHhr9fX12XDDDdPY2Pi2cwVwAAAAAOhClUolG264YQYNGpSWlpauXg6s0bp165b6+vqV/kkJARwAAAAA1gD19fVvu50DsGp8CCYAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBI3bp6AayZKpVKVy8BilGtVrt6CQAAAADvSe4ABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCJ16+oFAAAAwFrp55WuXgGU4dBqV68AKJg7wAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEXq1tULACjezytdvQIow6HVrl4BAAAAaxl3gAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRunX1AgAAgHdPpVLp6iVAMao/6+oVAABvxx3gAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJHWigB+9tlnp1KppFKp5L777mv12OTJk2uPrejXnDlzVnjMadOmZbfddkvfvn3T0NCQPfbYI7feeuu7cDYAAAAAALwbunX1At7OI488kkmTJqVPnz6ZP39+u/PGjx+fYcOGtRnv169fm7Grr746hx9+eAYOHJgJEyYkSa655prsvffeufbaa3PggQd20uoBAAAAAOgqa3QAb2lpyfjx47Pddttls802y9VXX93u3AkTJmT33Xd/22O+/PLLOf744zNgwIA88MAD2XjjjZMkp5xySrbffvsce+yxGTt2bPr27dtZpwEAAAAAQBdYo7dAOfPMM/Poo4/m8ssvT319facc85e//GVeeeWVHH/88bX4nSQbb7xxjjvuuMydOzfXXXddp7wWAAAAAABdZ40N4A888EDOPPPMTJo0KR/4wAfedv6dd96Zs88+O+eee26uv/76vPrqqyucd/vttydJxowZ0+axsWPHJknuuOOOji8cAAAAAIA1whq5BcqiRYtyxBFHZLvttsvJJ5+8Us+ZNGlSq6/79euXCy+8MEcccUSr8VmzZiVJNttsszbHWD62fE57a1u0aFHt6+bm5iSvb9fS0tKSJKmrq0t9fX2WLl2aZcuW1eYuH1+yZEmq1WptvL6+PnV1de2OLz/uct26vf7btmTJkpUa7969e5YtW5alS5fWxiqVSrp169buOACsidp7b12b33Odk3N6t88JANY0LS0tRb3nvvk1gK61RpbOb33rW5k1a1b+53/+5223PvngBz+Yyy+/PLvvvns23HDDvPDCC/nd736Xb33rW5kwYUL69euX/fbbrza/qakpSdLY2NjmWA0NDa3mrMhZZ52V008/vc349OnTs8466yRJ3ve+92X77bfPQw89lKeeeqo2Z4sttsiIESNy//3358UXX6yNb7fddhk6dGjuvPPOzJs3rzb+0Y9+NIMGDcr06dNb/YW7xx57pHfv3rnxxhtbrWHffffNwoULM2PGjNpYt27dMm7cuMydOzf33ntvbbxv37752Mc+lqeffjozZ86sjQ8cODA77bRTu+cPAF1p1qxZeeKJJ2pfl/Ce65yc07t9TgCwprnxxhuLes9dsGBB53xjgE5Rqb7xn8PWAPfee2923nnnTJ48OaeddlptfMKECbnyyitz77335iMf+cjbHufWW2/N3nvvna233joPPfRQbXzzzTfPrFmz0tLS0uZO55aWlvTo0SPbbrttHnzwwRUed0V3gA8ZMiRz586tBfS18V8n3zxeqVRWeP7Aqqv+rKtXAIU4tFrEHUFvHndOzundPqcePXoE6Byu86BztHx6cVHvuc3NzRkwYECamppqrQjoOmvUHeBLlizJ+PHjs+222+brX//6OzrWnnvumU033TQPP/xwmpuba3/hLL/zu6mpKeuvv36r5yzfzmRFd4cv17Nnz/Ts2bPNePfu3dO9e/dWY/X19Su8g729LUbaG3/zcTsyXldXl7q6tlu+tzcOAGui9t5b1+b3XOfknNpb46qOu94DYG31xve1Et5z2zsW0DXWqCvhV199NbNmzcrMmTPTo0ePVCqV2q8rr7wyyes/slKpVHL99de/7fEGDBiQJK1+9OSt9vl+q/3BAQAAAABYu6xRd4D37Nkzn/vc51b42J133plZs2Zlv/32y8CBAzNs2LC3PNb8+fPz6KOPpk+fPrUQniS77bZb/uM//iPTp09vs5XKtGnTanMAAAAAAFi7rXF7gLdnRXuAz5s3L88//3w233zzVnMXLlyYz33uc/mP//iPTJw4MZdffnntsZdffjnDhw9P9+7d84c//CEbb7xxkuSZZ57J9ttvnyT561//mr59+67Uupqbm9PY2Fjcvk72AIfOY29I6CSHrhWXLLDGc50Hncd1HnSSwq7zSm1FsLZao+4AX1UvvfRSRowYkVGjRmXLLbfMBhtskL/97W+55ZZb8swzz2SbbbbJueee2+o5/fv3z8UXX5zDDz88I0eOzGc+85kkyTXXXJOXXnop11xzzUrHbwAAAAAA1lxrdQBfb7318sUvfjH3339/brzxxrz88svp3bt3ttxyy5xwwgk57rjj0rt37zbPO+ywwzJgwIBMmTIlU6dOTaVSyYc+9KF885vfzF577dUFZwIAAAAAQGdba7ZAWVOV+mMtfjQWOo8fjYVOUtiPxkJXcZ0Hncd1HnSSwq7zSm1FsLaq6+oFAAAAAADA6iCAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUKQOB/A777wzTz311FvOefrpp3PnnXeu0nFfe+21nHTSSdl1110zePDg9OrVKxtssEFGjx6dqVOnpqWlpc1zmpubc9JJJ2Xo0KHp2bNnhg0bln/913/Nq6++usLXWLZsWS666KJss8026d27dwYOHJhDDjkkf/3rX1dprQAAAAAArLk6HMD32GOPXHHFFW8556c//Wn22GOPVTruq6++mksvvTSVSiXjxo3LSSedlAMOOCDPPvtsjjzyyPzzP/9zli1bVps/f/787LbbbrngggsyYsSIfOUrX8kWW2yR733ve/nYxz6W1157rc1rfP7zn88JJ5yQarWaE044Ifvss09+/etfZ9SoUZk1a9YqrRcAAAAAgDVTt44+sVqtvu2cZcuWpVKprNJx11tvvTQ1NaVHjx6txpcsWZK9994706dPz0033ZRx48YlSc4555zMnDkzp5xySr773e/W5n/961/P2WefnQsuuCDf+MY3auMzZszIj3/84+y66665+eaba69z6KGHZt99981xxx2XadOmrdKaAQAAAABY86zWPcBnzZqVxsbGVXpOXV1dm/idJN26dcsBBxyQJPnzn/+c5PUI/+Mf/zjrrrtuTjvttFbzTzvttKy77rr58Y9/3Gr8Rz/6UZLkO9/5TqvX+fjHP57dd98906dPf9utXQAAAAAAWPOt0h3gRx55ZKuvr7/++syZM6fNvKVLl9b2//74xz/+jha43LJly/Jf//VfSZKtt946yeuB/bnnnsvYsWPTp0+fVvP79OmT0aNHZ9q0aXn66aczZMiQJMntt99ee+zNxo4dm9tvvz133HFHDj/88E5ZNwAAAAAAXWOVAvgb9/yuVCqZOXNmZs6cucK5lUolo0aNygUXXNChhS1evDhTpkxJtVrNSy+9lFtvvTWPP/54Jk6cmD333DNJavt1b7bZZis8xmabbZZp06Zl1qxZGTJkSObPn5/nn38+W2+9derr61c4/43HXZFFixZl0aJFta+bm5uTJC0tLbUP6Kyrq0t9fX2WLl3aar/y5eNLlixptYVMfX196urq2h1/8wd/duv2+m/bkiVLVmq8e/fuWbZsWZYuXVobq1Qq6datW7vjALAmau+9dW1+z3VOzundPicAWNO0tLQU9Z775tcAutYqlc7Zs2cneX3rkU022SQnnnhivvzlL7eZV19fn/79+7e5K3tVLF68OKeffnrt60qlkq997Ws566yzamNNTU1J0u42Kw0NDa3mrer8FTnrrLNarWu56dOnZ5111kmSvO9978v222+fhx56qNV2KltssUVGjBiR+++/Py+++GJtfLvttsvQoUNz5513Zt68ebXxj370oxk0aFCmT5/e6i/cPfbYI717986NN97Yag377rtvFi5cmBkzZtTGunXrlnHjxmXu3Lm59957a+N9+/bNxz72sTz99NOt/hFj4MCB2Wmnndo9fwDoSrNmzcoTTzxR+7qE91zn5Jze7XMCgDXNjTfeWNR77oIFCzrnGwN0ikp1ZT7NcgWuvPLKbL/99tl22207e02tLFu2LM8991x++9vf5t/+7d+y1VZb5cYbb0xDQ0N+/vOf57Of/WxOPfXUnHHGGW2ee+qpp2bKlCn59a9/nQMOOCDPPfdcNtpoo4wePTp33XVXm/k333xzxowZkxNOOCEXXnjhCtezojvAhwwZkrlz59YC+tr4r5NvHl/VDy8F2lf9WVevAApxaLWIO4LePO6cnNO7fU4r+rwdoGNc50HnaPn04qLec5ubmzNgwIA0NTXVWhHQdTq818X48eM7cx3tqqury8Ybb5xjjz02AwYMyEEHHZQzzzwzZ599du1O7vbu2F6+Pcnyeas6f0V69uyZnj17thnv3r17unfv3mqsvr5+hVuttLfFSHvjbz5uR8br6upSV9f2M0/bGweANVF7761r83uuc3JO7a1xVcdd7wGwtnrj+1oJ77ntHQvoGu94s+f7778/v//97/PKK6+scG/BSqWS00477Z2+TJJkzJgxSV7/IMvk7ffsfvMe4X369MmGG26Y2bNnZ+nSpW3+4ny7PcUBAAAAAFh7dDiA/+Mf/8j++++fu+++O2+1i0pnBvDnnnsuyf//17fNNtssgwcPzt1335358+e32nN8/vz5ufvuuzN8+PAMGTKkNr7bbrvlF7/4Re6+++7suuuurY4/bdq0JGkzDgAAAADA2qfDPwt50kkn5a677spuu+2WqVOn5uabb86MGTPa/LrttttW6biPPfbYCj8sYMGCBTnppJOSvP5BBMnrcf2oo47Kq6++mu985zut5n/nO9/Jq6++mqOPPrrV+DHHHJMkOe2007J48eLa+E033ZTbb789Y8aMydChQ1dpzQAAAAAArHk6/CGYAwYMyPvf//7ce++9nfqBiZMnT87555+fnXfeOcOGDUtDQ0OeffbZ3HTTTXnppZeyyy67ZNq0aendu3eS1+/0Hj16dB588MGMGTMmI0eOzAMPPJDp06dn1KhRueOOO2pzlzv66KPz4x//OFtttVXGjRuX559/Ptdcc03WXXfd3Hvvvdl8881Xer3Nzc1pbGws7oMNfAgmdB4fjgSd5NAOXbIAb+I6DzqP6zzoJIVd55XaimBt1eEtUBYuXJhdd9210y+g//mf/znPPfdc7rnnntx777159dVX09jYmG233TYHH3xwjjzyyFYfZNCnT5/ccccdmTx5cn71q19lxowZ2XDDDfPVr341kyZNahO/k+Syyy7LNttskx/+8Ie58MILs+666+aAAw7ImWeemU033bRTzwcAAAAAgK7R4TvAR48enY022ijXXnttZ69prVLqv+q5Mwg6jzuDoJMUdmcQdBXXedB5XOdBJynsOq/UVgRrqw7vAT5p0qT853/+Z+67777OXA8AAAAAAHSKDm+B8sILL2TcuHHZbbfd8tnPfjYjR45s91+1jjjiiA4vEAAAAAAAOqLDW6DU1dWlUqnkjU9/849TVqvVVCqVLF269J2tcg1W6o+1+NFY6Dx+NBY6SWE/GgtdxXUedB7XedBJCrvOK7UVwdqqw3eAT506tTPXAQAAAAAAnarDAXz8+PGduQ4AAAAAAOhUHf4QTAAAAAAAWJN1+A7wp556aqXnvu997+voywAAAAAAQId0OIAPGzZspT5Ap1KpZMmSJR19GQAAAAAA6JAOB/AjjjhihQG8qakpDz74YGbPnp3ddtstw4YNeyfrAwAAAACADulwAL/iiivafaxarea8887LOeeck5/85CcdfQkAAAAAAOiw1fIhmJVKJV/72tey1VZb5V//9V9Xx0sAAAAAAMBbWi0BfLkddtght9122+p8CQAAAAAAWKHVGsD/8pe/+ABMAAAAAAC6RIf3AG/PsmXL8uyzz+aKK67Ib37zm+y5556d/RIAAAAAAPC2OhzA6+rqUqlU2n28Wq2mf//+Oe+88zr6EgAAAAAA0GEdDuC77rrrCgN4XV1d+vfvn1GjRmXixIkZNGjQO1ogAAAAAAB0RIcD+O23396JywAAAAAAgM61Wj8EEwAAAAAAukqnfAjm3XffnZkzZ6a5uTkNDQ3ZbrvtMnr06M44NAAAAAAAdMg7CuD33HNPJk6cmD//+c9JXv/gy+X7gm+22WaZOnVqPvrRj77zVQIAAAAAwCrqcAB/9NFHM2bMmCxYsCB777139thjj2y44YZ54YUXMmPGjEyfPj1jx47Nfffdlw984AOduWYAAAAAAHhbHQ7g3/72t7N48eLceOON2WeffVo9dsopp+S//uu/st9+++Xb3/52fvGLX7zjhQIAAAAAwKro8Idg3n777TnwwAPbxO/l9tlnnxx44IGZMWNGhxcHAAAAAAAd1eEA3tTUlOHDh7/lnOHDh6epqamjLwEAAAAAAB3W4QA+ePDg3HfffW8557//+78zePDgjr4EAAAAAAB0WIcD+H777Zfbb789p512Wl577bVWj7322muZNGlSZsyYkX/5l395x4sEAAAAAIBVValWq9WOPPGll17KjjvumNmzZ2f99dfPhz/84fzTP/1T/va3v+X3v/99XnzxxWyyySa5//77s95663X2utcYzc3NaWxsTFNTUxoaGrp6OZ2mUql09RKgGNWfdfUKoBCHduiSBXgT13nQeVznQScp7Dqv1FYEa6tuHX3i+uuvn/vuuy8nn3xyfvGLX+TGG2+sPdarV69MnDgxZ599dtHxGwAAAACANVeHA3iSDBgwIJdffnkuu+yyPP7442lubk5DQ0NGjBiR7t27d9YaAQAAAABgla1yAD/zzDMzf/78nH766bXI3b1792yzzTa1OYsXL86pp56avn375utf/3rnrRYAAAAAAFbSKn0I5i233JJvfetbWX/99d/yDu8ePXpk/fXXz6mnnpoZM2a840UCAAAAAMCqWqUA/tOf/jT9+/fPcccd97Zzv/SlL2W99dbL1KlTO7w4AAAAAADoqFUK4Pfcc0/22muv9OzZ823n9uzZM3vttVfuvvvuDi8OAAAAAAA6apUC+HPPPZdNNtlkpecPHz48zz///CovCgAAAAAA3qlVCuB1dXVpaWlZ6fktLS2pq1ullwAAAAAAgE6xSnV68ODBeeSRR1Z6/iOPPJKNNtpolRcFAAAAAADv1CoF8F122SW33XZb5syZ87Zz58yZk9tuuy277rprR9cGAAAAAAAdtkoB/Etf+lJaWlpy4IEHZu7cue3Oe+mll/LpT386S5YsybHHHvuOFwkAAAAAAKuq26pMHjlyZE488cR8//vf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4dzJkyenUqm0+2vOnDkrfN60adOy2267pW/fvmloaMgee+yRW2+9dfWdFAAAAAAA76puXb2AFfnmN7+ZJ598MgMGDMiGG26YJ5988m2fM378+AwbNqzNeL9+/dqMXX311Tn88MMzcODATJgwIUlyzTXXZO+99861116bAw888B2eAQAAAAAAXW2NDOA//vGPs9lmm2Xo0KH57ne/m2984xtv+5wJEyZk9913f9t5L7/8co4//vgMGDAgDzzwQDbeeOMkySmnnJLtt98+xx57bMaOHZu+ffu+09MAAAAAAKALrZFboOy1114ZOnToajn2L3/5y7zyyis5/vjja/E7STbeeOMcd9xxmTt3bq677rrV8toAAAAAALx71sgA3hF33nlnzj777Jx77rm5/vrr8+qrr65w3u23354kGTNmTJvHxo4dmyS54447Vts6AQAAAAB4d6yRW6B0xKRJk1p93a9fv1x44YU54ogjWo3PmjUrSbLZZpu1OcbyseVzVmTRokVZtGhR7evm5uYkSUtLS1paWpIkdXV1qa+vz9KlS7Ns2bLa3OXjS5YsSbVarY3X19enrq6u3fHlx12uW7fXf9uWLFmyUuPdu3fPsmXLsnTp0tpYpVJJt27d2h0HgDVRe++ta/N7rnNyTu/2OQHAmqalpaWo99w3vwbQtdb60vnBD34wl19+eXbfffdsuOGGeeGFF/K73/0u3/rWtzJhwoT069cv++23X21+U1NTkqSxsbHNsRoaGlrNWZGzzjorp59+epvx6dOnZ5111kmSvO9978v222+fhx56KE899VRtzhZbbJERI0bk/vvvz4svvlgb32677TJ06NDceeedmTdvXm38ox/9aAYNGpTp06e3+gt3jz32SO/evXPjjTe2WsO+++6bhQsXZsaMGbWxbt26Zdy4cZk7d27uvffe2njfvn3zsY99LE8//XRmzpxZGx84cGB22mmnds8fALrSrFmz8sQTT9S+LuE91zk5p3f7nABgTXPjjTcW9Z67YMGCzvnGAJ2iUn3jP4etgZZ/CObUqVMzYcKElX7erbfemr333jtbb711Hnroodr45ptvnlmzZqWlpaXNnc4tLS3p0aNHtt122zz44IMrPO6K7gAfMmRI5s6dWwvoa+O/Tr55vFKprPD8gVVX/VlXrwAKcWi1iDuC3jzunJzTu31OPXr0CNA5XOdB52j59OKi3nObm5szYMCANDU11VoR0HXW+jvA27Pnnntm0003zcMPP5zm5ubaXzjL7/xuamrK+uuv3+o5y7czWdHd4cv17NkzPXv2bDPevXv3dO/evdVYfX196uvr28xtb4uR9sbffNyOjNfV1aWuru2W7+2NA8CaqL331rX5Pdc5Oaf21riq4673AFhbvfF9rYT33PaOBXSNoq+EBwwYkCStfvTkrfb5fqv9wQEAAAAAWLsUG8Dnz5+fRx99NH369KmF8CTZbbfdkry+Z/ebTZs2rdUcAAAAAADWXmt1AJ83b17+9Kc/tRlfuHBhjj766MybNy8HHXRQqx97Oeigg9LY2JiLLroozzzzTG38mWeeycUXX5wBAwbkgAMOeFfWDwAAAADA6rNG7gH+4x//OHfddVeS5OGHH66N3X777UmSnXfeOUcddVReeumljBgxIqNGjcqWW26ZDTbYIH/7299yyy235Jlnnsk222yTc889t9Wx+/fvn4svvjiHH354Ro4cmc985jNJkmuuuSYvvfRSrrnmmvTt2/fdO1kAAAAAAFaLNTKA33XXXbnyyitbjd199925++67a18fddRRWW+99fLFL34x999/f2688ca8/PLL6d27d7bccsuccMIJOe6449K7d+82xz/ssMMyYMCATJkyJVOnTk2lUsmHPvShfPOb38xee+212s8PAAAAAIDVr1KtVqtdvYi1WXNzcxobG9PU1JSGhoauXk6nqVQqXb0EKEb1Z129AijEoS5ZoDO4zoPO4zoPOklh13mltiJYW63Ve4ADAAAAAEB7BHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLAAQAAAAAokgAOAAAAAECRBHAAAAAAAIokgAMAAAAAUCQBHAAAAACAIgngAAAAAAAUSQAHAAAAAKBIAjgAAAAAAEUSwAEAAAAAKJIADgAAAABAkQRwAAAAAACKJIADAAAAAFAkARwAAAAAgCIJ4AAAAAAAFEkABwAAAACgSAI4AAAAAABFEsABAAAAACiSAA4AAAAAQJEEcAAAAAAAiiSAAwAAAABQJAEcAAAAAIAiCeAAAAAAABRJAAcAAAAAoEgCOAAAAAAARRLA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", - "text/plain": [ - "
" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_histogram([counts, second_counts], legend=legend, sort='desc', figsize=(15,12), \n", - " color=['orange', 'black'], bar_labels=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using the output from plot_histogram()\n", - "\n", - "When using the `plot_histogram()` function, it returns a `matplotlib.Figure` for the rendered visualization. Jupyter notebooks understand this return type and render it for us in this tutorial, but when running outside of Jupyter you do not have this feature automatically. However, the `matplotlib.Figure` class natively has methods to both display and save the visualization. You can call `.show()` on the returned object from `plot_histogram()` to open the image in a new window (assuming your configured matplotlib backend is interactive). Or alternatively you can call `.savefig('out.png')` to save the figure to `out.png`. The `savefig()` method takes a path so you can adjust the location and filename where you're saving the output." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Plot State " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In many situations you want to see the state of a quantum computer. This could be for debugging. Here we assume you have this state (either from simulation or state tomography) and the goal is to visualize the quantum state. This requires exponential resources, so we advise to only view the state of small quantum systems. There are several functions for generating different types of visualization of a quantum state\n", - "\n", - "```\n", - "plot_state_city(quantum_state)\n", - "plot_state_qsphere(quantum_state)\n", - "plot_state_paulivec(quantum_state)\n", - "plot_state_hinton(quantum_state)\n", - "plot_bloch_multivector(quantum_state)\n", - "```\n", - "\n", - "A quantum state is either a density matrix $\\rho$ (Hermitian matrix) or statevector $|\\psi\\rangle$ (complex vector). The density matrix is related to the statevector by \n", - "\n", - "$$\\rho = |\\psi\\rangle\\langle \\psi|,$$\n", - "\n", - "and is more general as it can represent mixed states (positive sum of statevectors) \n", - "\n", - "$$\\rho = \\sum_k p_k |\\psi_k\\rangle\\langle \\psi_k |.$$\n", - "\n", - "The visualizations generated by the functions are:\n", - "\n", - "- `'plot_state_city'`: The standard view for quantum states where the real and imaginary (imag) parts of the density matrix are plotted like a city.\n", - "\n", - "- `'plot_state_qsphere'`: The Qiskit unique view of a quantum state where the amplitude and phase of the state vector are plotted in a spherical ball. The amplitude is the thickness of the arrow and the phase is the color. For mixed states it will show different `'qsphere'` for each component.\n", - "\n", - "- `'plot_state_paulivec'`: The representation of the density matrix using Pauli operators as the basis $\\rho=\\sum_{q=0}^{d^2-1}p_jP_j/d$.\n", - "\n", - "- `'plot_state_hinton'`: Same as `'city'` but where the size of the element represents the value of the matrix element.\n", - "\n", - "- `'plot_bloch_multivector'`: The projection of the quantum state onto the single qubit space and plotting on a bloch sphere." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:38.155610Z", - "start_time": "2021-07-31T05:08:38.152536Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:14.580619Z", - "iopub.status.busy": "2023-08-25T18:25:14.580076Z", - "iopub.status.idle": "2023-08-25T18:25:14.584263Z", - "shell.execute_reply": "2023-08-25T18:25:14.583502Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.visualization import plot_state_city, plot_bloch_multivector\n", - "from qiskit.visualization import plot_state_paulivec, plot_state_hinton\n", - "from qiskit.visualization import plot_state_qsphere" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:53.778558Z", - "start_time": "2021-07-31T05:08:53.767409Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:14.587661Z", - "iopub.status.busy": "2023-08-25T18:25:14.587205Z", - "iopub.status.idle": "2023-08-25T18:25:14.599107Z", - "shell.execute_reply": "2023-08-25T18:25:14.598274Z" - } - }, - "outputs": [], - "source": [ - "# execute the quantum circuit \n", - "backend = BasicAer.get_backend('statevector_simulator') # the device to run on\n", - "result = backend.run(transpile(bell, backend)).result()\n", - "psi = result.get_statevector(bell)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:55.480726Z", - "start_time": "2021-07-31T05:08:54.964494Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:14.602902Z", - "iopub.status.busy": "2023-08-25T18:25:14.602620Z", - "iopub.status.idle": "2023-08-25T18:25:15.064751Z", - "shell.execute_reply": "2023-08-25T18:25:15.063989Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABBcAAAGpCAYAAADWRN6TAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOz9eXgkZ3nvjX+relPvi/Z9mdHs45nxrJoxGGPjgXA4kF8cbN73DbFPYjjZSI79noBNYichMUt8EpKc5JDEgJ2AE7/ExJCA8YI3jBdsj6TRNtpGo9FoGUndraX37qr6/SGq3C21pF6qq6pr7s91zQWWurueKnU99/N863vfNyMIggCCIAiCIAiCIAiCIIgCYdUeAEEQBEEQBEEQBEEQ5Q2JCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJCwRBEARBEARBEARBFAWJC4RmEAQBPM9DEAS1h0IQBEEQBEHIDK3zCELfGNUeAEEAa8JCMplELBYDABiNRhiNRhgMBrAsC4ZhVB4hQRAEQRAE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", - "text/plain": [ - "
" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_state_city(psi)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:56.152890Z", - "start_time": "2021-07-31T05:08:55.944830Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:15.069341Z", - "iopub.status.busy": "2023-08-25T18:25:15.068583Z", - "iopub.status.idle": "2023-08-25T18:25:15.385365Z", - "shell.execute_reply": "2023-08-25T18:25:15.384499Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAGmCAYAAADh6xL7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAkCklEQVR4nO3df5BV9X0//tddfuzyY3c1YvihiIpoOjGVsWp0oCohylRrTRt/oGkyYEDUplVjBKFYSamCSoOdOlHjRsE6RlAb7dRJESOsstHIqFiaEYsQ17UEq0m7FwQX0Pv9I9/dZj8suJf3Xu69ex+PmTvDnvO+97zue5bz2uc9P24ml8vlAgAAIEFVsQsAAADKn2ABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUU0JIlSyKTyXQ8Jk+enPR6GzZs6PR6Rx99dM8UCsBBpT/QG/UtdgFQLG+//XYcc8wxnZb17ds3PvvZz8a4ceNi5syZccopp/TIti688MIYO3ZsnHjiiUmvM2TIkLjlllsiIuKuu+7qgcoA2Jf2PjFp0qT4t3/7t4JsQ3+gNxEsqHijR4+OP//zP4+IiA8//DBeeeWVeOyxx+LJJ5+MZ599Ns4888zkbXzlK1+JKVOmJL/OkCFDYt68eRHx20+7AChv+gO9iWBBxTvuuOM6dsbtFi5cGLNnz46bb745Ghsbi1MYAEAZcY0FdOGb3/xmRES88sorXa5//vnn44ILLoghQ4ZEdXV1jBkzJubOnRs7duxI2u7jjz8eY8eOjYEDB0b//v3jpJNOiqeeeirpNQHoOatXr45MJhPz5s2Ln/3sZzFhwoSora2Nww8/PK655prYuXNnREQ8/fTTccYZZ8SgQYNi6NChMXPmzNizZ88Bb1d/oBwIFrAfffvufVDvnnvuibPPPjuampri/PPPj7/6q7+KI488Mm699dY455xzYteuXXlv5+OPP46pU6fGxRdfHIccckhce+21cfHFF8cvfvGL+OpXvxovv/xyT7wdAHrIz3/+85g4cWLU19fHjBkz4qijjop77rknpk+fHsuWLYuLLrooRo0aFTNmzIhDDjkk7rzzzrjtttvy3o7+QFnJQYX65S9/mYuI3KRJk/Zad9ttt+UiInf++ed3Wv6LX/wi17dv39xJJ52U++CDDzqtW7BgQS4icosWLepY9uCDD+YiIvfggw/ut5ZZs2blIiJ33333dVq+ePHiXETkvva1r+31nFGjRuVGjRr1Ke8SgAPVVZ9YtWpVLiJyEZF78sknO5bv2rUr9/u///u5TCaTGzJkSO7ll1/uWJfNZnOf/exnc5/5zGdyu3btyuVy+gO9kyMWVLy33nor5s2bF/PmzYsbb7wxvvSlL8WcOXNi6NChceedd3Yae99998WePXviH//xH+Owww7rtG7mzJlx+OGHx49+9KO8tv/mm2/G9773vbjkkkviyiuv7LTu0ksvjYiI9evXH8A7A6BQJkyYEBdeeGHHz/369YuLLroocrlcXHDBBXHqqad2rKutrY0//uM/jt/85jfx7rvvdnsb+gPlxsXbVLxNmzbFd7/73U7Lhg0bFi+88EIcd9xxnZa/9NJLERGxYsWK+OlPf7rXa/Xr1y82bNiQ1/Yffvjh2L17d1x33XV7rRs4cGBERPTp0yev1wSgsMaOHbvXsuHDh3/qui1btux1q/N90R8oN4IFFe9370/+/vvvx9KlS2PWrFnxJ3/yJ/Hyyy/H4MGDO8b+5je/iYiIW2+9tce2/6//+q/xmc98Jk4//fS91rV/sjV69Oge2x4A6erq6vZa1n5d3v7W7d69u9vb0B8oN06Fgt9x+OGHx3e+852YM2dOvPHGGzF37txO69ubRTabjVwut89Hd+3YsSP+/d//PY444ojIZDJ7rX/66acjImLixIkJ7wqAcqM/UI4EC+jCnDlzYsSIEfH9738/3n777Y7lX/ziFyPi/06JSrVu3br45JNPurxN7Y4dO+IHP/hBDBo0qONcWgAqg/5AORIsoAsDBgyIWbNmxe7du2P+/Pkdy6+55pro27dv/OVf/mW88847ez3vf//3f+O1117r9nZeffXViPjtdR6/+7yPP/44/uIv/qLj+o9DDz004d0AUG70B8qRayxgH6688sq4/fbb46GHHoo5c+bE6NGj48QTT4zvf//7cfXVV8cJJ5wQ5513XowePTq2bdsWmzdvjsbGxpgyZUrce++93dpGe+M4//zzY+LEiXH55ZfHgAED4umnn4433ngjpk2bFt/+9rcL+TYBKEH6A+XIEQvYh5qampg9e3bs2bOn012jpk+fHi+++GJ85StfiZdeeinuuuuuePzxx+ODDz6I66+/vsu7d+zLq6++GocddlgsX748Lr300li+fHncfffdMWjQoHjooYfi/vvv7/LcWgB6N/2BcpTJ5XOlKZCXJUuWxNSpU+PBBx+MKVOmdFrX1tYWtbW1ceaZZ8azzz6b92sfffTRERGdrgEBoDzoD/RGjljAQTB16tTIZDIxefLkjmXr16+P3bt3d3m/833ZsGFDZDKZyGQy0dzcXIBKATiY9Ad6E9dYQAGNHTs2brnllo6fTzzxxI5/t58/m0/jGDJkSKfXO+SQQ5JrBODg0x/ojZwKBUVy9dVXx7333hvr16/v1FAAqGz6A+VKsAAAAJK5xgIAAEgmWAAAAMnK6uLtTz75JLZs2RK1tbXu3QzQQ3K5XGzbti1GjBgRVVXl+3mTHgHQ8/LpEWUVLLZs2RIjR44sdhkAvVJLS0sceeSRxS7jgOkRAIXTnR5RVsGitrY2In77xurq6opcTe/zwAMPxHvvvVfsMmC/hg4dGldccUWxy+hVstlsjBw5smMfW67a67/++uujurq6yNUA9A5tbW2xePHibvWIsgoW7Ye26+rqBIsCGDBgQNTU1BS7DNivAQMG+P9fIOV++lB7/dXV1fZlAD2sOz2ifE+mBQAASoZgAQAAJBMsAACAZIIFAACQTLAAAACSCRYAAEAywQIAAEgmWAAAAMkECwAAIJlgAQAAJBMsAACAZIIFAACQTLAAAACSCRYAAEAywQIAAEgmWAAAAMkECwAAIJlgAQAAJBMsAACAZIIFAACQTLAAAACSCRYAAEAywQIAAEgmWAAAAMkECwAAIJlgAQAAJBMsAACAZIIFAACQTLAAAACSCRYAAEAywQIAAEgmWAAAAMkECwAAIJlgAQAAJDvgYLF27do477zz4pBDDolBgwbF6aefHsuXL+9y7K9+9av45je/GcOHD4+ampo44YQT4tZbb43du3cfcOEAlCb9AaAy9T2QJ61atSomTZoUNTU1MXny5KitrY0nnngiLr300mhpaYkbbrihY+zWrVvji1/8Yrz77rvxp3/6pzFmzJhobGyMuXPnxssvvxxPPvlkZDKZHntDABSP/gBQufIOFnv27Inp06dHVVVVPP/88zF27NiIiPibv/mbOO2002LOnDlx0UUXxahRoyIiYtasWdHS0hL33HNPXHXVVRERkcvl4vLLL49HH300Hn300bjssst67h0BUBT6A0Bly/tUqOeeey42bdoUl19+eUfTiIior6+POXPmxK5du2Lp0qUREbFt27ZYtmxZHHvssTFjxoyOsZlMJhYuXBgREffff3/iWwCgFOgPAJUt7yMWq1evjoiIc889d691kyZNioiIxsbGiIh48cUXo62tLc4555y9DmePGjUqTjjhhGhqaoqPP/44+vTps9frtbW1RVtbW8fP2Ww233IBOEgOZn+I0CMASk3eRyw2btwYERFjxozZa92wYcNi8ODBHWP2N7Z9+a5du6K5ubnL9QsWLIj6+vqOx8iRI/MtF4CD5GD2hwg9AqDU5B0sWltbI+K3h7a7UldX1zGmO2N/d9z/a/bs2dHa2trxaGlpybdcAA6Sg9kfIvQIgFJzQHeFOliqq6ujurq62GUAUIL0CIDSkvcRi/ZPl/b1KVI2m+0Y052xvzsOgPKlPwBUtryDRfv5sO3nx/6urVu3xvbt2zvG7G9s+/L+/fvHUUcdlW8ZAJQY/QGgsuUdLM4666yIiHjmmWf2WrdixYpOY04//fTo379/rFy5MnK5XKexzc3N8eabb8a4ceOib9+SPiMLgG7QHwAqW97BYuLEiXHsscfGI488EuvWretY3traGrfddlv0798/vvGNb0TEby++mzx5cmzevDnuu+++jrG5XC5mz54dERHTp09PfAsAlAL9AaCy5f1RUN++faOhoSEmTZoUZ555ZkyePDlqa2vjiSeeiObm5li0aFEcffTRHeMXLlwYq1atimuuuSaeffbZOO6446KxsTFeeumluOCCC2Ly5Mk9+X4AKBL9AaCy5X3EIiJiwoQJsWbNmhg3blwsW7Ys7rnnnhg6dGg8+uijccMNN3QaO3z48Pj5z38eU6dOjTVr1sTixYvj17/+dcyfPz8ef/zxvb4YCYDypT8AVK4DPnn1tNNOi5/85CfdGjt8+PD44Q9/eKCbAqCM6A8AlemAjlgAAAD8LsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsGCiIjYvXt37Nixo9hlwKfasWNH7N69u9hlAAD/j77FLuBAPPDAAzFgwIBil9Gr7NixI7LZbLHLgE+VzWbj7rvvjoEDBxa7lF5j586dxS4BgF6gLIPFe++9FzU1NcUuAyiSbDYrCPegjz76qNglANALOBUKAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJDihYrF27Ns4777w45JBDYtCgQXH66afH8uXL9xr3/PPPx3e+852YMGFC1NfXRyaTiSlTpqTWDEAJ0yMAKlPffJ+watWqmDRpUtTU1MTkyZOjtrY2nnjiibj00kujpaUlbrjhho6xDzzwQCxdujQGDhwYRx11VGSz2R4tHoDSokcAVK68jljs2bMnpk+fHlVVVfH888/HD37wg/j7v//7eP311+P444+POXPmRHNzc8f4b33rW/Ef//Efkc1m48EHH+zx4gEoHXoEQGXLK1g899xzsWnTprj88stj7NixHcvr6+tjzpw5sWvXrli6dGnH8lNOOSU+//nPR58+fXqsYABKkx4BUNnyCharV6+OiIhzzz13r3WTJk2KiIjGxsb0qgAoO3oEQGXL6xqLjRs3RkTEmDFj9lo3bNiwGDx4cMeYntDW1hZtbW0dPzv/FqB06REAlS2vIxatra0R8dvD2l2pq6vrGNMTFixYEPX19R2PkSNH9thrA9Cz9AiAylbS32Mxe/bsaG1t7Xi0tLQUuyQASoQeAVBa8joVqv1TqH194pTNZuPQQw9Nr+r/V11dHdXV1T32egAUjh4BUNnyOmLRft5sV+fIbt26NbZv397lubUA9H56BEBlyytYnHXWWRER8cwzz+y1bsWKFZ3GAFBZ9AiAypZXsJg4cWIce+yx8cgjj8S6des6lre2tsZtt90W/fv3j2984xs9XSMAZUCPAKhseV1j0bdv32hoaIhJkybFmWeeGZMnT47a2tp44oknorm5ORYtWhRHH310x/g1a9ZEQ0NDRES8//77HcumTJkSERFDhgyJRYsW9cw7AaCo9AiAypZXsIiImDBhQqxZsyZuueWWWLZsWezevTu+8IUvxO233x6XXnppp7FvvfVWp29ZjYjYtGlTbNq0KSIiRo0apWkA9CJ6BEDlyuRyuVyxi+iubDYb9fX1cdNNN0VNTU2xywHoFT766KNYuHBhtLa2Rl1dXbHLOWB6BEDPy6dHlPT3WAAAAOVBsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkyztYPPzwwzFjxow45ZRTorq6OjKZTCxZsmSf47PZbHz729+OUaNGRXV1dRx99NFx4403xvbt21PqBqAE6REAlatvvk+YO3duNDc3x5AhQ2L48OHR3Ny8z7EffvhhnHXWWbFu3bo499xz47LLLovXXnstFi1aFI2NjfH8889HTU1N0hsAoHToEQCVK+8jFg0NDfH222/H+++/H1ddddV+x95xxx2xbt26mDVrVqxYsSIWLlwYK1asiFmzZsXatWtj8eLFB1w4AKVHjwCoXHkHiy9/+csxatSoTx2Xy+WioaEhBg8eHDfffHOndTfffHMMHjw4Ghoa8t08ACVMjwCoXAW7eHvjxo2xZcuWGDduXAwaNKjTukGDBsW4ceNi8+bN0dLSUqgSAChRegRA71PQYBERMWbMmC7Xty9vH9eVtra2yGaznR4AlD89AqD3KViwaG1tjYiI+vr6LtfX1dV1GteVBQsWRH19fcdj5MiRPV8oAAedHgHQ+5T091jMnj07WltbOx4OiQPQTo8AKC153262u9o/hdrXp03th6z39WlVRER1dXVUV1f3fHEAFJUeAdD7FOyIxaedH/tp59cC0HvpEQC9T0GDxYgRI6KpqSk+/PDDTus+/PDDaGpqimOOOcY5sQAVSI8A6H0KFiwymUxMmzYttm/fHvPnz++0bv78+bF9+/aYPn16oTYPQAnTIwB6n7yvsWhoaIg1a9ZERMT69es7lq1evToiIsaPHx/Tpk2LiIiZM2fGU089Fbfffnu89tprcfLJJ8err74azzzzTJx66qlx3XXX9cy7AKAk6BEAlSvvYLFmzZpYunRpp2VNTU3R1NTU8XN70xg0aFA0NjbGvHnz4oknnohVq1bF8OHD44YbbohbbrklBgwYkFg+AKVEjwCoXJlcLpcrdhHdlc1mo76+Pm666aaoqakpdjkAvcJHH30UCxcujNbW1o7vjyhHegRAz8unR5T091gAAADlQbAAAACSCRYAAEAywQIAAEgmWAAAAMkECwAAIJlgAQAAJBMsAACAZIIFAACQTLAAAACSCRYAAEAywQIAAEgmWAAAAMkECwAAIJlgAQAAJBMsAACAZIIFAACQTLAAAACSCRYAAEAywQIAAEgmWAAAAMkECwAAIJlgAQAAJBMsAACAZIIFAACQTLAAAACSCRYAAEAywQIAAEgmWAAAAMkECwAAIJlgAQAAJBMsAACAZIIFAACQrG+xCzgQQ4cOjQEDBhS7jF5lx44dkc1mi10GdEtdXV0MHDiw2GX0Gjt37ix2CQD0AmUZLK644oqoq6srdhm9yu7du+Puu+8WLih5dXV18a1vfSv69etX7FJ6jWw2G9dff32xywCgzDkVioiI6Nevn0+AKQsDBw4UKgCgBAkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAA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", - "text/plain": [ - "
" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_state_hinton(psi)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:58.220624Z", - "start_time": "2021-07-31T05:08:56.933497Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:15.389159Z", - "iopub.status.busy": "2023-08-25T18:25:15.388695Z", - "iopub.status.idle": "2023-08-25T18:25:16.829616Z", - "shell.execute_reply": "2023-08-25T18:25:16.828929Z" - }, - "tags": [ - "nbsphinx-thumbnail" - ] - }, - "outputs": [ - { - "data": { - "image/png": "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DVINBQwhhBBiQTwQx/Wp63F96noAwEJ5EWeL5/F04SwulC5irryAxfIyZNhX4dVQoSAkBDEeHMB0eBg7w2M4EN2B3eExjIUY19IIgqo61D8mhBBCiC2qqiIjb2BdziKnFFBUS1AUBaoQQEAQERXCSASiGBATSAUZx+InFDCEEEII6Tm2dhI5IYQQQnoSChhCCCGE9BwUMIQQQgjpOShgCCGEEACFQgG//du/jRe84AXYtm0botEopqam8LznPQ+f+tSnUC6X6zfikh/96EcQBAHve9/78Gd/9me45ZZbsGvXLoTDYUxNTeE1r3kNHnzwQd+O148wiJcQQggBsLS0hJ07d+LZz342Dh48iPHxcayuruIrX/kKzp49i1tuuQVf+cpXIIrNv/u/+93vxp133om3vvWt+Id/+Afs27cPN954I8bHx/H000/jnnvugaqquPvuu3H77bf7cHb9BwUMIYQQAkBRFEiShHC4uvquJEn4xV/8RXzzm9/Ev/7rv+KlL31p08e65pprEAqF8K53vQujo6N44QtfWLX+O9/5Dm6++WYkk0nMzc0hEonYtLR1oQuJEEIIASCKYo14AYBgMIjbbrsNAHDixAl9+ec//3kIglD3Z9euXVXtnT59Go899hhe+cpX4tWvfnWNeAGAG264ATfddBNWV1fx2GOP+Xym/QEr8RJCCCEOKIqCr371qwCAq666Sl8+Pj6ON73pTbjrrrvw3Oc+F7fccou+7v7778d3v/td3HHHHbj11lur2rvnnnsAAK985SsdjxsKVaYUCAY5VFtBFxIhhBBioFQq4YMf/CBUVcXy8jK+/vWv4/jx43jLW96Cf/iHf6ja9u///u/xtre9DX/913+Nt7/97fry17zmNfjCF76A06dPY2ZmpmqfG2+8EWfPnsXp06dt+3Du3DkcPHgQIyMjOH/+PAIBTupohrKOEEIIMVAqlfC+971P/ywIAn7nd34Hf/Inf1Kz7SOPPAKgEtNi5OGHH8bg4GCNeFleXsYDDzyA3/zN37Q9frlcxh133IFisYgPf/jDFC82MAaGEEIIMZBMJqGqKmRZxvnz5/FXf/VX+Lu/+zvceOONSKfTVds++uijEAQBV199tb4snU7j9OnTNaIGAO677z7IsmzrPlIUBW9+85vx7W9/G7/+67+OO+64w9+T6yMoYAghhBALRFHEjh078I53vAOf/OQn8d3vfhcf+MAHqrZ59NFHsXfvXqRSKX3Zww8/DFVVcfTo0Zo277nnHoyMjOCGG26oWacoCt761rfi7rvvxhve8AZ84hOf8P+k+ggKGEIIIaQOWoDuN7/5TX3ZuXPnsLq6WiNUfvrTnwJAzfJCoYCvfe1reOlLX1oTmKsoCt7ylrfgrrvuwute9zp8+tOf9qXeTD/Dq0MIIYTU4eLFiwA2M4MA5/gXoFbA3H///djY2KhxH2ni5TOf+Qxuv/12fPazn2XciwsoYAghhBAATzzxBHK5XM3yXC6H3/7t3wYAvOQlL9GXP/roowBqhcoTTzwBALjyyiurln/xi19EJBKpSqvW3Eaf+cxn8NrXvhb/+I//SPHiEmYhEUIIIQD++Z//GX/6p3+K5z//+ZiZmcHAwABmZ2fxla98BcvLy7jhhhvw3//7f9e31ywwZgGzsrICAHjooYdw9dVXY2RkBIqi4N5779Wr62q8//3vx1133YVkMomDBw/izjvvrOnXq171Klx77bUtOOPehgKGEEIIAfCyl70MFy9exPe+9z18//vfRzabxeDgIK655hr8yq/8Ct761rdWxa488sgjGBgYqEmVfv3rX4+PfexjuO2223Dffffhuc99Ln7wgx9gfn4er3rVq6q2PXPmDAAgm83WBAhrzMzMUMBYwEJ2hBBCSIv5vd/7PXzkIx/BxYsXMTU11enu9AUUMIQQQkiLOXz4MIaHh/H973+/013pGyhgCCGEENJzMAuJEEIIIT0HBQwhhBBCeg4KGEIIIYT0HBQwhBBCCOk5KGAIIYQQ0nNQwBBCCCGk56CAIYQQQkjPQQFDCCGEkJ6DAoYQQgghPQcFDCGEEEJ6DgoYQgghhPQcFDCEEEII6TkoYAghhBDSc1DAEEIIIaTnoIAhhBBCSM9BAUMIIYSQnoMChhBCCCE9BwUMIYQQQv7/7dYBCQAAAICg/6/bEegKdwQGANgRGABgJ8ryK/iv/JHOAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_state_qsphere(psi)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:58.370300Z", - "start_time": "2021-07-31T05:08:58.223788Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:16.840662Z", - "iopub.status.busy": "2023-08-25T18:25:16.840109Z", - "iopub.status.idle": "2023-08-25T18:25:16.966388Z", - "shell.execute_reply": "2023-08-25T18:25:16.965586Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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XJ1//vnq06eP2rVrp4yMDKPLhcHKri5OSUnRiy++qFtvvVXXX3+9JkyYIJ/PpyVLlmjDhg1avHixSkpK5HK5ZDabtWvXLrVv397g6nEy/XKmeMOGDRo6dKjatWunJ598UkOHDpXFYgm3Ew4hSdnZ2WrZsqX+8Y9/qEOHDnr//fd16aWXasSIEXr77bfVpEmTiO1DoZBmz56tyZMna/fu3Uk3S0ggRLV99913mjdvni699FINGDAgYsDeu3evNm3apK+//lo5OTlau3atXC6XsrOz1a9fP91yyy3q27evgdWjLvD5fLJYLHr//fc1YcIEHT58WG3atNGePXvk8Xhks9nUvXt3DRs2TH369NGwYcOSbpDGMfn5+SooKNCiRYu0ZcsWffjhh8rPz5fNZtPAgQN1wQUX6MILL1TPnj2NLhV1wM6dO9W1a1fNnj1b9913X/jxq6++Wm+99Zb++te/6o477og4RWXv3r367W9/q5KSEq1Zs8bA6o1BIES13XzzzZo7d65atmyp/v3767zzztN5552n008/PbyNz+fTzp07tX79eq1YsUIrVqzQ2rVrtWjRIp1//vkGVg+jeDwefffdd/rqq6+0cuVKLV++XPv375f031nD3/zmN7ryyivVqVMnnX766cz0JKnDhw/rk08+0Ycffqh169Zp586dCoVCatq0qTp37qwhQ4Zo+fLl+uabbyRJzZs31/DhwzV06FCNHTtWLVq0MPgngFHuv/9+vfTSS3rnnXc0ZMgQBYNBmc1m7d27Vz169FCTJk309ddfq3Xr1uG2Dz74QJdffrmeeuop3XbbbUb/CCcdgRDV9v333+vNN9/UO++8o3Xr1kmS2rdvr6FDh2rUqFEaPny42rZtG96+pKREW7du1ZYtW3TdddcZVDWMdtppp+n7778Pn7PTt29f9e7dWxdccIE2bdqkWbNm6bbbbtOcOXPC31O2LZLLuHHj9Oqrr8pisahPnz7q16+fRo4cqZ49e6pDhw4ym83y+/1as2aNFixYoLfffjt8FemSJUs0bNgwg38CGKV169YaOnSonn/+eTVr1kyhUEiBQECpqan685//rHvvvVfXX3+95s2bJ+nYBWuTJ0/Wn//8ZxUXFystLc3YH8AABELUmN/v1zfffKO33npLCxcuDK8I36NHj/C5g0OGDFHDhg0NrhRG27p1qwYPHqxbbrlFWVlZGjNmjGw2W/hCkn379um6667TsmXL9M9//lPXXnstYTCJdenSRc8++6w6dOig7t27R7WXzeyUcTqdysnJ0UcffaTnnnvuZJaKOmT16tXq169f+GPhX/rxxx919dVXa+nSpfroo4904YUX6rvvvtPYsWPVtm1bffLJJwZUbTwCIWpVcXGxlixZojfeeEMff/yxiouLZbVawx8pl32cg+T1008/qVmzZhGPHf+HffXq1Ro8eLC6d++uhQsXqlOnTlF/+FH/ff311xo0aJDuvvtu/fnPfw4/zhsEnMjXX3+tZcuWacyYMerWrVu5febDDz/UJZdcopEjR+qdd97R+++/r9/+9rd65513NGbMGIMqNxaBEHGTn5+vjz76SK+//rq+/PJL+f1+9e3bV6tWrTK6NBhk4cKFuuyyyyJO5D5e2cD9yiuvaPz48RozZozeeecdg6qFkfbt26dx48YpJydH//73v3X55ZdHXW0MlOeVV17RueeeG3HKUnluv/12/e1vf9NDDz2kQ4cO6R//+IeKi4tPUpV1D4EQJ8XWrVs1b948de3aVb/73e+MLgcG+Ne//qVrr71Wq1evVu/evWNuFwwG5fV6deedd+qll17S7NmzNWHCBBY4T0K7du1Snz59dMopp+jzzz9XmzZtmCFEhRYuXKgrrrhCo0eP1ttvv6309PSobcr6UF5engYPHqzCwkKVlpbqhhtu0Ny5cw2oum7gMxicFN27d9fs2bMJg0ms7CTtCRMm6NChQzG3M5vNstvteuCBB8K3KysoKDhZZaKOCAQC6tSpk5555hlt3749vEgwYRAVeemllyRJ//nPf/Tmm29K+u/qBWXK+lBWVpZuu+02+Xw+hUKhcs83TCYEQgAnxSWXXKLbb79dS5cu1fPPPy8p+v7EZV/7fD517txZTz75pE477TS1bNnypNcLY5V9NHz55Zfruuuu08cff6w5c+YoFAopGAxG/ZEHioqK9Mknn+jKK69UMBjUE088oYMHDyolJSXmvdDvvvtuDR8+XMOGDVOvXr1ObsF1DIEQQNyVDcazZ8/WqFGjNH36dH300UcRsz3BYDD8tcVikdvtVkZGhh577DFDaoZxyvpLMBiUw+HQ008/rb59++qFF17QmjVrZDabw4GRcIgyL7/8sqxWq2688UY99dRT2rp1qx588EFJ0TPLZX3sxx9/1MiRIzV58uSTXm9dwzmEAE6KsiuFV6xYoTFjxqhhw4b68MMPwwuZu91u7d27V7m5uVq9erUWL16sffv2KS8vT82bNze4ehghEAjom2++0f79+/XQQw9p48aNslqtuuSSSzR27FhdeOGFSbleHMrXo0cPtWrVSvPnz1ejRo00YsQIbdy4UR9//LFGjhwZtX0oFNK0adP01FNPafv27Um/kDmBEEDc/XLZmJkzZ2r69OkaN26cRo8erXfffVebNm3S5s2bw7M9Xbp00Xnnncd6cklm48aN4XtYr1+/Xhs2bJAkNWvWTKeffrrWrFkjp9MpSWrbtq0uueQSXXnllSxCneR27Nihbt26Rdyq7o033tBvfvMbjRo1Sm+//bYyMjIk/feikrJb1R09ejTcz5IZgRDASeF2u7V//36tXLlSW7Zs0ezZsyPae/Tooc6dO4fXDuvbt6/MZrMsFotBFeNky83N1dlnny1Jstvt6tatm8466yxdfPHFysrK0plnnilJWrBggV566SV9+umn4e/95XqFSC733Xef/vGPf4RvVVfm1ltv1Ysvvqj58+fruuuuC39UbDKZwreqe/rpp3XLLbcYVXqdQSAEEFcvvfSSvv/+e73//vvat2+fXC6XJCk7O1v5+flq3LixHnvsMV188cUsPp3kZs2apRUrVujyyy/X0KFD1axZs/BdbKRjd0U6fvmhn3/+Wf/61780a9YsPffcc/rVr35lQNWoCzIzM3XOOeeEb1VX1le2bt2qoUOHyuFwaNGiReE73ni9Xk2aNEl/+ctfVFJSIofDYfBPYDwCIYC4OX7GJzMzUxdddJG6deumSy+9VBaLRc8++6yeeOIJ3X333Xr88cdlNpsVDAZZfDhJ/TLwSdGnG0j/XUaEfgJJWr58uYYOHaq//e1vuvnmm6PaZ86cqSlTpuimm27S888/L7PZrC1btujKK69U+/bt9fHHHxtQdd1DIAQQNx9++KG+/fZb/epXv9KZZ56plJSUiD/ufr9fI0eO1Jdffqn33ntPF198MbepS3JVWXg6FAopEAiwaHmSu/vuu/Xuu+9q4cKF6tOnT1Qf8ng86tu3r3bu3KlPP/1Uw4YNC98NqWzcAYEQQByV98e97LGy25B99913uvDCC+V2u/Xll1+qW7duBlULIBF5vV599913Ou2002Sz2SLaysaZTz/9VBdddJEGDx6sDz74QH/84x/15ptvqqioyKCq6x7ehgOIG5PJFLUgbFlALFss9vTTT9fUqVN1+PBhTZgwQYWFhUaUCiBBWa1W9erVKyoMSsfGmUAgoPPPP18333yzVqxYoTvvvFMfffSRrrzySgOqrbuYIQRQJ/z2t7/V66+/rscee0z33nuv0eUAqGc2btyoc889V16vV8XFxVq1apX69u1rdFl1BoEQgKHKPkIuKirSOeeco/z8/ArvdQwA1fW3v/1Nt99+u9q0aaP9+/cbXU6dwpm4AAxlMpkUDAbVsGFD/fGPf1RpaanRJQGop66//nodOHBAHTt2NLqUOocZQgAAgCTHRSUAAABJjkAIAACQ5AiEAAAASY5ACAAAkOQIhAAAAEmOQAgAAJDkCIQAUE/l5OTIZDJp2rRpEY8PHz486h7TAJIbgRAAalleXp5MJlPEP6vVqnbt2unaa6/Vhg0bjC4RACJwpxIAiJPOnTvrt7/9rSSppKREubm5euONN7Rw4UL95z//0eDBgw2p65VXXpHL5TLkuQHUTQRCAIiTLl26RH1c++CDD2rmzJmaPHmycnJyDKmrffv2hjwvgLqLj4wB4CS68847JUmrVq3SgQMHNHXqVA0cOFAtW7aUzWZTVlaWbr/9dh06dCjqeys692/8+PEymUzKy8s7YQ2cQwjgl5ghBAADmEwmLVu2THPmzNHIkSM1YMAAWSwWrV27Vs8//7wWLVqkNWvWqFGjRkaXCiAJEAgB4CR67rnnJEn9+/fXueeeq/z8fGVkZERs88orr2jcuHF65plnNHnyZCPKBJBkCIQAECc7duwIn0PodDr19ddf68svv5TdbtfMmTPVsmXLcr/vuuuu05133qnPP/+cQAjgpCAQAkCc7Ny5U9OnT5ckWSwWtWrVStdee60eeOABZWdnS5IWLlyoF154QWvWrNHPP/+sQCAQ/v4DBw4YUjeA5EMgBIA4GT16tD799NOY7XPmzNG9996rFi1aaNSoUWrbtq0cDock6cknn5TH4zlZpQJIcgRCADCA3+/XQw89pDZt2mjdunURHx+HQiE99thjUd9jNpvD35uaGjl8FxYWxrdgAPUay84AgAGOHDmiwsJCDRo0KOpcwm+//ValpaVR39OkSRNJ0v79+yMeDwaDWr9+ffyKBVDvEQgBwAAtW7aUw+HQmjVrIu4a8vPPP4fXKvylfv36SZLmzZsX8fif//xn7d69O261Aqj/+MgYAAxgNpt1++23a86cOTrzzDN18cUXq6ioSJ988ok6dOigzMzMqO+54YYb9Nhjj2natGlat26dOnfurG+//VabNm3SsGHDtHTpUgN+EgD1ATOEAGCQWbNmaebMmTKZTHruuef02Wef6ZprrtHixYtlsViitm/VqpWWLFmikSNHavHixfr73/+uxo0bKzc3V1lZWSf/BwBQb5hCoVDI6CIAAABgHGYIAQAAkhyBEAAAIMkRCAEAAJIcgRAAACDJEQgBAACSHIEQAAAgyREIAQAAkhyBEAAAIMkRCAEAAJIcgRAAACDJEQgBAACSHIEQAAAgyREIAQAAktz/B11fTskrHSqNAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_state_paulivec(psi)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:08:59.932552Z", - "start_time": "2021-07-31T05:08:59.518913Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:16.970636Z", - "iopub.status.busy": "2023-08-25T18:25:16.970134Z", - "iopub.status.idle": "2023-08-25T18:25:17.399215Z", - "shell.execute_reply": "2023-08-25T18:25:17.398371Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_bloch_multivector(psi)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we see that there is no information about the quantum state in the single qubit space as all vectors are zero. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Options when using state plotting functions\n", - "\n", - "The various functions for plotting quantum states provide a number of options to adjust how the plots are rendered. Which options are available depends on the function being used." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**plot_state_city()** options\n", - "\n", - "- **title** (str): a string that represents the plot title\n", - "- **figsize** (tuple): figure size in inches (width, height).\n", - "- **color** (list): a list of len=2 giving colors for real and imaginary components of matrix elements." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:09:02.178208Z", - "start_time": "2021-07-31T05:09:01.864008Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:17.403287Z", - "iopub.status.busy": "2023-08-25T18:25:17.402659Z", - "iopub.status.idle": "2023-08-25T18:25:17.838824Z", - "shell.execute_reply": "2023-08-25T18:25:17.838115Z" - }, - "scrolled": true - }, - "outputs": [ - { - "data": { - "image/png": "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P/Dss8+ybds2pkyZYhY71Go1WVlZBAcH4+bmZhYbJCQkJLqKq1ev8u2337J9+3bi4+O59dZbmTt3LnPmzMHPz6/L/DyDwcDly5dRqVRERUXd4MsIej3FCRtJP/ElqZcuUlb9vyINNlYwa8mzhE37b6vPp9FoRKGhtLQUuVwu+nlyubxD112ReQCnc4twc2heDGkNOj0U13mhsBtNoeN0ho+bh4eHR4fHtXTqK/Mou/AxNoV78LPNw76TspYr6qypHbML1z4TO+cEbaTo6GvIig4iC16IZ/TT2Nh3zKe3NGHhwIED3HfffXz55ZcsWbLELDb8Xvw8ixUXjAiCQGpqKlu3bmXr1q0kJiZy2223MXfuXGbPno2vr2+nTUD19fXExcVhY2NDREREi7UH9Ho93333HV9++SU5OTksX76cOXPmtPv8Go1GDKlTqVTiBOTr64uzs3Obrvutt97i7bffbrctLeHu7s6kSZN44YUXiIqK6rTzWBIKhYLly5ezfft2rly5YjLRxtKYP38+27dvF/8tk8no06cPc+bM4bnnnmtzGowlCQsA27Zt4/HHH+fHH39kxgzz7R489dRT7Ny5k969exMaGsrixYs79PyQkJCQ6A4IgkB2djZbt25l+/btnD17ltGjRzN37lzmzp1Lr169Os3P02q1XLx4EZ1OR2RkZIvRooIgoLy0nfTjn5OTfpGJ97xDwIg/dej8SqWSkpISlEolDg4Oop/n6uraputWxn+Jd9rzONmZ3hcxCFBS44LadTzOkc/jEjTO5OewRHT1VZTGrkDI2YyvLB25vWn+tqVqa864vItXyHh8fHzw9vY2a5H3osMv06dsFdb/X8OwXgvF2l7o/GfgEf089u592zSepQkLhw8fZvHixXz22Wfcd999ZouQ+r34eRYvLjREEAQyMzPZunUr27Zt48KFC4wdO1ZUugMDA012w1RVVREfH28x7RSvn4Ds7e3x8/Nr1QT06KOPsnHjxi6zVS6XM27cOJ555hnuuOOOLjuvOamtrWXNmjV89913XLp0SezR/XvA29ubSZMm8fzzzzN8+PBmj7U0YWH37t08+OCDfPfdd8ydO9dsduj1ej755BOOHTvG6dOnxaiYDz74gGeeecbsFcclJCQkugJBEMjLy2Pbtm1s27aNEydOMGLECObNm8fcuXPp06ePyfy8mpoa4uPjLaadol6vb+Tn2djYiEJDS6nBxcf+Tu+Sj7DtoktQ1DhQ6XQLjkMex63/7G5d2LG1GPRaShPXoUtfh4c2AQ+n9rUZUdXYY5h0BK19sLiBWFdXJ9Ze6+rin4W/PEe/ii9v2jFDb4DiWndq3SfgHPEsLr2bL3RtacLCsWPHWLhwIcuWLePBBx802736e/LzupW40BBBELh69aqodJ86dYpRo0aJSndwcHC7byClUkliYiJ9+vShX79+FvfQ1Ov1qFQqSkpKUCgUWFtbixOQh4eHaK8gCEybNo3jx4+bzVZ7e3tGjhzJI488wsKFCy3ub2lKBEEgJSUFhUJBdnY2GzZsIDY2lvr6enOb1mXI5XJGjx7NY489xowZMxp935YmLOzbt4/777+fdevWsXDhQnObgyAIyGQy0tLSuPfeeykpKWHNmjVmS9OQkJCQMCeCIFBUVMT27dvZunUrv/32G8OGDWPu3LnMmzevQ+2ujW2zAwICGDBggMX5JgaDgdLSUjF9QiaT4ePjg6+vL56eno02vAr33ku/+u1YmekSymptKbONwHbAn/Accl+7W1x2B4ybnLm5uQzyLIHs9bioz+DbyvoWCrUjVtNP4egZ1uh1Y5F3hUJBZWUlbm5uotBg6pocDSn6+Un6Vq9r072jqHGg0nEkDgMfwj28cet6o7AwcOBAevXq1QkWt41Tp04xf/583nnnHZ544gmz/85/L35etxUXGiIIAgUFBaLSffz4cSIiIkSlOyQkpNU3VG5uLunp6YSHhxMQENDJlnec6ycgAF9fX1xcXJg1axYZGRlmtvAaMpmMb775xiIWcZ2BwWAgOTmZyspKRowY0Si08tdff2XlypWcOHGCqqoqM1rZtdja2jJs2DDuvfdeZs2aRUpKisUIC7/88gv33HMPX3zxBUuWLDH7hNOQHTt2sHTpUmbMmMGyZcvw9fUVJyQJCQmJ3yOCIKBUKtmxYwdbt27l8OHDDBw4kDlz5jBv3jzCw8Nb/YwsKiri0qVL3abrgcFgoLy8XPTz9Ho9Pj4++Hh5oT++hP62581tIvD/xSBD3sc7+klzm9IpGIu7FxcXEx0djbOzs/hedf5ZquI/wbH8CH6O5WJ6QUOK1M7YzzwnFsm8GcbaawqFQqzJYRSWXFxcTOYLFB54lH41GzskSlXUWaOShWPdbwmyvneTkJRqMcLC+fPnmTNnDv/617949tlnLcqH6ul+Xo8QFxoiCAIlJSXiBHTkyBHCw8NFoWHgwIFNfnmCIJCenk5BQQGRkZG4u7t3vfEdRBAEysvLuXTpEosWLbKYzgZWVlZs3LixR+YVwbWJPyEhgdraWqKjo7G3t7/psUlJSYwePbrH1me4GcY6DQsXLuTZZ5/Fy6vpVlddwW+//caiRYv49NNPuf/++y3iYW6cVMrLy3n11Vf55ptvWLt2LX/4wx+aPE6pVHLlyhUuXbrE2LFj6d+/v5ksl5CQkOhaBEGgrKyMXbt2sW3bNg4ePEi/fv2YM2cO8+fPZ+jQoU2msho7QmRnZzNs2DB8fHzMYH3HEASByspKigpycDo9j4EellFYulYjQznwE7wiHjK3KZ2CIAhcunSJsrIyoqOjm40mqCvLouzCR9iV7MPPrgg7GyhQu+M0NxY757Z12tBqtWKkslKpxNbW9pqw5OODh4dHu1O2C/f/iZDaH03afrNWAwX1gch6z8Nj5AvYuwSabvA2Eh8fz8yZM/nrX//Kyy+/LPl5XUyPExcaIggCpaWl7Ny5k61bt3Lo0CH69+8vhtQNHjwYKysrKisrOXjwIAEBAU12hOhOJCcnM3HiRGpqasxtCgB2dnbs27eP0aObz9Hqruj1euLj49Hr9URFRbUqZ8rV1fWGriK/N/z8/JgyZQrPPfcc4eHhXXbeEydOsGDBAj788EMefvhhi5hw4H+TybFjx1i6dCn+/v588803hIWF3XDsgQMHeP7550lPT8fe3p66ujruuOMO3nrrLWJiYnqU+i0hISHREhUVFezZs4dt27axf/9+AgICRKEhKioKKysr6urq2LNnD76+vk12hOhO1FfmUbN7NL3kZeY2BYCqOiuqRnyPe9gsc5vSKRgMBpKSkqiuriY6OrpNLeK1NSrK4j/HI+pJbB071mnDGKlsrNNgMBhEocHLywsbG5tWjVO4915C6rebVFi4nmudRjyp87wD18jnkQd0XaH3pKQkpk+fzosvvshf//pXi/GHfk9+Xo8WFxoiCAIVFRXs3r2brVu3cvDgQXr37s3UqVPZv38/gYGB7Nq1q1sX1Dh06BALFixAp2tfkRlTI5fLOXHiRJM/nJ6AVqslPj4emUxGZGRkqx/sLi4uv6uCjy3h4uLCuHHjeOKJJ5g8eXKnnefMmTPMmzePt99+myeffNLiHsw6nY53332Xv//97/z3v//l+eefx97eXoxykclkbN26lfvuuw9nZ2eWLl1KZGQkaWlprFmzhn79+rF3794e3d5IQkJCojmqq6vZu3cv27ZtY+/evXh6ejJt2jROnTqFwWDgyJEjHW6lbE7UhXHIDk/G26nO3KYAUFpjh27CAZx7xZjblE5Br9eTkJBAfX090dHRXVposTmMESzGOg21tbV4enqKYsPNImgL9iyhv253F9sKihonquS34Dj4EdzC5nSa/3X58mWmT5/OE088wRtvvCH5eWbidyMuXE9VVRWrVq3ijTfewMfHBxsbG2bNmsX8+fMZOXKk2btDtJVvvvmGp59+2mLC7b28vDh37hx+fm0LAesuaDQaYmNjsbe3Z/jw4W2qMi2JCzfHzs6OqKgodu/ejVwuN9m4sbGxzJ49m3/+858899xzFjXhGBXolJQUHn74YfLy8ti0adMN0T55eXncfvvtZGZmsnPnTmbPni2+9+mnn/Lss8/y2muv8dZbb3X1JUhISEhYHDU1NWzYsIFXX30VR0dH7O3tmT59OnPnzmXs2LGt3hCwFMoz9uJ8fgmuDpYR+VislmM7/RQOnqHmNqVT0Ol0xMfHIwgCkZGRFr35qFarxYgGY0FIY50GYzR2we6F9NfvM7OlUF5rQ6nNEDynbcPOpW2tzJsjLS2N6dOn88ADD/Df//5X8vPMSPdaQZuQkydP8tZbb/Haa6+RnJzMe++9h1KpFAsDvfLKK5w4caJbhK//61//4qmnnrIYYaFPnz5cvny5xwoLdXV1nD9/HicnJyIiIszevqonodFoGDhwoEmFhYSEBObMmcNrr71mscICXIusuHDhAtOmTWPgwIEAogilVqv54osvyMzM5NlnnxUnHOP799xzD4GBgWRlZTV6XUJCQuL3SkJCAn//+9954IEHyMjIYOXKldTX13PvvfcSFhbGM888w6+//opWqzW3qS2ijF2FR+zdFiMs5Km9sZ+f3GOFBa1WS2xsLDKZjOjoaIsWFuBapHDfvn2JiYlh/PjxBAQEUFpaysmTJzl58iQZ399pEcICgLujDoO9v0mFhaysLGbNmsU999zD22+/Lfl5ZqZ7ybYm5MSJE3z22Wf88Y9/BGDBggUsWLCA2tpafv75Z7Zt28bixYuxt7dn9uzZzJs3j3HjxlncA6aiooKffvoJKysri7jRIiMj+e2333rsgru2tpYLFy7g4eHB4MGDLeoB1hO49957WbVqlcnGu3TpErNmzeKFF17glVdesbjvy2hPYWEhBw4cwNramunTp+PhcS030xhBlZGRwY8//sjgwYNZsmQJcG1iMb7v6OhIeXk5WVlZ1NbWduuwXwkJCQlTcObMGf7+97/zzDPPADBz5kxmzpyJVqvlyJEjbNmyhYcffhitVsusWbOYN28eEydObLYoszkw6LVos35Aa7DCEfOLC1dq++K16DzWdj1znqmvryc2NhZHR0eGDRvW7fxZe3t7evfuTe/evdHpdORvnUa44ylzmyWSKZtM4JxtJhsvJyeHmTNnMm/ePD744AOLizz/Pfp5lvUNdCFvvvmmKCw0xNHRkTlz5vDNN99QVFTEN998gyAILF26lP79+/PUU0/x888/o9FozGD1jbi5uXH27FlKS0v56KOPzLqTfuedd3L8+PFu9yBuLdXV1Zw7dw4fHx9JWOgE/vCHP7B69WqTjZeSksLMmTN5/PHHef311y3u+0pISKCwsBCAzMxMjhw5wrhx44iIiAAQI5H0ej1nzpwhNTWVadOmERkZCdBIUDx16hQajYaAgAAcHR0tQmiUkJCQMCfPPfecKCw0xNbWljvvvJPVq1eTl5fH1q1bcXFx4ZlnnqFfv348/PDD7Nmzh9raWjNYfSNW1rYELDyC9eIK8vqvIVM3irJa82x0ZWqj8bk7occKC8bIVGdn5zanvFoagiCg2DWDcHvLERYyZHcQMHenycbLz89n5syZTJ06leXLl1ucsPB79fMs61uwMOzs7Jg6dSpffPEFBQUF/PDDDzg4OPDEE0/Qr18/HnvsMfbu3UtdnfkL69ja2vLYY49x8uRJKioqWLNmDaNGjeqySIv777+fHTt2WMQCrr6+nlWrVqFWq002ZmVlJefPnycwMJABAwZ06DotJX3Fkli8eDFffPGFycbLyMhg1qxZPPDAA7z55psWcV82RKlU8sgjj7B48WLeeecd1q1bR3FxMYsXL6Zv377A/+4TQRA4cuQILi4u3HrrrTg4OIjvGSfSuLg4dDodkyZNArC465WQkJCwRGxsbJg4cSKffvopOTk57NmzBz8/P/785z/Tr18/HnjgAbZv325Sf6K9yGQyPIf+kYCFR7BbUkbB4O/INIxHWdP5kRaCAJlW0whYdAyZBSy4BUFAGbsKXa3pWq7X1NRw7tw5PDw8btrOtLsgCAJF2yYRanXC3KaIZDKRwLmmKyZZVFTEjBkzuO2221i1apXFfV+/Zz/vd1vQsSPo9XpOnDjBli1b2LFjBxUVFUybNo158+Zx5513WlQrS0EQ2LVrF6tWreLs2bPU19eb/BwPPfQQ//jHP/D09DT7j7uqqoqoqCgKCwuRyWT06dOH+fPn89xzz7W7p3V5eTlxcXH069dPfCB0BGdnZ0lgaMCiRYv45ptvTDZednY206dPZ/78+Xz88cdmvyeboqamhrfeeotly5aJu2NeXl688cYb/OlPf2r0DBEEgV69euHm5sbevXvp169fo8rC+fn5PP744xw/fpy9e/cyZswYs1yThISERE/BYDBw7tw5tm7dyvbt2ykoKODOO+9k7ty5TJ8+HVdXV3Ob2IjK7EOoE1bgUn0SX7lpW5HrDRCnm4v3xGV4eXmZfTffoNei2BxDP4c06nVQrOmFLmA2HiNfwt4lsF1jVldXc+HCBQICAggLC7PohVtLCIJA0dYJhNpcMLcpIpnCeALm7zfZeCUlJUyfPp3o6GjWrVtnkcVZf89+niQudBCDwcCZM2dEoaG4uJgpU6Ywb948pk6danG9lA8dOsSKFSs4efIkNTUdm4BkMhlvvfUWU6dOpaSkBL1eL1anNccEVFxczIgRIygra7r3s7+/PzNmzOCll15qtUigUqm4ePEiYWFhBAUFmcROSVz4HwsWLGD9+vUmG+/q1atMmzaN6dOns3LlSosUFq5n48aNLF++nHPnzuHk5MSECRO47777mD17NnK5nEuXLjF06FDGjh3L8ePHb/j8jz/+yGOPPcakSZNYtWpVu0U0CQkJCYkbMRgMXLx4kS1btrBt2zays7OZPHkyc+bMYebMmbi7u1vUYrQ6/yyVcR8hrziKn1MlVh0wTaODTM/nqe/9ACUlJWg0Gry9vfH19cXb27vLF3V6jZqyLVEEO+Xf8J5OD0V13tR7T8FtxIs4eYe3asyKigri4uIIDg6mX79+FvVdtofCzeMJtY01txkimYZxBNx10GTjKZVKZs6cyaBBg/juu+8srhZeU/ze/DxJXDAhBoOB2NhYtm7dyrZt27h69SqTJ09m3rx5zJgxA1dXV4t6aB04cID33nuPpKQkqqur2/RZKysrNm7cyJw5c4DGPXeLi4upr68XJyBjq8/OJCsri9GjR7c6dNHDw4PJkyfz4osvMnz48CaPKSkpISkpifDwcAICAkxmqyQuXGPevHls3LjRZOMVFBQwbdo0Jk6cyJo1a7qFsNCQhIQE3n//fTZt2sScOXNYv349crmcgoICxo8fT3h4OHv27EGn0yEIAra2tqhUKh5++GF2797NDz/8wF133WVRzxgJCQmJnoQgCCQnJ7Nlyxa2b9/O5cuXmThxIvPmzWPWrFl4eXlZ1DO4OPMUhcffJEh2kUDnCqzbMC3WamQoB36CV8RDwLVrr66uFv28mpoavLy8RD/Pzs6uk67iGpoaBdXbR9BbrmrxWIMBimtdUbtNwCXyeVx6j27yuLKyMuLj4wkJCaFPnz6mNtks1BQnUBG3DAfVIfwcVNiYMdAk0zAG//k/m+w3UVZWxqxZswgODmbz5s2dfs+Zmt+LnyeJC52EIAgkJSWxefNmtm3bRnp6OnfccQdz585l5syZeHp6mvXmyM/PJyUlhSFDhuDv709SUhIffPABhw4duunOvxE7Ozv27dt3Q39WIw0noJKSEtRqdadOQAkJCUycOLHdKR/Ozs7ceuutPPvss0yYMAG4VtX18uXLDB06FF9fX1OaK4kLwOzZs9m0aZPJxisqKmL69OnccsstrF271uxhmx2htraW9PR0UfQSBIGYmBgyMjI4dOgQI0aMEI995513+Otf/8q8efP4/vvvLa7KuYSEhERPRRAE0tLSxA2lixcvcuuttzJv3jxmz56Nn5+fWf084waJMfKyrjSTsgsfYF+yD38HRbOLzqo6K6pGfI972KybHqNWq0U/r6qqCg8PD3x9ffH19TX5XFRffpX6n2Lwl1e16/MlakeqnEfjOORJ3EKnI5PJUCqVJCQkMHDgQHr16mVSey2F+so8yi58jE3hHnxt83Dowk3+TH0M/nf9arLfQEVFBbNnz8bX15ft27d3a3+np/t5krjQBQiCQEpKiqh0JyUlcdtttzF37lxmz56Nj49Pl01AgiBw5coVrly5QkREBJ6enjcck5mZyQcffMC+fftQKBSN3pPL5Zw4cYKwsLBWn/NmE5CPjw8ODg4dup5jx44xa9YsdDpdh8Yx4uDgwPDhw7njjjt44okn8Pb2Nsm4Dfm9iwuzZs3ihx9+MNl4CoWCGTNmMHz4cDZs2GCRuXftxdgf+ciRIyxevJiQkBAefvhh/P39OXToECtXriQwMJD9+/czaNAgc5srISEh8btEEASys7NFoeHcuXOMGTOGuXPnMnfuXAIDA7tUaMjLyyMtLY0hQ4bg5+d3w/v1VQWUnf8Qm8Jd+NsVYNdg2iytsUM34QDOvWJafb7a2loUCgXFxcVUVFTg6uqKr68vfn5+HW6ZV6NIRvj5NnycTFM8vbTGjhLZEK5a30bo+GdMGplqyejqqyiN/RTh6mZ8SMfZvvO6DWTqRuK/4IjJ7vmqqirmzZuHs7Mzu3fv7vDawZLoiX6eJC50MYIgkJmZKQoNsbGxjB07lrlz5zJnzhwCAgI6bQISBIHU1FSKi4uJjo5uVT2IgoICPvzwQ3bt2oVer+fUqVNNTlStpa6uThQaysvLxQnI19e3zYUwd+3axR//+MdOa8diY2NDREQES5cu5YEHHjDZbvjvWVwYOXIkH374oVibo6NOh0qlYubMmYSFhbFp06ZukXvXVgRBQKfTsW7dOv75z39SVFSEg4MDtbW1TJo0iTfffNPii/tISEhI/F4QBIHc3Fy2bdvGtm3bOHnyJCNHjhSFhj59+nSqn5ednU1OTg6RkZF4eHi0+BltjYrSC8uQ5W3F1lCJ4/SjOHiGttuG+vp6FAoFJSUllJaW4uzsLPp5zs7ObRqrOvcktsdn4OGobbc9zVFZZ43SajA2IX/EM/JRrGwsf1fYFAh6PaVJ36BJX4enNsGkf984VX/Kh64Rv3O5XN6h8dRqNQsWLMDKyoqffvqpw+NZIj3Nz5PEBTMiCAI5OTliNeLTp08TExMjTkBBQUEmm4D0ej3JyclUVVURHR3d4UWdKdBoNKLQcP0EJJfLm7329evX8+STT3bZIt3KyoqBAweyZMkSnnrqqQ79/brTgzEwMJB///vffPXVV5w/fx6NRtPusaZOncp3330nOh1lZWXI5fJGTkdb7vfy8nJmzZpF79692bJlS7fLvWsv+/btE2uM9O/fHzc3N3ObJCEhISHRBIIgUFhYyPbt29m6dSvHjh1j+PDhop/Xv39/k/l5xihZhUJBVFSURRQU12q14pyvUqlwdHQU53wXF5dmr70iYx/O5+/GxaHzdtgbUqORUawPQRa8CM/oZ7Fx/H3MrYIgUJHxE7WXPse5+jS+TrW095bM1EbiOedXlEql6Ns7OjqKG0ptrT1XW1vLokWL0Gg07Nu3zyLu6a6gu/t5krhgIQiCQH5+vqh0nzhxgsjISObNm8fcuXM7VMFWq9Vy8eJF9Ho9UVFRFrkI02q14sNIqVTi4OAgTkDXP4yWLVvG3/72N7PZamxxedddd/H888/j5eXVps93J3EhKCiIlJQUioqKSE5Oprq6mg0bNnD8+HGqqlqf+zh58mR27tzZ6DXjd65QKFAqldja2orpMu7u7s0WZKysrGTu3Ll4eHiwY8eOHhUi1xaM4XQSEhISEpaNIAgolUpRaDh8+DCDBg1i7ty5zJs3j0GDBrX7ea7/v/buPCyq8v0f+PuwCcgumztJqYiguOa+L6wzmJpramZamiUamh81UwO1TLOy0nJJS0pmWJRFQUBBcVcEEcUFUZBNdhhgmHl+f/jl/MSlWAZmBu/XdX2uzxUzc84zjMx9z3ue8zwyGZKSklBWVgYnJyeV+ALpedXV1Xjy5AlycnKQm5vL13xLS8sXdtzITzoMs+QF0NdRzkeUSimQLe3wf1tcejV4i0t1UrM7mq15JXQe/Qm9gmhY6RXWeRHQe1WOsJpyttbrWPOa5+bmIjc3FxoaGnzQ8F/b11dUVGD69OkoKirC8ePH1e4DtqKoY59H4YIKYowhOzsbgYGBEIlEOHXqFHr06MEXoK5du9b5H1pFRQWuXr2KVq1aoVevXmqx0J1MJqsVNGhpafEFaNu2bdi+fbuyh8jT1dXF7du36xUwqFO40LlzZ0RFRSE5ORmOjo611qC4ceMGduzYgYiIiBfW5njW6NGjcfTo0X89j1wuR35+Pt90MMb43Uae39a0tLQUnp6e0NXVxbFjx1SyiSKEEEJehTGGgoICBAUFQSwWIyIiAm+88QYEAgE8PT1hb29f5x2PpFIprl27BsYYevfurZJfID1PJpPVqvkcx/F9nvzeYVilrWzWxQf/TZFEC7IJVxp1qYiqqwkWnt8draLgHgqv7IBWdiisdR7XWpvjWfeqHGA1Jf5fP5vI5XIUFhbyr7lUKq3V5z17WWtVVRVmzZqFx48fIzIysk6X9xDVQeGCimOMIT8/nw8aTp48ibfeegseHh7w9PSEnZ3dKwtQWVkZrly5AlNTU/To0UPttuYDnr4Z1STda9aswYkTitsrt7FatWqFmJiYV25l+SrqFi78+OOPLwQLz8vKysL27dsRHByM9PR0/ucjR45ESEhIvc7JGENRURE/lbJmnY7c3Fy4ublh8eLFAICQkJB6X79JCCGEqJqioiIcPXoUYrEYx48fR9u2bfkvlJycnF7Zv9V8gVSzGLU6fIH0vGc/dBZf+RZDWh2Gtoo8jaIKTUgGBcOo80hlD6XJvCpYeJ5UUoD8Kz8A6f6w0rzHzyq5V9kDVlMv1OvbdcYYSkpK+N6urKwMpaWluH37Njw9PfHll1/i/v37iIqKqvfsYKJ8FC6okZoPXcHBwXwB6tSpEx80ODo68gXoxo0byMnJQYcOHRR6TZ+yTJs27T+//W5Oenp6iI+Pr9euGTXUKVxo3749zp49W69dM8rKyrBr1y7cv38fu3btavQYysrKIBKJ8P333+P27dswMDDAypUrMW3aNHTp0qXRxyeEEEJURWlpKUJDQyESiRAaGoo2bdrAw8MDQqEQ/fv35wOElJQUZGdnw9zc/F+/aFIXj09+Cpui3+o8Db+pFUi0IR1+ol67ZqibugYLz5NXVyL/+l5Is8/DeuK+Rn/GKC8vx4kTJ7BlyxYkJSVBV1cXn332GWbNmtWoy4WIclC4oMaKi4sREhICsViMsLAwWFpawsPDAxYWFvj6669x8OBBODs7K3uYjcIYw4QJE3DmzBllD4VnYGCAixcvolOnTg16vDqFC127dsXVq1eVPQxUVlZixowZyMvLw7Rp03DixAn+etX4+Hi1+p0SQgghdVFeXo7jx49DJBLh2LFjMDAwgIeHB2xtbfHVV19hy5YteO+999T+w1dm2HuwrRA1eCFBRXtS3gpsTAz0reo3M1WdNDRYaCoymQyLFi3C1atXsWDBAsTExODEiRPo1KkToqKi0L59e2UPkdQRhQstRFlZGcLCwvDtt9/i4sWL6NOnDwYOHAihUIiBAweq5VQ5mUyGIUOGIDExUdlD4ZmYmODKlSuN2o5TnT4IOzg44Ny5c0odQ1VVFWbPno2MjAxERkbCzMwMwNPdIuLi4uDm5qbU8RFCCCFNraKiApGRkdi+fTtiYmLQu3dvODk5wdPTE0OHDlXLrZgZY8gKcoMtYpQ9FF5umR40nOOhZ1b/manqQhWDhaVLlyIuLg4xMTF8kFBWVobIyEi4u7ur/cyc18krluYg6kZfXx+pqam4efMmjh07BqlUCpFIhKlTp0JXVxfu7u4QCoUYMmQItLRU/2WXSCTo168f0tLSlD0Unrm5Oa5du/ZaLSyj7DdzqVSK+fPn48GDB4iKiuKDBeBp0EPBAiGEkNeBrq4usrKycP78eRw+fBgmJibw9/fH+++/D5lMBldXV3h6emLkyJFqsagjYwxZ/kNhq31N2UPhZZUZoJXrRbQyadjMVHWQl5eH69evq0ywIJfLsXz5csTExNQKFoCnX8YJBAIljo40BM1caCHS0tIwduxY+Pv7o3fv3vzPq6qqcPLkSYhEIgQFBYHjOL4ADR8+XGUL0IwZM17YulCZ2rZti6tXrypkj111mrnQt29fnD59Winnrq6uxocffojr168jOjq6UbNFCCGEEHWWn5+PgQMH4vfff8fw4cP5n1dXVyM2Nhb+/v4IDAxEWVkZXF1dIRAIMHbsWJXdqjkrejk65v+iMos3ZpYZQ19wFToGLbfXUMVgYdWqVQgODkZ0dDRsbVvujhyvEwoXWpDq6up/nZVQXV2NU6dO4ciRIwgKCkJlZSVcXV0hFAoxatQolStA9+7dw7Zt2xAWFobs7GyljaNz5878dp6KoE7hwsCBAxEVFdXs55XJZFi8eDHOnTuHU6dOqUQRJIQQQpTpv/o8mUyGs2fPQiQSISAgAAUFBZg4cSIEAgHGjx+vcv1HVUkW8i9tg9bjIFjpZKCVkibWPiozh+Gka9DWa7kzU2uChR49esDa2lrZw4FcLsfatWvxzz//IDo6Gl27dlX2kIiCULjwmpLJZIiLi+MLUHFxMZydnSEUCjF27Fjo6+sre4i1ZGVlYdu2bQgKCkJGRkaznbdr1664cOGCQq9lVLXi/m+GDBnS7Nt/yuVyfPrpp4iJiUF0dHSDF84khBBCXldyuRwXL16Ev78/AgIC8PjxY4wfPx4CgQDOzs4KmYmpSFJJAfIvfw/u4RFYa6VBt5mWkEgvbwuTd65Cq5Vq/D4YYwpfoFPVggXGGDZu3Ih9+/YhOjoaPXr0UPaQiAJRuEAgl8tx7tw5PmjIzc3F+PHjIRQKMWHCBBgYGCh7iLUUFBRgx44d8Pf3b9I1Gezt7XHu3DmFrzugTuHCiBEjEBoa2mznk8vl+PzzzxEWFobo6Gi88cYbzXbul0lLS8ONGzdgaGhYaxoqIYQQoi7kcjmuXbvGBw01l9J6eHjA1dUVxsbGKrXjhKyqDE+u/AiW5gdLLhWtWzXNR5V7pR1hPvUqtHT0muT49VVZmI6qkH4AGEoNBqO1wxIYdRnXqGOqYrCwdetW7Nq1C1FRUXBwcFDqeKjPUzwKF0gtcrkcV65cgb+/P8RiMR49eoSxY8dCIBDAxcUFRkZGKlWAysrK8OOPP8LPzw+pqalQ1D9nR0dH+Pj4QENDA5aWlrC0tISpqalCggZ1ChfGjBmD4ODgZjmXXC7H6tWrERAQgOjoaLz55pvNct6Xyc3NxcaNG/HTTz8BADQ1NdG2bVusWbMGc+fOVctVuQkhhBDGGJKSkvig4datWxg5ciSEQiHc3NxgZmamUn2evLoSTxL2QHbnICxYMgx15Qo57q1iG9zt8D2goQULCwtYWlrCzMxMaburVRTcQ3XoQFi2Lq/18yflOijU7Qvd7h/AxO7der02qhgs7NixA9u2bUNkZCT69OmjtLFQn9d0KFwgrySXy5GYmMgHDXfu3MGYMWPg4eEBNzc3mJqaqlQBqqysxJ49e7Bv3z7cunWrwUFDzQdquVyOwsJC5OTkICcnBzKZjC9Abdq0aXABUqdwYcKECRCLxU1+HsYY1q9fj0OHDiEmJgbdunVr8nO+Sn5+PhYsWICAgAB+8dOKigocOXIEly9fxt69ezF58mSljY8QQghRBMYYbt26BZFIBLFYjOvXr2PYsGEQCoVwd3eHpaWlSvV5TCZD/o2DkNzcDQtZEkz1ZQ06zr0qR1hNOQsAKCoqQk5ODrKzsyGVSmFubg4rKyu0adOm2XZXk+SnQh72NixaV/zr/YoqNPFEsye0bGfDrNcH0NB89QdgVQwWfvrpJ/j6+uL48eMYMGCA0sZCfV7TUvlwoSmuPSL1xxjDzZs3+aT7xo0bGD58OF+AzM3NVeJ1ys7ORlJSErp3746IiAjs3bsXCQkJqK6urtPjPTw8cPjw4Rd+zhjjC1BOTg6qqqpgbm4OS0tLmJub16sAqVO44Orqin/++adJz8EYg6+vL/bs2YOoqCjY29s36fn+ayz/+9//sGXLFsyePRtbt26FpaUlgKdT54YPH4527drh2LFjMDc3V9o4CSGkpaA+TzUwxnDv3j0+aLh06RIGDx4MgUAADw8PtGvXTiVep/z8fFy7dg22trYwKr+Aipu7YVJxCW30q+r0+Luyt2E9KfKF58IYQ0lJCd/nSSQStGnTBpaWlrCwsGiyb7IlebfATgyCuX5lvR5XVskhh70FrvNUmPVZUmvNCFUMFvbs2YMvv/wSYWFhGDx4sFLHQn1e01L5cIGoHsYY7ty5wwcNV69exeDBgyEUCuHh4QFra2ulFKDMzEykpKTAwcEBFhYWtcbr7++P3bt349KlS6iqenkBmjFjBvbs2fOf52GMobS0FNnZ2XwBMjMzg5WVVZ0KUHOHCxzHNXgWh1AoxJ9//qngEf1/jDFs27YNO3fuxMmTJ9GrV68mO1dd3L17FwMHDkSnTp1w6NAh9OjRo9bq3HPnzsUff/yBhIQE/jrB5ORkXL9+HQYGBnBycqq1RzMhhBCibhhjSE9Ph1gshlgsRnx8PPr37w8PDw8IhUJ06tRJKX1ebm4uEhMT0b17d7Rr167WbUV3wlB+4ycYlZ6DRWvJSx9/lxuDtoK6XepZWlqK3NxcZGdno7S0FKampnyfp6jdw8pzb4CLGIY29QwWnlchBbKlHSFvL4Tc5j0k33kMe3t7lQkWDhw4gFWrVuHo0aMYMWKEUsdDfV7TU9lw4ejRo0hPT0deXh5Gjx6NN998E23btoVcLlf4Anuk4RhjePDgAZ90nz9/HgMHDoRAIIBAIECHDh2apQA9fPgQqamp6NWrF9q0afOv9w0LC8NPP/2Ec+fOQSJ5WoAWLFiAHTt2NOjcZWVlfNJdUlICU1NTfp2GlxUgdZq5MGXKFOzfv79Jjs0Yw86dO/HNN98gIiICffv2bZLz1Mfy5cuxfft2/Prrr1iwYMELt3/11VfYvHkz4uPj0bt3b/5nvr6+qKqqgrGxMezt7bFu3TqMHz++mUdPCCHqg/o89cAYQ2ZmJgICAiAWixEbG4tevXrxfZ6trW2z9HlZWVm4ceMGevbsCSsrq3+9b0l6LEoSvodhcSws9UsBAHe13dHOza9B55ZIJHyfV1RUBGNjY77P09Nr2GKQ5dnXoXFyBMzqOOOirqQyILPcHDJrF5j0XQFdM1uFHr8+GGP466+/4OXlhaCgIIwePVppY6lBfV7TU8lwYdq0aQgJCYFEIuGLjJOTE7777jsMGzZM2cMjr8AYQ0ZGBsRiMUQiEc6ePQsnJycIhUIIBALY2Ng0SQG6f/8+0tLS4OTkBBMTk3o9NjY2Frdv38b8+fMVMpa6FCB1ChfqOpujvhhj+OWXX7Bp0yaEh4dj4MCBCj9HfRUXF8Pe3h4cx+Hq1au1QiqZTAZNTU14eHggKioKISEhGDFiBKqrq/HgwQMkJycjOjoaJ0+eRGJiIoYMGYKgoCCYmZkp8RkRQohqoj5PPTHGkJOTg8DAQIjFYkRHR8POzg4CgQBCoRDdunVrkj4vIyMDt27dgqOjY72nqpc9voryzDOw6LtEIWOprKzk+7yCggIYGBjAysoKlpaWde7vyh5fhWb0KJjpSxUypleRyYFsiQkkpqNg5LQMrds235c4jDEcOXIES5Ysgb+/PyZOnNhs534V6vOah8qFC3PmzIG/vz8WLVqEKVOmoKioCH///Tf2798PjuPwyy+/YN68ec22yAppGMYYsrOzERAQAJFIhFOnTqFnz558AXrrrbcaXYAYY7h79y4ePXqEvn37qtyezZWVlcjNzUVOTg7y8/NhYGAACwsLODo6Kntodfbee+/h559/VugxGWPYu3cv1qxZg5CQEAwdOlShx2+osLAwuLq64rPPPsN3333H/7zmeuDi4mK0bdsWlpaWuHr16kuDrCVLlmDfvn3w9fXF0qVLm3H0hBCiHqjPaxkYYygoKEBQUBBEIhEiIyPRpUsXCAQCeHp6okePHgqZgZKeno67d++id+/eMDU1VcDIFUcqlfKXTuTn50NPTw+WlpawsrKCgYHBS/vc0owL0Dk9DiZ6dVsLTFEYA3LKW6PU8P+2uHxjbJOeLzAwEAsWLICfnx/c3d2b9Fx1RX1e81Cpd+6LFy/i2LFjmDlzJtatWwdjY2MAT1esHzhwIHx9fbFo0SJUVFRgyZIlKrGwDHk5juNgbW2Njz76CIsWLcKTJ08QFBQEf39/+Pr6omvXrvDw8ICnpyfs7Ozq/VrWrHCck5OD/v37q+RsgFatWqFDhw7o0KEDX4AeP36s7GHVi5GRkUKPxxjDwYMHsXr1ahw9elRlggXGGM6fPw+O417Y51gul0NTUxN+fn6QSCQYNWoUTExMak3dZYzh9u3bCAoKgq2tLcaNG8c/lqb3EkLIU9TntRwcx8HMzAzz5s3DvHnzUFhYiKNHj0IsFmPkyJFo3749P3O1d+/e9a6FjDHcv38f6enp6NOnD/9vRZVoa2ujXbt2aNeuHaqrq5GXl4ecnBxcvHgROjo6/MxVY2NjcByH0odn0SrOGcbNHCwAAMcBVq3LYCWPABIi8CS+FQp1+8Jo0Fa0buuk0HMdO3YMCxYswMGDB1UmWKA+r/moVLjw4MEDFBQUYMSIETA2NkZ1dTU4joOmpiYWLlwIMzMzrF27FsuWLYOZmRlmzpxJqwyrAY7jYG5ujvnz5+P9999HYWEhgoODIRaL8d1336FTp0580u3g4PCff6SMMSQnJ6OgoAD9+/dv8PVuzammAD270KSq69SpEzZt2qSw4zHG4OfnhxUrViAwMBAjR45U2LEbi+M45OTkQE9P74VApWbL0d9++w0A+Etonp30xXEcIiIikJGRgWnTpsHOzg4AqOAQQsgzqM9ruUxMTDB79mzMnj0bJSUlCA0NhUgkgrOzM8zNzfnFIPv371+nPi81NRWPHz9Gv379YGBg0EzPouG0tLRgbW0Na2tryGQy5OfnIzs7G1evXoWmpibMNB6i28OPYKzXsO0zFa2NfiUkZbfQyqSLQo97/PhxzJs3D3v37sWkSZMUeuzGoD6v+ahUuFDzYufn5wMAPyWuJhWaMmUKZDIZli5dinnz5qF79+4qsQgcqTuO42Bqaoo5c+Zgzpw5KC4uxrFjxyAWizFu3DhYWlryl0707dv3hT9auVyOpKQklJaWol+/ftDV1VXSM2kYuVyu7CHUSceOHXHt2jWFbr0kFovx6aef4p9//sHYsU07Ha8hJBIJysvL+WlwVVVV0NbWBsdxCAwMxKVLlzBs2DAMGTIEwP8vRsDT96zDhw/DwsICEyZMAEBpNiGEPI/6vNeDoaEh3n33Xbz77rsoLy9HeHg4RCIRPD09YWhoCHd3dwiFQgwaNKhWLQWefqBLSUlBXl4e+vXrp5IzU/+LpqYmLCwsYGFhAblcjsykY7C+sQhGeqrTA2aUmcFoUiK09BQ3IyQqKgqzZ8/GL7/8gqlTpyrsuIpCfV7zUPpv5NlUqE2bNtDR0cHOnTuRkpLC/1xDQ4P/UDZt2jR4e3ujuroaO3bsQFlZWbOPmSiOkZERZsyYAX9/f2RnZ2Pr1q3IysqCu7s77O3tsXLlSpw9exYymQwlJSX46KOPkJ+fr5bBAqAe4UKHDh2QkJCgsK2WACA4OBiLFi3Cn3/+CRcXF4UdV5Hc3NwAADExMQAAHR0dcByHhIQErF+/Hvr6+lixYgWApwv/POv8+fOIj4/HyJEjMWjQIACUZhNCCEB93utOX18fkyZNwp9//omsrCzs2rULEokE06ZNQ9euXfHpp58iJiYGUqkUlZWV+OSTT5CWlqayl7zWV2naSbRNnqliwYIpDCZdV2iwEBsbi+nTp+P777/HrFmzVHK2EfV5zUPlFnRcsmQJdu3ahZUrV8LLy6vWNPJnE6IRI0bg4cOHuHjx4n9uPUjUj0QiwYkTJyASiXDs2DHo6OjA0NAQOjo6CAkJgaWlpbKH2CBlZWUqPfb27dsjMTFRocFCaGgo5syZgwMHDmDy5MkKO66ilZSU4N1330V8fDy8vb3Ru3dvlJeX4/PPP0daWhp8fHywYsWKFxYZq6iowMKFC3H48GHs3r0bc+fOpWm8hBDyCtTnEeDpt8bR0dEQiUQIDAyEXC6HtbU1SktLcezYMXTpotjp+spQeCcURpffhUErFQsWPBOgra+4v6n4+Hh4enpiy5YtWLRokcr2P9TnNQ+lhQtHjx5FamoqSkpKMGDAAPTq1Qvt2rVDeno6Jk+ejFu3bmHjxo2YPXt2rdVhq6qqoKOjA29vb3z77beIiopSqWu3ieI9fvwYI0eOhEQigVQqhUwmg5ubGzw9PTF8+HCFTt1vaiUlJbC2tlb2MF6qXbt2uH79ukLXsIiMjOS3s5w+fbrCjttULl26BG9vb8TExEBbWxtSqRTW1tZYsWIFvLy8at23prAkJiZixIgRsLOzw19//YXOnTtT0SGEvPaozyN1VVRUhDFjxuDRo0fQ09NDYWEhXF1dIRQKMXr0aLWcqVqYGgzjKzPQupXqfIebWWaC1p7XFRosXLx4EQKBABs2bMAnn3yi8r0P9XlNTylrLkydOhXBwcGoqqrifzZo0CB88MEHmDdvHr766issWbIE69evh1QqxfTp0/mVWHV0dAAAhYWFsLa2ho2NjTKeAmkmWVlZGD9+POzt7XH48GFoaGjg1KlT8Pf3x4IFCyCVSvkCNGrUKIV+494UVPWyiLZt2yo8WDh16hRmzpyJXbt2Ydq0aQo7blPq168foqKikJiYiJiYGHTo0AG9e/dG586d+fvUFBSO4yCXyxEYGIjCwkJMmDCBvx8VHELI64z6PFJXRUVFcHV1haGhIVJTU6Gvr48zZ85AJBLBy8sLRUVFmDhxIgQCAcaPHw99fX1lD/k/FaSIYXL9PZUKFh6XGUPf85pCg4WrV69CKBRizZo1ahEsANTnNYdmn7kwb948+Pv7Y8mSJZg3bx7S0tIQGxsLX19fyOVyrF27Fl999RUCAgKwcuVKZGZmYsaMGVi8eDF69eoFAAgJCcGSJUvQpUsXiESil+5DSlqGvLw87NixA+vXr39hmpJMJkNsbCxEIhECAgJQWloKZ2dnCIVCjB07ViV3kaiqqoJAIMD58+dRWVmp7OEAAKysrJCYmKjQaxvPnDmDd955B9999x3mz5+v9m/CNVN15XI54uLikJGRgeHDh0NfXx+jRo1CUVERDh06hCFDhtACP4SQ1xr1eaQ+SktL8fXXX2PdunUv9G1yuRwXLlyAv78/AgICkJ2djXHjxkEoFGLixIkwNDRU0qj/3ePj82GYFwzL1uXKHgoA4HGZEfQ8r0NHX3E7liUmJsLFxQXLly/HF198QX0e4TVruJCcnIxhw4bB3d0d27dvrzUNLiYmBlOnTkVeXh68vb2xefNmxMTEYOPGjYiOjkbr1q0hEAhQWFiIhIQESCQSxMbG8luBkNebTCbDuXPn+KAhLy8PEyZMgEAgwIQJE1RyG6OIiAj88MMPiI+PR3m5cgqQpaUlkpKSFBosnD9/HkKhED4+Pvj444/VvuA8Ky8vD5988gn+/vtvDB06FB07doSfnx/mzp2L33//HQCtHkwIeX1Rn0eailwux9WrV+Hv7w+xWIz09HSMHTsWAoEALi4uMDY2Vrl+o/ThWRRf2wGD4tOw1C+BhhKG97jMCHqCa9AxsFLYMZOTk+Hi4oKPPvoI69evV7nfe2NQn9d4zRouhIeHw8XFBf7+/pg0aRJ/XV3Ni3Tp0iVMnjwZ6enp+Prrr/HFF1+gqqoKW7duRVBQEJKSkmBpaQknJyds2bIF3bp1a66hEzUil8tx+fJlPul+9OgRxo0bxxeg5/e3VQVnz57Fjh07cPr0aZSUlDTLOS0sLJCYmKjQ5P/y5cvw8PDAl19+iU8//bRFFZwaubm5+Oabb3DgwAHk5uYCAMaNG4cVK1agV69eKr1gJyGENCXq80hzYIwhKSmJDxpu376NUaNGQSgUwtXVFWZmZirXf5RnJ6DoynfQy4+ElV4hNJvhs2lWmSF0Pa5Bx1Bxa33dvn0bzs7OmDNnDnx9fVXu96wI1Oc1TrOGC7GxsRgxYgRWrlwJX1/fWrfVXN9y8eJFjBkzBlKpFIcOHcI777wD4Okq+0VFRTAzMwNjTCWnvBPVI5fLcf36dT5ouHv3LsaMGQMPDw+4ubnBxMRE5d4Yr1+/jm3btiEyMhKFhYVNcg5TU1PEx8ejQ4cOCnv+CQkJcHV1xapVq/D555+r3O+1KQQEBOC7777DmTNnoKenhyFDhmDnzp3o3r27sodGCCHNjvo80twYY7h16xZEIhFEIhGSkpIwbNgwCIVCuLu7w8LCQuX6EUl+KgovbUOr3HBY6+ZCS1Px58gs0UflyFOwaN9dYd+y3717F87Ozpg6dSq+/fbb1+Lbe+rz6q9Zw4XMzEw4ODigU6dOOHjwIHr27Fnr9ppkOzg4GO+88w5mzpyJ/fv3AwCtykkajTGG5ORkPmhITk7GiBEjIBQK4ebmBnNzc5X7N5aamopt27YhPDycT08by8zMDEeOHIFEIoGmpiYsLS1haWkJExOTBheKGzduwNnZGZ9++inWrFmjcr/HppaSkoJt27YhNjYWV65cUYsFpwghRNGozyPKxBjD3bt3IRKJIBaLceXKFQwaNAhCoRAeHh5o27atyv0bqyx+hIJL26CVdRTWOo+ho4Cl9rNK9fHQ7gjySjkwxmBhYQFLS0u0adOmwX1eWloanJ2d4e7ujp07d74WwcKzqM+ru2Zf0HHTpk1Yt24dvLy84OPjw68KXIMxhoqKCkydOhXh4eFISkqiaXFE4RhjSE1N5YOGa9euYciQIXwBsrKyUrkC9OjRI2zYsAFhYWHIz89v0DHMzMyQlJQEY2NjyOVyFBQUICcnBzk5OQ0uQCkpKXB2dsaHH36IDRs2qNzvrTk9uyDQ61Z4CSEEoD6PqAbGGNLT0/mg4dy5cxgwYAA8PDwgFArRsWNHletXqspzkXH6a2hmBsHGMAe6DdhpPbtMHzruV9HKqAMYYygsLOT7vOrqapibm8PS0hLm5ubQ1KzblImMjAyMHz8e48ePx88///xa9zfU5/23Zg8XsrKy8M477yA+Ph6bN2+Gt7d3rdtrkmtfX1/873//Q2xsLIYMGdKcQySvGcYY0tLS+AJ04cIFvP322xAIBBAIBGjfvr1KFKCMjAzcunULTk5OqK6uxo4dO/hFjerC1NQUiYmJtRbYqsEYQ1FREbKzs5GTkwOpVForaHh+p44aqampcHZ2xqxZs7B582Z6oyWEkNcc9XlE1TDGkJmZCbFYDLFYjLi4OPTq1QtCoRACgQBdunRRiT4vJycHiYmJcHBwgJmxHvIv7wR78DesNO9BX+e/P67llOlD2/UyWpl0euE2xhhKSkqQk5OD7OxsVFRU1AoatLVfnmQ8fvwYEydOxNChQ/Hbb7/VOZAgr69mDxeAp4uBTJw4EWlpadi0aROWLFnywiJ7n332Gfbv34+zZ8+iR48ezT3EJkFT/lQfYwyPHj3iC9CZM2fQt29fPmiwsbFRymv46NEj3L59G05OTi+EAyUlJdi5cyf++ecf3L17Fy/7kzY1NUVCQgLatPnv/Y2fLUA5OTmQSCRo06YNLC0tYWFhwReg+/fvY+LEifyWkxQsEEIIAajPI6qLMYbs7GwEBgZCLBYjJiYGdnZ2fNDQrVs3pbyGzwYLzy8YKK+uxJOrP0N+709YIAUGreQvPr5MD9qul9DKxOY/z8UYQ1lZGd/nlZaWwszMjL9Mtma2UU5ODpydndGnTx8cOHDglV80EfIspYQLAHDz5k0IBALcuXMH77//PmbOnIlRo0YBAEJDQ/HZZ5/B2toawcHBLWJ/4+dXTCaqjzGGrKwsBAQEQCQS4fTp03BwcOCDhrfeeqtZCtC/BQvPq6ysxM8//4w///wTKSkpkMvlMDExwfXr1+sULLxMaWkpX4AiIiJw8uRJjBo1Cn5+fnB1dcWPP/5I/6YJIYTUQn0eUXWMMeTn5yMoKAgikQiRkZF48803IRAIIBQK0aNHj2Z5Lf8tWHhhzDIZniTugzR1H8xliTDWlSG3TBeaLheha9qlQecvLy/n+7xz587h77//xpgxY3D8+HF0794df/311ytnNhDyPKWFC8DTVUc/++wzhIeHQ19fH4MHDwZjDAkJCaiursapU6daRJq9cOFCGBgYYOPGjdDX16fCo4YYY8jLy+MLUFRUFLp27coXIDs7uyYJGuoTLDxPJpPhwIED/GrJinDv3j3s2bMHgYGBePjwIYYOHYpJkyZh0qRJ6NTpxWl4hBBCXl/U5xF1UXN56NGjRyESiXDixAl06NCB7/N69erVJK9pdnY2kpKS6hQsvGzMBcl+0G87ALpmtgoZz6NHj7B3716IRCLcvXsX/fr1wzvvvINJkybhrbfeUsg5SMum1HABAAoLCxESEoJvv/0WmZmZMDQ0RN++fbFhw4YWscDPrFmz8Ndff6Fdu3ZYtGgRli9fDj09PSo8aqxmgZygoCCIxWKcOHECNjY2EAgE8PT0RM+ePRXy2jYmWGgqWVlZcHZ2xttvv41Nmzbh2LFjEIlEOHXqFBITE2lrHkIIIbVQn0fUUUlJCUJCQiASiRAWFgYLCwt4eHjA09MT/fr1U8hrWxMsODo6KuxLoMYqKiqCu7s7rKyssHv3bhw/fhxisRgRERGIiIjA8OHDlT1EouKUHi7UkEgkqKyshJaWFnR0dF5YXVgd/frrr/j444/h5OSE3Nxc5OXlYdWqVVixYgUVnhakqKgIx44dg1gsRnh4OKytrfmku0+fPg16jVUxWMjNzYWLiwscHR1x8ODBWtfe5efnw9TUlK41JYQQ8lLU5xF1VVZWhvDwcIhEIoSEhMDY2Bju7u4QCoV4++23G7TIoSoGCyUlJRAIBDA0NMTRo0ehq6vL31ZcXAw9PT26PIL8J5UJF1qaW7duYebMmbh58yYuX76MsrIyTJ48GXl5efD29qbC00KVlpYiLCwMIpEIoaGhMDU15bc9GjBgQJ0KkCoGC0+ePIGrqyveeust+Pn5UXEhhBDyWqM+7/UkkUgQEREBsViM4OBgtGrVCu7u7vD09MSQIUPqtOihKgYLZWVlmDRpEjQ1NRESEoLWrVsre0hETdG7XROpqKiAkZERdu/eje7du6NXr174/fffYWFhga1bt+Lbb7+FRCLh90olLYOBgQGmTJkCPz8/ZGVl4fvvv0dhYSEmT56M7t27w8vLC6dPn0Z1dfVLH6+KwUJhYSG/U8bhw4cpWCCEEPLaoz7v9aSnpwcPDw/s378fWVlZ2LdvHxhjeO+992Bra4vFixcjIiICVVVVL328KgYLEokEU6dOBWMMR48epWCBNArNXFCwZ7chSklJQfv27WFoaMjfFhMTg/nz5yM3N7dWsi2VSmt9aKPtjFqWiooKnDx5EmKxGEFBQdDU1ISbmxs8PT0xbNgwaGtrY+fOnTAxMYGHh4fKrJxdXFwMDw8PtGnTBgEBAbWmyBFCCCGvG+rzyMtUV1fj9OnTOHLkCAIDA1FRUQE3NzcIBAKMHj0aurq6+OOPP/gZLqoSLFRUVGDatGkoLi7G8ePHYWxsrOwhETVH4UIze1nhWbZsGQwMDAAAIpEItra26N27t3IHSpqMVCpFTEwMRCIRAgMDIZVK0a9fP8TFxeGPP/6As7OzsocI4OklHkKhEPr6+jh69Cj09PSUPSRCCCFEpVGfR2QyGc6cOQN/f38EBgaiqKgIgwcPRnR0NHbu3IlZs2Ype4gAnm5fPmvWLGRnZyMiIkJlZswS9UbhgoIcPXoUqampKCkpwYABA9C3b19+SxmZTFbrWvvnC8/q1avh5eWFgIAALFmyBO3bt8f58+fRqlUrSrVbuOrqanz++efYtWsX3nzzTWRkZMDFxQVCoRBjxoxR2gf6srIyvPPOO+A4DiEhIXxTRAghhLyOqM8jDSGXy+Hr64uvvvoK3bp1w/379zF+/HgIhUJMnDhRaf2VVCrFe++9h7S0NERFRaFNmzZKGQdpeShcUICpU6ciODi41vVVgwcPxrx58zB//nwALy88J0+exMKFC5GTk4OJEyciNjYWlZWVOH36NBwcHJr9eZDm9/PPP2PVqlUICwvDwIEDER8fD5FIhICAADx58gQTJ06EQCDAhAkTmu0auJpr7yoqKhAeHs5P91S2tLQ03LhxA4aGhrQVEiGEkGZDfR5pKH9/f8yZMwf//PMPnJ2dceXKFfj7+0MsFuPRo0cYM2YMhEIhXFxcYGRk1CxhU3V1Nd5//32kpKQgKiqKD8mUjfq8loHChUaaN28e/P39sWTJEsybNw9paWmIjY2Fr68v5HI5vL29sXnzZgAvFh4AOHXqFKZPn46srCyYmJjg9OnT6NmzpzKeCmlmjDF88sknmDFjBgYPHlzrNrlcjkuXLsHf3x8BAQHIzMzEuHHjIBAI4OzsDCMjoyYZU2VlJaZPn46CggKcOHFCJa69y83NxcaNG/HTTz8BADQ1NdG2bVusWbMGc+fOpQUmCSGENBnq80hjrF27Fm+//TZcXV1r/VwulyMpKYkPGlJTUzF69GgIBAK4ubk12fbeMpkMixYtwpUrVxAdHQ1ra2uFn6O+qM9rWShcaITk5GQMGzYM7u7u2L59e61rlWJiYjB16lTk5eVh6dKl2LFjB4CnbyYcx/FvGPv378fSpUuhra2NuLg42NnZKeOpEBUml8uRkJDABw337t3DmDFjIBAI4OrqChMTE4UUoKqqKsyePRsZGRmIjIyEmZmZAkbfOPn5+ViwYAECAgLg6uoKT09PVFRU4MiRI7h8+TL27t2LyZMnK3uYhBBCWiDq80hzYIwhJSWFDxpu3LiB4cOHQyAQwN3dHRYWFgrp82QyGT755BOcOXMGMTExaN++vQJG3zjU57VAjDRYWFgY4ziOiUQixhhjlZWVjDHGZDIZY4yxixcvss6dOzOO49iXX375wuMPHDjAjI2NmZmZGUtKSmq2cRP1JZfLWVJSElu/fj1zdHRk2trabNy4ceynn35iDx48YKWlpaysrKze/yssLGSenp7MwcGB5ebmKvtpMsaePtcvvviCaWhosDlz5rDs7Gz+tvv377OOHTuygQMHqsx4CSGEtCzU55HmJpfLWWpqKvP19WX9+/dnWlpabPjw4Wzbtm0sNTW1wX1eSUkJ++CDD5iNjQ1LS0tT9tNkjFGf11JpKDvcUGc118BfvHgRAKCjowMA0NDQAGMM/fr1w5EjR2BgYICtW7dCLBbzjy0tLcWjR4+go6OD6Oho2NvbN/8TIGqH4zjY29vjyy+/xLVr15CUlISRI0di7969sLW1hZubG/bs2YOsrCywOk5Kqq6uxsKFC5GSkoKIiAiYm5s38bOom3v37mH37t3o1asXvL29YWlpierqagCAjY0NRo8ejQsXLuDx48cAnibyNYqKipCVlaWUcRNCCGkZqM8jzY3jOLz55ptYtWoVzp8/j9TUVHh4eEAsFqN79+4YN24cfvjhB6Snp9e5z5PL5Vi5ciWOHz+OyMhIdO7cuYmfRd1Qn9cyUbjQCLa2tjA1NUV4eDiSkpJq3cZxHORyOfr3749Dhw5BKpUiODiYv93AwAAzZ85EYmIiHB0dm3vopAXgOA5du3bF6tWrcfHiRdy6dQsuLi7w8/ND165dMXHiROzatQuPHj16ZQGSyWRYvHgxrly5gpMnT8LKyqqZn8Wr7dq1C/n5+fjoo4/Qo0cPAICWlhZ/+xtvvAEdHR2+EGlqauLevXuYNm0ahg4div79+2P48OG1/u4IIYSQuqI+jygTx3GwsbHB8uXLERsbi7S0NEybNg1hYWFwcHDAyJEjsX37dty7d++VfZ5cLsfatWsRGBiIyMhI2NraNvOzeDXq81ooZU6baAk2btzIOI5jy5cv56fLPUsul7Py8nLm5ubGtLS0WEpKihJGSV4ncrmcPXjwgG3fvp0NGzaMaWpqsoEDBzIfHx9248YNfkpdSUkJmzdvHrO1tWXp6enKHnYtRUVFrEOHDqxjx44sLy+v1m3V1dWMMcbc3d2Zvr4+i46OZowxFhkZyTp16sQ0NDTYsGHD2KxZs1i/fv2Yrq4u27BhQ3M/BUIIIS0A9XlE1cjlcvb48WO2a9cuNnbsWKalpcV69erFvvzyS3blyhW+zystLWUrV65kVlZW7MaNG8oedi3U57VcFC400uPHj9ngwYMZx3Fsy5YtL9wul8sZY4z5+PgwjuNYXFxccw+RvMbkcjnLyMhgP/74Ixs9ejTT0tJiffr0YevXr2fTpk1jnTt3Zvfv31f2MF8QGhrKOI5jy5Ytq/Xzmr+noqIipq+vzzp37syKi4tZZmYmc3R0ZBzHsZ9++om/f3JyMhs1ahSzsLBg586da9bnQAghRP1Rn0dUmVwuZ7m5uey3335jzs7OTEdHh9nb27PVq1ezhQsXMnNzc3b9+nVlD/MF1Oe1XHRZRCNZW1tj3759sLGxwapVq+Dj44Pi4mL+9prVXbOzs2FkZFRrpeGW5NnroIjq4DgO7dq1w+LFixEZGYnMzEwsXLgQ4eHh8Pf3R0REBGxsbJQ9zFoYYzh//jw4jnthn2O5XA4A8PPzg0QiwcSJE2FoaIjt27cjKSkJS5cuxccff8zf387ODp9++iny8vIQExPDH58QQgipC+rznqI+TzVxHAdzc3PMnz8fISEhyM7Ohre3N86fP489e/bg2LFjcHBwUPYwa6E+r2WjcEEBunbtipCQELz55ptYs2YNvLy8EB0dzd8eGhqK0NBQODo6ol27dkocadMpKyur9d9UhFQPx3GwsLDAhx9+iLNnzyIvLw9vvfWWsof1Ao7jkJOTAz09PRgZGdW6rWb/8N9++w0AsHDhQty4cQP//PMPevfujdmzZwN4Wlhqikvv3r0BADk5OZDJZE2ybzQhhJCWi/o86vPUAcdxMDExwXvvvYfIyEjk5+dj4MCByh7WC6jPa9m0/vsupC7s7OwQFhaGzz77DAcOHMCRI0cwePBgMMaQkJCA6upqBAYGwsTERNlDVaivv/4a8fHxSEhIwNChQ9GvXz94eXnxbw5ENXEcB2NjY2UP45UkEgnKy8v5v5eqqipoa2uD4zgEBgbi0qVLGDlyJJycnLBp0yakp6fjww8/5Ffjrlloi+M4XL9+nT8m/bskhBDSENTnUZ+nbqjPI8pAMxcUyNbWFgcPHsT+/fvRpUsXXLlyBXfu3MHw4cMRFxfHr4TaUggEAmzcuBF3795F+/btERISgs8//xzDhw/HlStXIJVKlT1Eoqbc3NwAgJ/ipqOjA47jkJCQgPXr10NXVxfe3t6oqqpCdHQ02rZti2HDhkFXV5c/hobG07e306dPAwBGjBgB4P9PuSOEEELqg/o86vOIYlCf14Ipa7GHlq68vJwVFBSwkpKSl64urO5Wr17NdHR0mI+PDysoKGCMMZaWlsamT5/OtLW1WdeuXVlQUBCrqKhQ7kCJWiouLmbOzs7MxMSE+fj4sNDQUObv78/eeOMNxnEc8/X1ZVKplKWnpzNLS0vm4uLCMjIyGGNPFwOqWRDo4cOHbPDgwczY2Jg9ePBAmU+JEEJIC0J9HvV5pOGoz2u5KFwg9VZdXc2GDh3K+vbty4qLixljjC+s+fn5bPPmzczCwoK98cYbLCQkhH8DIKQ+Ll68yEaNGsU4jmM6OjqM4zjWtm1btm3bNv4+Z8+eZRzHsblz59Z6rEwmY4wxduDAAaajo8OmT5/OqqqqmnX8hBBCiDqiPo80B+rzWiZac4HUi1wuR05ODq5evYrhw4fD0NAQVVVV0NHRAWMMpqamWLx4MfT09LBhwwZ8/vnn6NGjB2xsbCCTyehaKFJn/fr1Q1RUFBITExETE4MOHTqgd+/e6Ny5M3+fnJwcAOAX0KqsrIS2tjY0NDQgkUjg5+cHqVSKOXPmQFtbWynPgxBCCFEX1OeR5kJ9XstE4QKpFw0NDbRt2xZ9+vTBzZs3IZFIoKenB7lcDg0NDTDGYGBggHnz5iE/Px8bNmzA0qVLERwcTAWHNIiDg0OtbZTYM1sMDRo0CNra2khOTgYAtGrVir/tjz/+QHh4OMaOHYsJEyY034AJIYQQNUV9Hmlu1Oe1LLSgI6kXxhhkMhkcHR3x4MEDrFmzBlKplC84HMeBMQZDQ0MsWbIEw4YNw7FjxxAaGqrsoZMWomaLIcYYLC0tMX/+fISFhWHz5s1ISUmBVCrF77//jhUrVsDMzAybNm3i708IIYSQV6M+jygb9XnqjWP0SpA6qCkoNbKzs/H222+jpKQEW7duxdy5c2sVnpqE++TJk5gwYQK2bNmC5cuXK/EZkJYqJSUFixYtQlxcHLp06YKKigo8evQI3bp1g6+vL4RCobKHSAghhKg06vOIqqI+T73QzAXyr65duwapVFqr4MhkMlhZWWHbtm1gjGHr1q34+++/IZPJ+IJTw9raGhzH4eHDh8oYPnkNdO/eHTExMTh8+DAcHR0xaNAgbNq0CUeOHKGCQwghhPwL6vOIqqM+T73QmgvklVxcXJCeno7t27dj9OjR/LV0Nf8/duxYrF+/HmvXrsW6deuQn5+PBQsWQEdHhz/GuXPnoKOjw19L9XwyToiiTJkyBVOmTEFFRUWtfZAJIYQQ8iLq84g6oT5PPVC4QF5q2bJlCA8PBwCsXbsWHMdh1KhR0NTU5AuHkZERZs6cCS0tLaxbtw5eXl44c+YM1qxZAyMjI8TExOD777+HtbU1v9AKFRzS1HR1dam5IYQQQv4F9XlEXVGfp9pozQXyAn9/f8yfPx+dOnXC+PHjsWfPHnTr1g0+Pj58sv3sH7VEIsH58+fxySef4MaNG9DT04OmpiZkMhnatGmD0NBQ9OzZU8nPihBCCCGEUJ9HCGkqFC6QWiQSCaZMmYLIyEhcvnwZdnZ22LhxI7755hvY2dm9svAAQGlpKfz8/JCQkID8/Hz06tUL7777bq39agkhhBBCiHJQn0cIaUoULpAXXLhwAXFxcfDy8gIAZGVlYffu3di6detLCw/w9Bo7DQ1aH5QQQgghRJVRn0cIaSoULpCXkkql0NbW5v87Ozsbv/7660sLT43i4mIYGRnV2geZrocihBBCCFEt1OcRQpoCRZDkpWoKTk32ZGVlhYULF8Lb2xs3b97E6tWrERkZyd//0KFDWLt2Le7du8cXGio4hBBCCCGqh/o8QkhToJkLpF6eTbZ79OgBX19fPHjwAN7e3tDX10d8fDzat2+v7GESQgghhJB6oj6PENIYFC6QesvMzMT+/fvh4+ODdu3aISsrC5qamjh9+jS/z3FLIpfL6TpDQgghhLwWqM8jhDQU/SWRepHJZGjXrh0WLFiAiRMn4s6dO9DW1kZcXFyLKzgxMTEAAA0NDcjlcuUOhhBCCCGkiVGfRwhpDAoXSL3ULOxz/PhxxMbGwsTEBHFxcbC3t1fyyBRr8uTJcHV1xaFDhwBQ4SGEEEJIy0d9HiGkMbSUPQCifsLDw7Fq1SqUlpbiwoULsLOzU/aQFMrLywtisRgcx2HVqlXQ1NTE9OnT+cJDU+cIIYQQ0lJRn0d9HiENRX89pN66d+8OJycnXLhwocUl2SKRCL/99hv69++PjRs3IjMzE8uWLcNff/0FgJJtQgghhLRs1OdRn0dIQ9HMBVJvNjY2EIlE0NHRUfZQFKq4uBgREREoLS3FDz/8gAEDBsDMzAwff/wxvLy8AAAzZsygZJsQQgghLRb1edTnEdJQFC6QBmlpBQcAWrdujYEDB2Lo0KEYMGAAZDIZFi1aBA0NDSxatOilhYfjOH6fZ8YY7flMCCGEELVHfR71eYQ0BG1FScgzZDIZqqqqoKenV+vnu3fvxqJFi2BpaYnvvvsOM2bMeOnjKekmhBBCCFFN1OcR0rTor4O89p7N1zQ1NV8oOADw4Ycf4pdffkFOTg68vLz41YUB4Mcff8S0adMAgAoOIYQQQogKoT6PkOZDMxfIa+vatWuwt7eHtrZ2nR9Tk2xbWVlh+/bt4DgOK1asQEZGBu7duwcbG5umGzAhhBBCCKkT6vMIaX4ULpDXkouLC9LT07F9+3aMHj2a39e5Lvbu3YsPPvgAZmZmKC8vh66uLk6fPo2ePXs24YgJIYQQQkhdUJ9HiHLQ3B7y2lm2bBnCw8ORnJyMtWvXIjo6GjKZ7D8fV3Of999/H3PnzkV+fj709PQQGxtLBYcQQgghRAVQn0eI8lC4QF4r/v7+2Lt3L+zt7bFs2TIkJyfjiy++QFRU1H8WnprUe9++fYiMjISxsTHi4uJa3B7QhBBCCCHqiPo8QpSLwgXy2pBIJNi/fz8qKyvh5+eHb775BsuXL8fNmzexevXqOhWemJgYLF68GIWFhYiNjYWdnV0zjZ4QQgghhLwK9XmEKB+FC+S1oaenh3Xr1sHHxwf29vbQ0NDAwoUL4e3t/crC8/ySJB07dsTUqVNpihwhhBBCiAqhPo8Q5aMFHclrRyqV1lo5ODs7G7/++iu2bt0KOzs7+Pj4vLD4T0lJCQwNDcEYg1QqhY6OjjKGTgghhBBC/gX1eYQoD81cIK+dmoJTk6tZWVm9kGxHRkby9z906BDWrFmDO3fugOM4KjiEEEIIISqK+jxClIdmLhDyf55Ntnv06AFfX188ePAA3t7e0NfXR3x8PNq3b6/sYRJCCCGEkHqiPo+QpkfhAiHPyMzMxP79++Hj44N27dohKysLmpqaOH36NBwcHJQ9PEIIIYQQ0kDU5xHStChcIOT/yGQyaGpqIjc3Fx999BHEYjFMTU1x+vRp2oaIEEIIIUSNUZ9HSNPTUvYACFEVNQv7HD9+HLGxsTAxMUFcXBxtQ0QIIYQQouaozyOk6dHMBUKeER4ejg8++AAFBQW4cOECJdmEEEIIIS0E9XmENC3aLYKQZ3Tv3h1OTk5UcAghhBBCWhjq8whpWjRzgZDnVFVV0TZEhBBCCCEtEPV5hDQdChcIIYQQQgghhBDSKHRZBCGEEEIIIYQQQhqFwgVCCCGEEEIIIYQ0CoULhBBCCCGEEEIIaRQKFwghhBBCCCGEENIoFC4QQgghhBBCCCGkUShcIIQQQgghhBBCSKNQuEAIIYQQQgghhJBGoXCBEEIIIYQQQgghjULhAiGEEEIIIYQQQhqFwgVCCCGEEEIIIYQ0CoULhBBCCCGEEEIIaZT/BwUMc+MrpFIfAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_state_city(psi, title=\"My City\", color=['black', 'orange'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**plot_state_hinton()** options\n", - "\n", - "- **title** (str): a string that represents the plot title\n", - "- **figsize** (tuple): figure size in inches (width, height)." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:09:03.630399Z", - "start_time": "2021-07-31T05:09:03.400479Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:17.843784Z", - "iopub.status.busy": "2023-08-25T18:25:17.842591Z", - "iopub.status.idle": "2023-08-25T18:25:18.141422Z", - "shell.execute_reply": "2023-08-25T18:25:18.140719Z" - }, - "scrolled": true - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAHRCAYAAAD3zC+CAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAsgUlEQVR4nO3de3SU9Z0/8M+ESwKYRAsWULmJVLu2K4tYdWG5SDEt1tq1WtG2CiqirV1rrVBYXbCugpdW96xbb4jAtlZRW9ytVUSFKKlVj4rLdsVyEcRarFqbgNx1fn/0l2yzBEz4ZphM8nqdM+eQ5/nOPJ/5nvB88p7nMplsNpsNAACABEX5LgAAACh8ggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggVAM+rbt29kMpnIZDJxySWX7HHsDTfcUDe2ffv2Oa9t+vTpkclkYsSIEXscN2fOnMhkMtG3b99d1tW+v7Vr1+akRgAKl2ABkCM/+clPYvv27btdP3v27H1YTctUG3amT5+e71IASCRYAOTA4MGD4913342HHnqowfW/+tWvYsWKFXHMMcfs48rSPPHEE/HKK6/EwQcfnO9SAGhhBAuAHDj33HMjYvdHJe6666564wpF//7944gjjogOHTrkuxQAWhjBAiAHPv3pT8fgwYPjsccei9/97nf11m3atCnmz58fhxxySJx44om7PLempibKysqiffv2sX79+t1uY8yYMZHJZOJHP/pRs9e/O7u7xmLEiBGRyWRiyZIlsWzZsjj11FOjW7duUVxcHH/1V38VP/jBDyKbzdZ7TiaTiauuuioiIq666qq6600ymUyMGzeu3tg//vGPMXXq1DjyyCOjc+fOUVpaGkcffXRcf/31sWXLll3qXLJkSd31JDt27IjrrrsujjzyyOjUqVN07do1Tj311HjllVeadW4A2jrBAiBHzj333Pjwww9jzpw59ZbPnz8/Nm3aFOecc04UFe26Gy4rK4tx48bFBx98ELfddluDr7169ep49NFHo6ysLM4+++xclL9XFi5cGMcee2ysWLEiRo8eHccff3z89re/je9+97tx6aWX1ht7zjnnxFFHHRUREUcddVScc845dY+hQ4fWjVuzZk0MGjQoZsyYEW+//XaMGTMmTjjhhFi5cmVMnjw5hg4dGu+9916D9ezYsSPGjBkT3//+96N3795x0kknRZcuXeLnP/95/O3f/q2L0AGaUxaAZtOnT59sRGSffvrp7J/+9Kdsp06dsocddli9MUOGDMlmMpns6tWrs6+99lo2IrLt2rWrN+a3v/1tNpPJZD/+8Y9nt27dust2LrvssmxEZL/1rW81urZp06ZlIyI7fPjwPY67++67sxGR7dOnz27f32uvvVZv+fDhw7MRkY2I7G233VZv3RNPPJHNZDLZdu3aZdevX99gTdOmTdttPccee2w2IrJf/OIXs5s2bapb/oc//CE7aNCgbERkzzrrrHrPWbx4cV09f/M3f5P9/e9/X7duy5Yt2YqKimxEZC+44II9zgUAjeeIBUCOlJeXx6mnnhqrVq2KysrKiIh49dVXo6qqKoYPHx6HHnrobp87YMCA+PznPx9/+MMf4v7776+3bsuWLTF79uzIZDLxzW9+s8l1VVZW1jvt6P8+xo8f3+TXrHXqqafGxIkT6y074YQToqKiIj744INYvHhxk15v6dKl8eyzz0bnzp3jjjvuiC5dutStO/DAA+OOO+6IiIh777033njjjV2en8lk4u67744ePXrULSspKak7Bevxxx9vUj0A7F7ub5wO0Iade+658ZOf/CRmz54dw4cPr7uYuzEXbV9yySXxy1/+Mm655Zb42te+Vrf8nnvuiffeey9Gjx4dhx9+eJNr6t69e3zuc5/b7fpVq1ZFVVVVk183IuLkk09ucPknP/nJePTRR3e53uSjLFmyJCIiPve5z0X37t13WX/00UfHUUcdFS+//HJUVlbGV7/61Xrre/fuXXe61f+tJyKaXA8AuydYAOTQyJEjo1+/fvHAAw/EzTffHPPmzYuysrI47bTTPvK5o0ePjk9+8pPx7LPPxgsvvBBHH310RET827/9W0REXHzxxXtV0xFHHLHLdR9/ac6cOXsdLHr37t3g8rKysoiI2Lp1a5Ner/YP/379+u12TP/+/ePll19uMCR8VD3btm1rUj0A7J5ToQByqPYOR5s3b45zzjknNmzYEGPHjo1OnTo16rnf+ta3IiLilltuiYiIZ555Jl566aXo27dvfOELX8hp7XujoYvR86ml1QPQmtnjAuTYuHHjoqioKP7zP/8zIpr23RVnn3127L///nHvvffGu+++WxcwLrroojbxR3PtF/GtWbNmt2Nq1/nS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W15eXh5Tp06N7du3x9y5c+uWDx48OI488sho165dsxUMQMukRwC0bU06FWrJkiUREXHiiSfusq6ioiIiIiorK9Or+v+2bdsW27Ztq/vZYXKAlkuPAGjbmnTEYuXKlRERMWDAgF3W9ejRI/bbb7+6Mc1hxowZUV5eXvfo1atXs702AM1LjwBo25oULKqrqyPiz4e1G1JWVlY3pjlMmTIlqqur6x7r169vttcGoHnpEQBtW5PvCrUvFRcXR3Fxcb7LAKAF0iMAWpYmHbGo/RRqd5841dTU7PaTKgBaNz0CoG1rUrCoPW+2oXNkN2zYEJs2bWrw3FoAWj89AqBta1KwGD58eEREPPbYY7usW7hwYb0xALQtegRA29akYDFq1Kg49NBD45577olly5bVLa+uro5rr702OnbsGGeffXZz1whAAdAjANq2Jl283b59+5g1a1ZUVFTEsGHDYuzYsVFaWhoPPvhgrFu3Lm688cbo27dv3filS5fGrFmzIiLi7bffrls2bty4iIjo1q1b3Hjjjc3zTgDIKz0CoG1r8l2hRo4cGUuXLo1p06bFfffdFzt27IhPf/rTcd1118UZZ5xRb+yqVavqfctqRMTq1atj9erVERHRp08fTQOgFdEjANquTDabzea7iMaqvaPI9773vSgpKcl3OQCtwtatW2PmzJlRXV0dZWVl+S5nr+kRAM2vKT2iSddYAAAANESwAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACCZYAEAACQTLAAAgGSCBQAAkEywAAAAkgkWAABAMsECAABIJlgAAADJBAsAACBZk4PFj3/845g4cWIMHjw4iouLI5PJxJw5c3Y7vqamJr7zne9Enz59ori4OPr27RuXX355bNq0KaVuAFogPQKg7Wrf1CdcccUVsW7duujWrVv07Nkz1q1bt9ux77//fgwfPjyWLVsWJ554Ypx55pnx0ksvxY033hiVlZXx1FNPRUlJSdIbAKDl0CMA2q4mH7GYNWtWrF27Nt5+++248MIL9zj2+uuvj2XLlsXkyZNj4cKFMXPmzFi4cGFMnjw5nn/++bjpppv2unAAWh49AqDtanKw+OxnPxt9+vT5yHHZbDZmzZoV++23X1x55ZX11l155ZWx3377xaxZs5q6eQBaMD0CoO3K2cXbK1eujDfffDOGDBkSXbp0qbeuS5cuMWTIkFizZk2sX78+VyUA0ELpEQCtT06DRUTEgAEDGlxfu7x2XEO2bdsWNTU19R4AFD49AqD1yVmwqK6ujoiI8vLyBteXlZXVG9eQGTNmRHl5ed2jV69ezV8oAPucHgHQ+rTo77GYMmVKVFdX1z0cEgeglh4B0LI0+XazjVX7KdTuPm2qPWS9u0+rIiKKi4ujuLi4+YsDIK/0CIDWJ2dHLD7q/NiPOr8WgNZLjwBofXIaLA466KCoqqqK999/v966999/P6qqqqJfv37OiQVog/QIgNYnZ8Eik8nE+eefH5s2bYqrr7663rqrr746Nm3aFBMmTMjV5gFowfQIgNanyddYzJo1K5YuXRoREcuXL69btmTJkoiIGDp0aJx//vkRETFp0qR46KGH4rrrrouXXnopBg0aFC+++GI89thjccwxx8S3v/3t5nkXALQIegRA29XkYLF06dKYO3duvWVVVVVRVVVV93Nt0+jSpUtUVlbG9OnT48EHH4zFixdHz54947LLLotp06ZFp06dEssHoCXRIwDarkw2m83mu4jGqqmpifLy8vje974XJSUl+S4HoFXYunVrzJw5M6qrq+u+P6IQ6REAza8pPaJFf48FAABQGAQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAAQDLBAgAASCZYAAAAyQQLAAAgmWABAAAkEywAAIBkggUAAJBMsAAAAJIJFgAA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", - "text/plain": [ - "
" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_state_hinton(psi, title=\"My Hinton\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**plot_state_paulivec()** options\n", - "\n", - "- **title** (str): a string that represents the plot title\n", - "- **figsize** (tuple): figure size in inches (width, height).\n", - "- **color** (list or str): color of the expectation value bars." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:09:05.915291Z", - "start_time": "2021-07-31T05:09:05.790887Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:18.146375Z", - "iopub.status.busy": "2023-08-25T18:25:18.145164Z", - "iopub.status.idle": "2023-08-25T18:25:18.300399Z", - "shell.execute_reply": "2023-08-25T18:25:18.299672Z" - }, - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAoQAAAHvCAYAAADNbM5LAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABYeElEQVR4nO3deXhTZf7+8Ttps7WlQNn3sgpKkX0R2RUUF9xRVMBxd3CGH86ooA6gIqjg6Lg7OKCjoIyg4oKAIwUBEWQHKQJSQKACVrov2X5/8G2GkKZ0C6dp3q/r4tKe5+Tkk5OnT++cnPMck9fr9QoAAAARy2x0AQAAADAWgRAAACDCEQgBAAAiHIEQAAAgwhEIAQAAIhyBEAAAIMIRCAEAACIcgRAAACDCEQgBAAAiHIEQQFhITEyUyWSSyWTSn//85xLXff75533rRkdHh7y2KVOm+J6v6F9UVJQSEhLUr18/vfzyy3I6nSGvI5i5c+fKZDJp7NixfstTU1NlMpmUmJhoSF0Aqo7Qj5QAUMnef/99Pf/887JarcW2/+tf/zrHFZ3SoEEDXXbZZZIkp9Op3bt3a/Xq1Vq9erU++OADLVu2TLGxsYbUBgAl4QghgLDSvXt3/fbbb/r000+LbV+7dq1SUlLUo0ePc1yZ1L59e82dO1dz587V+++/rx9++EGLFy9WVFSU1q5dq2efffac11SSJk2aaNeuXfrvf/9rdCkADEYgBBBW/vCHP0gKfhTw7bff9lvPaFdddZVuu+02SdKCBQsMrsafxWJR+/bt1bp1a6NLAWAwAiGAsJKUlKTu3btr2bJlOnz4sF9bdna2FixYoKZNm2ro0KEBj83MzFR8fLyio6N16NChoM8xfPhwmUwmvfbaa5VSc7du3SSdOmevyKJFi3TXXXepY8eOql27tux2u1q2bKk//OEP2r17d7HbGThwoEwmk5KTk4ttLzqXccqUKaWqq7hzCFNSUmQymVS7dm3l5+cHfWz37t1lMpkCjtS6XC7Nnj1bAwcOVEJCgmw2m1q2bKn777+/xH1++PBh/fWvf1VSUpJq1Kih2NhYtWvXTmPHjtXatWtL9XoAlB+BEEDY+cMf/iCPx6O5c+f6LV+wYIGys7M1ZswYmc2Bw1t8fLzGjh0rt9utN954o9ht79u3T1999ZXi4+M1evToSqk3MzNTkmSz2XzLbrrpJs2fP18Oh0ODBw/WsGHDZDabNWfOHHXr1s2wENS+fXv16dNHJ0+e1CeffFLsOtu3b9fGjRvVoEEDXXHFFb7lWVlZuvTSS3X33Xdr48aN6tSpk66++mrZbDa98cYb6tKlizZv3hywvf/+97/q2LGjZs6cqWPHjmnIkCG64oorVKtWLc2bN09vvfVWqF4ugP9DIAQQdkaNGiWHwxEQCP/1r3/JZDKV+HXxgw8+KJPJpNmzZ6ugoCCg/fXXX5fX69WYMWMUFxdX4Vq9Xq8WL14sSercubNv+fvvv69jx45pw4YNWrhwoT799FPt3btXr776qnJycnTPPffI6/VW+PnLo2j/nbl/i8yZM0eSdNttt/ldxX3fffcpOTlZV155pfbt26fk5GT95z//UUpKiv7+97/rt99+08iRI+V2u32POXTokK6//nqdPHlSjz76qA4dOqRPPvlECxYs0Pr16/XLL7/orrvuCt2LBSCJQAggDNWsWVPXXXed9u7dq5UrV0qSdu/erTVr1mjAgAFq1apV0Me2bdtWl19+uY4dO6b//Oc/fm15eXm+UPnHP/6xQjU6nU79+OOPGjVqlNavXy9JGj9+vK995MiRAVccm0wmPfDAA+rTp4927typXbt2VaiG8ho5cqRiYmK0fPnygK/lnU6n3nvvPUnSHXfc4Vu+a9cuzZ8/X40bN9a8efNUv359v8eNHz9ew4cP1549e7RkyRLf8hdeeEEZGRm66qqrNH369IArx+vXr6+LL764sl8igDMQCAGEpTMvLin6b2kuJimax/CVV17xWz5v3jz9/vvvuuSSS3TeeeeVuaaVK1f65iG0Wq264IIL9MEHH8hqtWrWrFm69tpr/dbfu3evXnnlFY0fP1533nmnxo4dq7Fjx+rXX3+VpKDnEoZajRo1dMMNN8jj8ejdd9/1a/viiy90/Phx9ezZUxdccIFv+Zdffimv16vLL79cNWrUKHa7AwcOlCS/r8O/+uorSdI999xTya8CQFkwDyGAsDRo0CC1bNlSH330kV588UW9++67io+P1w033HDWx1566aXq0KGDvv/+e23cuNF30cerr74qSRo3bly5ajp9HkKz2az4+Hidf/75uvrqq9WwYUPfem63W+PGjdObb75Z4tfCReceGuEPf/iD3n33Xc2dO1cTJ070LS/6uvj0o4OS9PPPP0s6dZV3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vhITEzVhwoRKKxYAAACVr9yBsF69elq5cqVuv/12vf32277l48aNkyT16tVL8+fPV82aNSteJQAAAEKmQncqadWqldasWaMtW7Zo3bp1Sk9PV3x8vHr16hUwDQ0AAJXJbrerbdu2stvtRpcChL1KuXVd586d1blz58rYFAAApWKxWNSoUSOjywCqhQrdug4AAKM4nU4dPXpUTqfT6FKAsFfqI4RPPvmkTCaT/vjHPyohIUFPPvlkqR5nMpn0xBNPlLtAAACKk5+frz179iguLk4Wi8XocoCwVupAOGXKFJlMJo0cOVIJCQmaMmVKqR5HIAQAAKjaSh0IV6xYIenU7ehO/xkAAADhrdSBcMCAASX+DAAAgPBUbS8q2bBhg4YPH65atWopNjZWvXv31oIFC8q0jYKCAj355JO+aQ0aN26se+65R8eOHQtR1QCA0oqKilLNmjUVFRVldClA2Ct3IPz888913XXX6ciRI8W2HzlyRNddd52WLFlS7uLKa8WKFerbt69Wr16tm266Sffdd5/S0tI0cuRIzZo1q1Tb8Hg8GjFihCZPnqy6detq/Pjx6tOnj2bPnq0+ffro+PHjIX4VAICSxMTE6MILL1RMTIzRpQBhr9yB8NVXX9W+ffvUuHHjYtsbN26s/fv369VXXy13ceXhcrl09913y2w2a9WqVXrrrbc0a9Ysbd26Ve3atdOkSZN04MCBs27nnXfe0dKlS3XLLbdo7dq1mjFjhhYuXKjXXntNP//8sx5//PFz8GoAAMF4vV55PB55vV6jSwHCXrkD4datW9WrV68S1+nVq5e2bNlS3qcol2+++Ub79u3TqFGj/CbLrlmzpiZNmqTCwkK98847Z93OP//5T0nS9OnTZTKZfMvvvfdetWrVSu+//77y8vIqvX4AQOlkZ2dr9erVys7ONroUIOyVOxCmp6erfv36Ja5Tt25dnThxorxPUS7JycmSpKFDhwa0DRs2TJK0cuXKEreRn5+v77//Xuedd55atGjh12YymXTppZcqJydHP/zwQ+UUDQAAYKByB8J69epp9+7dJa6ze/duJSQklPcpymXPnj2SpLZt2wa0NWzYUHFxcb51gtm3b588Hk+x2zh922fbDgAAQDgo972M+/fvr4ULF2rbtm3q1KlTQPvWrVu1ePFiXXfddRUqsKwyMjIknfqKuDjx8fG+dSqy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Gjx4sC699FJ17dpVzZo1U1xcnNHlwmBFVxdHRUXprbfe0n333afRo0drwoQJcjqdWrFihbZt26Zly5YpOztbubm5MpvN+vnnn9W8eXODq8e5dOaR4m3btql///5q1qyZXnzxRfXv318Wi8XXTjiEJCUlJal+/fr617/+pRYtWmjx4sW65pprNGjQIH300UeqXbu23/per1czZszQY489pv3790fcUUICIcrtxx9/1Ny5c3XNNdeoV69efgP2wYMHtWPHDn3//fdKTk7W5s2blZubq6SkJPXo0UP33nuvunXrZmD1qAqcTqcsFosWL16sCRMm6Pjx42rUqJEOHDiggoIC2Ww2tW/fXgMGDFDXrl01YMCAiBukcUpaWprS09O1dOlS7dq1S59//rnS0tJks9nUu3dvXXbZZbr88svVqVMno0tFFbBv3z61bdtWM2bM0MMPP+xbfvPNN2vBggX6xz/+oXHjxvmdonLw4EHddtttys7O1qZNmwys3hgEQpTbPffco9mzZ6t+/frq2bOnLrnkEl1yySU6//zzfes4nU7t27dPW7du1Zo1a7RmzRpt3rxZS5cu1aWXXmpg9TBKQUGBfvzxR3333Xdau3atVq9ercOHD0v631HDW2+9VTfeeKNatWql888/nyM9Eer48eNasmSJPv/8c23ZskX79u2T1+tVQkKCWrdurX79+mn16tVav369JKlu3boaOHCg+vfvr5EjR6pevXoGvwIY5ZFHHtHbb7+tjz/+WP369ZPH45HZbNbBgwfVsWNH1a5dW99//70aNmzoa/vss890/fXX66WXXtL9999v9Es45wiEKLeffvpJH374oT7++GNt2bJFktS8eXP1799fQ4cO1cCBA9W0aVPf+tnZ2UpJSdGuXbt0++23G1Q1jNahQwf99NNPvnN2unXrpi5duuiyyy7Tjh07NH36dN1///2aNWuW7zFF6yKyjBkzRv/+979lsVjUtWtX9ejRQ0OGDFGnTp3UokULmc1muVwubdq0SQsXLtRHH33ku4p0xYoVGjBggMGvAEZp2LCh+vfvr9dff1116tSR1+uV2+1WdHS0XnjhBf3lL3/R6NGjNXfuXEmnLlh77LHH9MILLygrK0sxMTHGvgADEAhRYS6XS+vXr9eCBQu0aNEi34zwHTt29J072K9fP8XHxxtcKYyWkpKivn376t5771ViYqJGjBghm83mu5Dk0KFDuv3227Vq1Sq99957GjVqFGEwgrVp00avvvqqWrRoofbt2we0Fx3ZKZKTk6Pk5GR98cUXeu21185lqahCNm7cqB49evi+Fj7Tr7/+qptvvlkrV67UF198ocsvv1w//vijRo4cqaZNm2rJkiUGVG08AiEqVVZWllasWKH58+fryy+/VFZWlqxWq+8r5aKvcxC5fvvtN9WpU8dv2el/2Ddu3Ki+ffuqffv2WrRokVq1ahXwhx/V3/fff68+ffpo/PjxeuGFF3zL+YCAs/n++++1atUqjRgxQu3atSu2z3z++ee6+uqrNWTIEH388cdavHixbrvtNn388ccaMWKEQZUbi0CIkElLS9MXX3yhefPm6dtvv5XL5VK3bt20YcMGo0uDQRYtWqTrrrvO70Tu0xUN3O+++67Gjh2rESNG6OOPPzaoWhjp0KFDGjNmjJKTk/Wf//xH119/fcDVxkBx3n33XQ0ePNjvlKXiPPDAA3rjjTf01FNP6dixY/rXv/6lrKysc1Rl1UMgxDmRkpKiuXPnqm3btrrzzjuNLgcG+OCDDzRq1Cht3LhRXbp0Cbqex+NRYWGhHnzwQb399tuaMWOGJkyYwATnEejnn39W165d1aRJE3399ddq1KgRRwhRokWLFumGG27QsGHD9NFHHyk2NjZgnaI+lJqaqr59+yojI0N5eXm64447NHv2bAOqrhr4DgbnRPv27TVjxgzCYAQrOkl7woQJOnbsWND1zGaz7Ha7Hn30Ud/tytLT089Vmagi3G63WrVqpVdeeUV79uzxTRJMGERJ3n77bUnSf//7X3344YeS/jd7QZGiPpSYmKj7779fTqdTXq+32PMNIwmBEMA5cfXVV+uBBx7QypUr9frrr0sKvD9x0c9Op1OtW7fWiy++qA4dOqh+/frnvF4Yq+ir4euvv1633367vvzyS82aNUter1cejyfgjzyQmZmpJUuW6MYbb5TH49HMmTN19OhRRUVFBb0X+vjx4zVw4EANGDBAnTt3PrcFVzEEQgAhVzQYz5gxQ0OHDtXUqVP1xRdf+B3t8Xg8vp8tFovy8/MVFxen5557zpCaYZyi/uLxeORwOPTyyy+rW7duevPNN7Vp0yaZzWZfYCQcosicOXNktVp111136aWXXlJKSooef/xxSYFHlov62K+//qohQ4boscceO+f1VjWcQwjgnCi6UnjNmjUaMWKE4uPj9fnnn/smMs/Pz9fBgwe1bt06bdy4UcuWLdOhQ4eUmpqqunXrGlw9jOB2u7V+/XodPnxYTz31lLZv3y6r1aqrr75aI0eO1OWXXx6R88WheB07dlSDBg30zjvvqGbNmho0aJC2b9+uL7/8UkOGDAlY3+v1asqUKXrppZe0Z8+eiJ/InEAIIOTOnDZm2rRpmjp1qsaMGaNhw4bpk08+0Y4dO7Rz507f0Z42bdrokksuYT65CLN9+3bfPay3bt2qbdu2SZLq1Kmj888/X5s2bVJOTo4kqWnTprr66qt14403Mgl1hNu7d6/atWvnd6u6+fPn69Zbb9XQoUP10UcfKS4uTtL/LiopulXdyZMnff0skhEIAZwT+fn5Onz4sNauXatdu3ZpxowZfu0dO3ZU69atfXOHdevWTWazWRaLxaCKca6tW7dOF110kSTJbrerXbt26t69u6666iolJibqwgsvlCQtXLhQb7/9tr766ivfY8+crxCR5eGHH9a//vUv363qitx3331666239M477+j222/3fVVsMpl8t6p7+eWXde+99xpVepVBIAQQUm+//bZ++uknLV68WIcOHVJubq4kKSkpSWlpaapVq5aee+45XXXVVUw+HeGmT5+uNWvW6Prrr1f//v1Vp04d311spFN3RTp9+qHff/9dH3zwgaZPn67XXntNV155pQFVoypo3LixLr74Yt+t6or6SkpKivr37y+Hw6GlS5f67nhTWFioSZMm6e9//7uys7PlcDgMfgXGIxACCJnTj/g0btxYw4cPV7t27XTNNdfIYrHo1Vdf1cyZMzV+/Hg9//zzMpvN8ng8TD4coc4MfFLg6QbS/6YRoZ9AklavXq3+/fvrjTfe0D333BPQPm3aND3xxBO6++679frrr8tsNmvXrl268cYb1bx5c3355ZcGVF31EAgBhMznn3+uH374QVdeeaUuvPBCRUVF+f1xd7lcGjJkiL799lt9+umnuuqqq7hNXYQry8TTXq9XbrebScsj3Pjx4/XJJ59o0aJF6tq1a0AfKigoULdu3bRv3z599dVXGjBggO9uSEXjDgiEAEKouD/uRcuKbkP2448/6vLLL1d+fr6+/fZbtWvXzqBqAYSjwsJC/fjjj+rQoYNsNptfW9E489VXX2n48OHq27evPvvsM/31r3/Vhx9+qMzMTIOqrnr4GA4gZEwmU8CEsEUBsWiy2PPPP1+TJ0/W8ePHNWHCBGVkZBhRKoAwZbVa1blz54AwKJ0aZ9xuty699FLdc889WrNmjR588EF98cUXuvHGGw2oturiCCGAKuG2227TvHnz9Nxzz+kvf/mL0eUAqGa2b9+uwYMHq7CwUFlZWdqwYYO6detmdFlVBoEQgKGKvkLOzMzUxRdfrLS0tBLvdQwA5fXGG2/ogQceUKNGjXT48GGjy6lSOBMXgKFMJpM8Ho/i4+P117/+VXl5eUaXBKCaGj16tI4cOaKWLVsaXUqVwxFCAACACMdFJQAAABGOQAgAABDhCIQAAAARjkAIAAAQ4QiEAAAAEY5ACAAAEOEIhABQTSUnJ8tkMmnKlCl+ywcOHBhwj2kAkY1ACACVLDU1VSaTye+f1WpVs2bNNGrUKG3bts3oEgHAD3cqAYAQad26tW677TZJUnZ2ttatW6f58+dr0aJF+u9//6u+ffsaUte7776r3NxcQ54bQNVEIASAEGnTpk3A17WPP/64pk2bpscee0zJycmG1NW8eXNDnhdA1cVXxgBwDj344IOSpA0bNujIkSOaPHmyevfurfr168tmsykxMVEPPPCAjh07FvDYks79Gzt2rEwmk1JTU89aA+cQAjgTRwgBwAAmk0mrVq3SrFmzNGTIEPXq1UsWi0WbN2/W66+/rqVLl2rTpk2qWbOm0aUCiAAEQgA4h1577TVJUs+ePTV48GClpaUpLi7Ob513331XY8aM0SuvvKLHHnvMiDIBRBgCIQCEyN69e33nEObk5Oj777/Xt99+K7vdrmnTpql+/frFPu7222/Xgw8+qK+//ppACOCcIBACQIjs27dPU6dOlSRZLBY1aNBAo0aN0qOPPqqkpCRJ0qJFi/Tmm29q06ZN+v333+V2u32PP3LkiCF1A4g8BEIACJFhw4bpq6++Cto+a9Ys/eUvf1G9evU0dOhQNW3aVA6HQ5L04osvqqCg4FyVCiDCEQgBwAAul0tPPfWUGjVqpC1btvh9fez1evXcc88FPMZsNvseGx3tP3xnZGSEtmAA1RrTzgCAAU6cOKGMjAz16dMn4FzCH374QXl5eQGPqV27tiTp8OHDfss9Ho+2bt0aumIBVHsEQgAwQP369eVwOLRp0ya/u4b8/vvvvrkKz9SjRw9J0ty5c/2Wv/DCC9q/f3/IagVQ/fGVMQAYwGw264EHHtCsWbN04YUX6qqrrlJmZqaWLFmiFi1aqHHjxgGPueOOO/Tcc89pypQp2rJli1q3bq0ffvhBO3bs0IABA7Ry5UoDXgmA6oAjhABgkOnTp2vatGkymUx67bXXtHz5ct1yyy1atmyZLBZLwPoNGjTQihUrNGTIEC1btkz//Oc/VatWLa1bt06JiYnn/gUAqDZMXq/Xa3QRAAAAMA5HCAEAACIcgRAAACDCEQgBAAAiHIEQAAAgwhEIAQAAIhyBEAAAIMIRCAEAACIcgRAAACDCEQgBAAAiHIEQAAAgwhEIAQAAIhyBEAAAIMIRCAEAACLc/wc2Z9yRlGfogwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_state_paulivec(psi, title=\"My Paulivec\", color=['purple', 'orange', 'green'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**plot_state_qsphere()** options\n", - "\n", - "- **figsize** (tuple): figure size in inches (width, height)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**plot_bloch_multivector()** options\n", - "\n", - "- **title** (str): a string that represents the plot title\n", - "- **figsize** (tuple): figure size in inches (width, height)." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:09:08.540562Z", - "start_time": "2021-07-31T05:09:08.227233Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:18.306822Z", - "iopub.status.busy": "2023-08-25T18:25:18.305000Z", - "iopub.status.idle": "2023-08-25T18:25:18.752819Z", - "shell.execute_reply": "2023-08-25T18:25:18.752096Z" - }, - "scrolled": true - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_bloch_multivector(psi, title=\"My Bloch Spheres\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using the output from state plotting functions\n", - "\n", - "When using any of the state plotting functions it returns a `matplotlib.Figure` for the rendered visualization. Jupyter notebooks understand this return type and render it for us in this tutorial, but when running outside of Jupyter you do not have this feature automatically. However, the `matplotlib.Figure` class natively has methods to both display and save the visualization. You can call `.show()` on the returned object to open the image in a new window (assuming your configured matplotlib backend is interactive). Or alternatively you can call `.savefig('out.png')` to save the figure to `out.png` in the current working directory. The `savefig()` method takes a path so you can adjust the location and filename where you're saving the output." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Plot Bloch Vector \n", - "\n", - "A standard way of plotting a quantum system is using the Bloch vector. This only works for a single qubit and takes as input the Bloch vector. \n", - "\n", - "The Bloch vector is defined as $[x = \\mathrm{Tr}[X \\rho], y = \\mathrm{Tr}[Y \\rho], z = \\mathrm{Tr}[Z \\rho]]$, where $X$, $Y$, and $Z$ are the Pauli operators for a single qubit and $\\rho$ is the density matrix.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:09:13.556822Z", - "start_time": "2021-07-31T05:09:13.553512Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:18.757885Z", - "iopub.status.busy": "2023-08-25T18:25:18.756680Z", - "iopub.status.idle": "2023-08-25T18:25:18.761557Z", - "shell.execute_reply": "2023-08-25T18:25:18.760971Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.visualization import plot_bloch_vector" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:09:14.078221Z", - "start_time": "2021-07-31T05:09:13.830668Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:18.766127Z", - "iopub.status.busy": "2023-08-25T18:25:18.764979Z", - "iopub.status.idle": "2023-08-25T18:25:18.953668Z", - "shell.execute_reply": "2023-08-25T18:25:18.952956Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_bloch_vector([0,1,0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Options for plot_bloch_vector()\n", - "\n", - "- **title** (str): a string that represents the plot title\n", - "- **figsize** (tuple): Figure size in inches (width, height)." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:09:16.121246Z", - "start_time": "2021-07-31T05:09:15.903295Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:18.958875Z", - "iopub.status.busy": "2023-08-25T18:25:18.957624Z", - "iopub.status.idle": "2023-08-25T18:25:19.151618Z", - "shell.execute_reply": "2023-08-25T18:25:19.150810Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgcAAAIYCAYAAAD5IU91AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9eYxsZ1rf/z37qb16776b737t6+XaHns89iy2Z0ZAADEjRSSZSMnMsEhAECAQJBJSBggkIUryC5GAINCwSUn4gxGygBDCzHgYPLbH+3b3pe/W3bfX6qo6+/L+/jjv+/ap6tq7qu/i9yO1uruWc95z6tR5n/dZvo9ECCEQCAQCgUAgoMi3ewACgUAgEAjuLIRxIBAIBAKBoAFhHAgEAoFAIGhAGAcCgUAgEAgaEMaBQCAQCASCBoRxIBAIBAKBoAFhHAgEAoFAIGhAGAcCgUAgEAgaEMaBQCAQCASCBoRxIBDcZfzKr/wKHn300Tt2e/3w3HPP4ed+7uduy74FAkF7hHEgEPTIl770JUiShJ/4iZ/Y9ty/+lf/CpIk4Utf+tKO9iFJEv9RVRUHDhzAz//8z8PzvB1tdxT8/u//Pk6dOoV8Po9yuYzHHnsM/+E//IfbPSyBQDAEhHEgEPTB/v378b//9/+G4zj8Mdd18T//5//EgQMHhrKPP/zDP8Ti4iKuXLmC3/md38Gf/umf4td//deHsu1h8dWvfhU/93M/h5/5mZ/B22+/jZdeegm/9Eu/hHq9fruHBt/3b/cQBIK7HmEcCAR98Pjjj2P//v342te+xh/72te+hgMHDuCxxx7jj/3Jn/wJJiYmtq34P//5z+Nf/It/0XEf5XIZs7Oz2L9/P37wB38Qn/vc5/Dmm2+2fX0cx/i1X/s17Nu3D4Zh4NFHH8Xf/M3fNLzmxo0b+MIXvoDx8XHkcjk88cQTePXVVxte86d/+qc4ePAgSqUS/tk/+2eo1Wpt9/nCCy/gn/yTf4If/dEfxdGjR/Hggw/iC1/4An7jN36Dv+ZLX/oSPv/5z+NXf/VXMTU1hWKxiJ/4iZ/YNnnHcYxf+qVfwvj4OGZnZ/Erv/IrDc9XKhX82I/9GN/Gpz/9abzzzjv8eRYW+YM/+AMcOnQIpmn29D6BQNAeYRwIBH3yIz/yI/jDP/xD/v9Xv/pVfPnLX254zQ//8A8jiiK88MIL/LHl5WX81V/9FX7kR36k532dP38e3/jGN/DUU0+1fc1v/dZv4b/8l/+C//yf/zPeffddfO/3fi9+6Id+CBcuXAAA1Ot1PPvss7h58yZeeOEFvPPOO/ilX/olxHHMt3Hp0iX8xV/8Bf7yL/8Sf/mXf4lvfetb+I//8T+23efs7CxeeeUVXL16teP4v/71r+PMmTN48cUX8b/+1//C1772Nfzqr/5qw2v++I//GLlcDq+++ir+03/6T/i1X/s1/L//9//48z/8wz+M5eVl/J//83/wxhtv4PHHH8dnPvMZrK+v89dcvHgRf/7nf46vfe1rePvtt3t+n0AgaAMRCAQ98cUvfpF87nOfI8vLy8QwDDI/P0/m5+eJaZpkZWWFfO5znyNf/OIX+et/8id/kvyjf/SP+P//5b/8F3L48GESx3HbfQAgpmmSXC5HDMMgAMgP/uAPEt/3+Wu+8pWvkFOnTvH/9+zZQ37jN36jYTtPPvkk+amf+ilCCCG/93u/RwqFAllbW2u5z6985Sskm82SarXKH/vFX/xF8tRTT7Ud58LCAvnYxz5GAJDjx4+TL37xi+TP/uzPSBRFDedrfHycWJbFH/vd3/1dks/n+eueffZZ8olPfGLb2P/1v/7XhBBCvv3tb5NisUhc1214zZEjR8jv/d7v8fFrmkaWl5f58728TyAQtEd4DgSCPpmamsIP/MAP4I/+6I/wh3/4h/iBH/gBTE5Obnvdj//4j+Nv//ZvcfPmTQDAH/3RH/Gkxk78f//f/4e3334b77zzDv7yL/8S58+fbxuKqFarWFhYwMc//vGGxz/+8Y/jzJkzAIC3334bjz32GMbHx9vu8+DBgygUCvz/ubk5LC8vt3393NwcXn75Zbz33nv42Z/9WYRhiC9+8Yv4vu/7vgaPxKlTp5DNZvn/Tz/9NOr1Oq5fv84fe+SRR7Ztm+37nXfeQb1ex8TEBPL5PP+5cuUKLl26xN9z3333YWpqiv/f6/sEAkFr1Ns9AIHgbuRHfuRH8NM//dMAgN/+7d9u+ZrHHnsMp06dwp/8yZ/ge77ne/DBBx/gr/7qr7pue3Z2FkePHgUAnDhxArVaDV/4whfw67/+6/zxfshkMl1fo2law/+SJDVM8u146KGH8NBDD+Gnfuqn8BM/8RP45Cc/iW9961t4/vnnex5fp33X63XMzc3hxRdf3Pa+crnM/87lcg3P9fo+gUDQGmEcCAQD8H3f933wfR+SJOF7v/d7277ux37sx/Df/tt/w82bN/HZz34W+/fv73tfiqIAQEOFBKNYLGLPnj146aWX8Oyzz/LHX3rpJXz0ox8FkKzM/+AP/gDr6+sdvQc75eTJkwAAy7L4Y++88w4cx+EGyiuvvIJ8Pt/zeXj88cextLQEVVVx8ODBnscy6PsEAkGCCCsIBAOgKArOnDmD06dP88m7Ff/8n/9z3LhxA7//+7/fcyJipVLB0tISFhYW8K1vfQu/9mu/huPHj+OBBx5o+fpf/MVfxG/+5m/iz/7sz3Du3Dn8m3/zb/D222/jZ3/2ZwEAX/jCFzA7O4vPf/7zeOmll3D58mX8+Z//OV5++eX+D5zykz/5k/h3/+7f4aWXXsLVq1fxyiuv4F/+y3+JqakpPP300/x1vu/jR3/0R3H69Gn89V//Nb7yla/gp3/6pyHLvd16PvvZz+Lpp5/G5z//efzt3/4t5ufn8Z3vfAe//Mu/jNdff33o7xMIBAnCcyAQDEixWOz6mlKphH/8j/8x/uqv/gqf//zne9ouq3yQJAmzs7P41Kc+hX//7/89VLX11/VnfuZnsLm5iV/4hV/A8vIyTp48iRdeeAHHjh0DAOi6jr/927/FL/zCL+D7v//7EYYhTp482TYc0guf/exn8dWvfhW/+7u/i7W1NUxOTuLpp5/G17/+dUxMTPDXfeYzn8GxY8fwqU99Cp7n4Qtf+MK2UsVOSJKEv/7rv8Yv//Iv48tf/jJWVlb4OZmZmRn6+wQCQYJECCG3exACwb3MZz7zGTz44IP47//9v9/uoewqX/rSl1CpVPAXf/EXt3soAoGgT4TnQCAYERsbG3jxxRfx4osv4nd+53du93AEAoGgZ4RxIBCMiMceewwbGxv4zd/8TZw4ceJ2D0cgEAh6RoQVBAKBQCAQNCCqFQQCgUAgEDQgjAOBQCAQCAQNCONAIBAIBAJBA8I4EAgEAoFA0IAwDgQCgUAgEDQgjAOBQCAQCAQNCONAIBAIBAJBA8I4EAgEAoFA0IAwDgQCgUAgEDQgjAOBQCAQCAQNCONAIBAIBAJBA8I4EAgEAoFA0IAwDgQCgUAgEDQgjAOBQCAQCAQNCONAIBAIBAJBA8I4EAgEAoFA0IAwDgQCwUB89atfxdmzZ2/3MAQCwQgQxoFAIGjLb//2b+PgwYMwTRNPPfUUvvvd7/LnXn31Vfze7/3ebRydQCAYFcI4EAgEnOeee47//Wd/9mf4+Z//eXzlK1/Bm2++iVOnTuF7v/d7sby8DAD43Oc+hxdeeOE2jVQgEIwSYRwIBIKW/Nf/+l/x4z/+4/jyl7+MkydP4n/8j/+BbDaLr371qwCAz3zmM7h16xbef//92zxSgUAwbIRxIBAItuH7Pt544w189rOf5Y/JsozPfvazePnllwEAhmHge77ne4T3QCC4BxHGgUAg2Mbq6iqiKMLMzEzD4zMzM1haWuL/i9CCQHBvIowDgUAwMN///d+P7373u1hdXb3dQxEIBENEGAcCgWAbk5OTUBQFt27danj81q1bmJ2d5f9fuXIF5XIZ5XJ5l0coEAhGiTAOBALBNnRdx0c+8hF8/etf54/FcYyvf/3rePrpp/ljL7zwAr7/+78fqqrejmEKBIIRIYwDgUDQkp//+Z/H7//+7+OP//iPcebMGfzkT/4kLMvCl7/8Zf6aF154AZ/73Odu4ygFAsEoEOa+QCBoyT/9p/8UKysr+Lf/9t9iaWkJjz76KP7mb/6GJyleuXIF586dw/d93/fd5pEKBIJhIxFCyO0ehEAguPv4rd/6Lfz1X/81/u///b+3eygCgWDIiLCCQCAYiBdeeAE/9EM/dLuHIRAIRoDwHAgEAoFAIGhAeA4EAoFAIBA0IIwDgUAgEAgEDQjjQCAQCAQCQQPCOBAIBAKBQNCAMA4EAoFAIBA0IIwDgUAgEAgEDQjjQCAQCAQCQQPCOBAIBAKBQNCAMA4EAoFAIBA0IBovCQSCoWFZFoIggCzLUBQFhmGIds4CwV2I+NYKBB8C4jhGHIaIoghxHIPQH/YcABBCtn7YY+w17DFZhgRAkiRIcuJ4lOnvumXBdV2APg9ZhizLmJqchJnNCiNBILiLEL0VBIK7lDiOEQYBoiBIJv0w5AYAkSREYYg4ikDiGHEU7WhfBOi4jbplwfU8AEDGNEEIQRhFCMMQiiyjVCxCVhTIigJFUSDLMggAWVWhKgpUTUv+VlWomgZN13c0XoFAsDOEcSAQ3MGwFX8YBAh9HxH9OwpDRGGIbl9f9nxMCKQdjKOTcVCzLHjUMMjncjANg499s1pFFMcwdB2FfB58tB3GTQBAkhIjQdOgGcbWbxGmEAh2BWEcCAS3EUIIoihC4PvwXBdg7n7mFQjDVm9CTH+32h4hJDEq6G9CCCIWSqDPA3QSZmEEtl1qRMSpbRP6OJrCCgAQBAEsx4EEIJfNwtB1gD4nSRLCMIRlWSAAstkssqYJmYYkZEmCLMt8Ww3Hkdp3MzL1NGi6vvVjGDAzmd5OukAg6IowwQWCEUMIQUjd/WEYIggCeI4Dz3Xhuy58zwOJomRSpROxoihQaFKfoigAaJwf1JuQmvxZPgD7v2nnSE+vcRzz/aDFpNzxOJINNKz+LctCRAhMw4AkSfCDoGHfkCRAkuB5HoIgQBxFiTGQnvQlqcFQaP5NJAmKLIPQ/dXrdVj1Our1Ouq1GmqWBd/zkM3lkC8UUBobw9jEBKampjAxNYWxsTFomtbXsQoEH3aE50AgGDKEELiuC9u2EYYhfNdFEIaIfJ/nCDAkAJAkKIoCEscIwhBBGIJEEUJmBNCJn63yZZrop8gyVFWFlnKzp7/MMl2ds1U6My6IJCU1zHTiZmPo9FuSJB5WYLcMx/NgWRYkAKViMZnI6b7TRgghBBubm4jjGPlsFrKiNBwT3SgIPR+O58Gq12E5Dhzbhm3bsFKhCwDQNA35bBb5QgGFfB66YcC1bdRtmxsPURwjRpJUmcnnUSwWMTY5icnJSRw6dAh79+4dwqctENybCONAIBgShBDYto3NzU14tg3fdRH6fjKhUwNAkmU+IUqSxCfIKIoa8gPiKELEfsdxEhag3gWJ/gDgK27TMJAxTWia1vB827EOcnwprwQhBJXNTURxjGwmg2zKpc8m+TQsL8E0DORzuYbtxHGMII6xcPMmrs7PY2VtDQCgyDIy2Sxy2SzMTAamaSKbySCTzbb0BCjMaKJelyAI4LguLMuCQ42Ger2OGk2enNu3Dx//5Cdx7NixrudLIPiwIYwDgWCHhGGIaqWCyvo6fMdBFIaQZBmGrkOWZb76D8Nwu9u/CZbNnw4pMK+CLEm8AiCKY0RRBM/zENFVOiEEhqbBMAzo1EjoBJvEe70BpI0D1/NQtyzIkoSxcpnviyUTSk23FT8IUK3Vtr3esm1cuXIFV+fn4fk+puiqfnJqCoZh8PGxvIkodewx/R1FUddjUBUlqYSg53N5eRnnzp3D+vo6SuPjePzJJ/HIqVMwTLPHsyEQ3NsI40AgGIAoDOFaFirr66hubiKOIoRRhJgQaKoKhRoFrZDoxK+2MAKaJ3RmWERx3LLagBACPwjgui58lrxIPRWmYcAwDJ6z0I5+jYO01yCXzSLDJtQOeQyEEGxUKogJQS6fx/raGq5cvoxby8vQNQ333XcfDh06hEKh0GEAhBsJzbBzxIwFbji0+wwkCaqiYHNzE5cvX8bq8jJyhQIeeOABnHzoIRTHxmBms1zDQSD4sCGMA4GgR0Lfh2vbcGo1VCoVWJbFV/IE2Fqxp94jKwo0Wr+vqSqv8e8ErzigiYa9fkWjKILreXA9b0vMCIChaTBpOWAnb0K3vTDjgHsNZBljpVJDrkEnX8XK2hquzM9jaXERYRBgfHwchw4dwr69e6H0UZ7oU12HMAgSb0yb88M8DjEN20Q0KbSVp8GyLCwtLmJtbQ2GYeDI4cM4dvw4ymNjyBcKyJdKkLsYWQLBvYQwDgSCDsRxDLdeh1OrwarXsVGpwLJt7hVQVJXH+mWA1+YzY6ClIdDmKxezkkE6qRO6Gpb7rSogBL7vw/W8xJtA98dyE0zDaD0uGppou116zJXNTYRRxL0GrXIM0sd0a2kJV65cwcLSEmRZxuzcHE7efz/K5XLX4wjpZM6rPVhuRiqUwvbTZiPbvBksz4EZDCETiZIkuK6LxaUlrK2uQpZlzM3O4siRI8jl8xifnMTk1BSy+XzHcQsE9wLCOBAImiCEwLdt1DY3YVWr8HwffhAkngKaGKhrGvL5fGIYMGW/AcV50noE6TE0ZPMDDZNhyxADezxVKhjQvATuTQAgEYJsNosMLT/clnvAJlT6m1BdBUIn52q1CkgSxthqOjVGKTWWmzdu4N0PPoBjWdxLkC8UIMkySoVCQ1JhHMdb+RR0P1FKdCm9XVmSktwMWYbKPDH02FoZCdvOVZOxwHQd/CCA53nwfR+1eh0Li4tYXl5GNpPB0WPHoNPPOJvPY3JqChPT08jS5EiR0Ci41xDGgUBA8RwH1Y0NWNUqXNfdWpXGMWr1OiRJgq7rGB8bQ9Y0dzwhtDIK0s91/GqmJm72eomQtmMihMDzfbiuiyCKAEKgqiryuRzUtLucbrN5K0wh0XYcOK4LTdNQzOcbQgoMx3Hw9ttvY3FpCXvn5nDi/vsxRr0EtXodruclioequmUIxHFSXsmqOVLVGCoNx2iKApnmc0jtvBzUSIiYnsIO8IMAKysreP311wFZxtFjx7j6Iys/zeZyKJXLGJ+chGma/EcguNsRxoHgQwshBJ7noVapoF6pwHWchgmF6QI4npdMTqqKcrHYNcGv636xlWhI6ESYnsaYO73vcMK2B1p/tR3XhWXbfF/ZTAaZtLHTLFKELeOA6xXkcokaIns9fc38/Dzee/ddKKqKxx59FHv37AHo8bKKhVq9Dk1VkctmG/bF+yzQ5Ey1U1iGaSjQ39sMAWZ49XLiumDZNr7zne/Asiw89vjjyGQyPO+BKU/KioJCsYjS2BjKY2MoFArIZDIioVFw1yKMA8GHDt/3UatWUatU4FpWw0Soqip0TYOh6wjCEDXLAqEVCKVCoePNnpUrBmHIXeQh/TsKgiQkASSTWUqYiIkasZVz+nHW/VChssX8tT1OOsz4YDF6RhRFsGwbfhCAEAJVUZDL5bigUnMeAQHgex42azUAwFi53DCGer2Ot958Eyurq7jv4EE8/NBD0DUtUYOkYRlQpci640DTNIyXy4khQI2CQVb6rao4CD2/zLuQDs8M6kvwgwCvvPIKVldX8ZGPfAR79+5NBKtoKIJ93oHvQzUMFMpljI+PY4waCqIfhOBuQxgHgg8FURTBcRxsbmzAqdUQeh4XFNI1DbquQ9d1KHTCs6jaHoljaJqGrGkmE0Bq0o/S/0fRljRwSo2QewVSJXjp3gZ8smJuclalQN/DIVsyyFwAqUlmWFVVGLoOQ9f5MW29nTTsh+3X87zkOGkYI0OFhtK5CGystVoNrudB1zQU8nm+rQsXLuD06dPIZDJ47LHHMD4+nsTug6BBG0Gmhpdl21BkeZuB0ZbU2JmXgI2NhV+k1HERpgRJH+cS06ntcKEo+ngvXpoojvHmm2/i6tWrePDBB3H8+HG+3zCKEAQBfN+H47rwfR+SJCGTy6E0Po7x8XGUy2VkRP8HwV2CMA4E9yxMxtiyLNQ3N+E7DuIggCRJMGjWfjqZzA9D1Ot1rK2vw3YcRFEERVWhs1UfnfhlbCX+cSOAwlb1Ko2NK7RNsUT7BwBo7IVACAgtrYupi5pl5KdLGdnf7LiA1qWHzJVOSNKfQU+JIhnUAEqv0JlRYNk2XN8HCIEiy0kugq5zMSMCYG19HXEcJ3LFuo5KpYI333oLmxsbOHzkCA4fOcJLBfn5oWJQhq7z1fMG1YUoFAowmpUOW4Q0utG2UiEFO4edRKiYYSe3CqukDIszp0/j9JkzOHTwIE49+ug2AyeOY3i+D9u2YTsOwjCEkcmgUCqhPDaGiYkJFLp4oQSC240wDgT3HEEQJHr8tRoc20bouokHQFVhmiZXLgzDELbjwHYcWI4D33XhBQFi6qo2DCOJq1PXPEuMU6ibn7nD+eOpJkkMlhzX69dsW4VC8/OpXIX0bxbS8IIAge9vqTHKcmKIJG8GUp4S5mFgQkm+76Nu24mHgyTNlLLZLGQpaahU2dyEJEkoFAo4f+4czp09i1yxiAceeAC5bHarUkKSuCGiqWpDF0cgSUr0fH+b7PKg9GIcpM9fO2GkBlguSLMUNfVOXL12DW+88QYmp6bw1FNPtW3sFIYhHMdBnUo2q5qGXKGAYrmMsbExjI+PN3h4BII7BWEcCO4J4jiGbdtwaLOewLYR+j4kAKZp8oQ723Fg0RWd53mQJImHElzX5Z6CcqkEM5NpkDLuF4JESbGfL1g3eeV+tuP7fvJD3d0BNRh4+AJbngOWa6FrGkJqHKiqCkgSctls0qfAcWBZFk6fPg3LsnDfwYM4eN99fAWspbwTrLywFSwhUtd1FIekGdCPgRBRJctBkWj+x/KtW3j11VeRzWbx9DPPbIUM2mgr+L6PmmXBsizEAHL5PHKFAkqlEsbHx1EqlQYek0AwbIRxILiriaKIN9QJaQvkOAyTlbGmISKEGw2u6yZ18ixWT1fRpmEkiW20PK2Qzw8lgSzsw2OQPh7em6DHBD0eZ0/lJTS751k83g9D3j7Z930EVGWQvZKFJaIwhBsEkJHkCji2jeXlZayvraFUKuH4iRPIZTI8z0HXtLZu8uY8gDCKkj4LVGGx1XGyz4d9VgC25UCkjzdqUX7ZDu5tGcKtr1qt4qWXXoJuGHj+uee2GZGtelfEcQzLsrBZq8HzfWRod8l8sYi9e/cil8sJ3QTBbUcYB4K7kpDmBziOk8ga1+tQkKzqAqoOaDsOTzZjk42iKFvd/TIZaJqWrIhpaV+xUNhxqSIA3kkxnXAINE1uTX+3658wCtLJjwHtzeB5HhcB8nw/SeL0PFQ2N3F9cREEwPTEBPbu2YNCoYB8Nsu9Lv3um1U9FPP5rrF31p6adVxM/8+MB8JyLZiRlDrfzcmd7P8wDHs2wDpRqVTwzW98A4ePHsUjDz2UdN5Mf96pclWWD8Imf9d1sVmtwrJtZFPhhsnJSWRoYqhAcDsQ9TWCu4owDFGr1eA4DgDArlZhV6tc5Id1RJQlCaosQ1YUZAwDBjUIDCZik9qebdsgAHI0jNA3zRN/DxnwUou/hzUNsPJFnvRI3ejsN5NlJikJYibwpNFOiGEQoG5ZSTlntYqxbBZBHGOsVIIqy7BtG77nIWuaSTvlpvOaHgubrJnnAAB0TeOJi2qLc55usMRyKtrBjAVQT0OzAcESDRksN0KlIkxA782nWlEul/HQww/j3Xffxcz0NKanpxvG1oyUMhRM04RhGAiCAKvr61i7dYvnKRSLRRSLRWSzWWEkCHYdYRwI7gqCIOCeAiBR4Vu/dQu+40BW1cQrIElQdT2pRDBNZDIZmLqerORaQOIY9XodBICmqtsMh5Y0GQKtGCRvoBcHXrNgUpya5CMq6xylKiF6UViUgK2KCgCQJAS+D0mW4fs+bly7holyGfcdPIg33noLM5OTiQFFCBRNS3I96nV4nodcNotcLocsPY+dei6oqgrf9xMZ5zaKgtyQYV0WUx0qWW8Edh7aKU0C4DoRzNOQ/jsGtqpPsN2j0yvHjh7FreVlvP766/jsZz4DnZ6D9LXAkxtTBhno/gxdx+z0NOq2jdrGBsJU6Mc0TeTzeWEkCHYVYRwI7mh830e9XofrugAA27ZR2diAV69DV1XohgFD05ChKn/9qNJZjoOQrl7zudz2F/RgCDTTTt+/6/vaTWxIXOVItSMOqM5CrxFBJbWaltPueVa6J8tJCIHV58sylm7exMULF7D/wAEcPXIEtXodiiShWCjANM2t3IQoguu6iAlBvV5HtVqFLMvcUMiYJhRZ5hMty31g4YiYlT22GrckAVQxsflImXuepIwF1o8h3eYa9DVhG4ONVYewRlmqpvGx9WUwSBKe+MhH8Hdf/zpef+MNPPPMM9teQpoMNpkmNrLtyrKMYj4PU9dRrdVQr1QQRRGy2Sz3mOXzeZGTINgVhHEguCMJwxCbm5vwPA8xXeGzpEMVQEbXkTFNlEqlJEu8z/p4z/N4/wTWDAjYuWu/X68BVzDEVnyc/eYKi1R1sRWKJEFSFP473ZSIudqbJ5L0qjWOYzi2Dcfz+LFfvnwZV69exQP334+jx4/DtqxktS1JPDTjUGMtm8lgamICFq0ScVw3SbizbdTqdciyjGw2i3w2iyw13AiSVtaQpMTTwcSNOpyn5ue40ZQqH2USz+kQAjMUWnke4iiCREjSlZFWdgDgOhXNxkJ6HK1yRkzTxBMf+Qi+89JLuHTpEo4cOdLhiLauFUI/R1YGqus6yuUy6pYFp1qFIsuI4xjZbBbVahX1el0YCYKRI4wDwR0FoSvQer3ODQTHcSATAikMYSoKctksCsXilrZ/8sae98GkgwEgY5rQ29SoDzr+bvAbemqSDgnhHgGmuNjqfaz7I9NXSCsxdhtXWriJCUQ5rsvd3Yos48yZM1hcWMDjjz2Gw4cO8fOk0OoNiRoIEs07YMZbIZdDIZdLjA3Pg21ZcFwXURzDtizU63VIsoysaSKfyyXiU0hCOzLAEwhTA+5+Hls9RidydrwyNZiIokBvcY7iOIbr+whoqWdEyz39JmNB07TknFNjoVXOCICkxfPRo3jv/fcxOTmJYqnU1eBkSo+8xwQ17EqFAmzbRnV9Hflike9f0zRUq1U4joNyudxWY0Eg2AnCOBDcMfi+j0qlAtd1sbGxAcdxoKsqtDiGRAiK5TKKhcJ2YZpeSK3QLdpPQVVVZLPZoR5D2jhIGwHNk3e6D4NPKwOakVPGgEaFlhoS63oxCKRGGeeYkC2vCd2GRvsbvPbaa6hUKvjY009jz9wc914AaCztlCSYNFxQtywEQYBavY58LgeFGm/ZTIYbILbjJFUlVMLachweBshmMijkcttLR6VUt0dastjLJMtllNMhITqZp8sfWXKkLMswNA0a9T7EhCCin0vaWPA8Dx77XJixQD+b5jDEww8/jNXVVXz3tdfw6eef54ZVq26XrWAeBQmJZ0ZV1cTAqlYTXQQqnBTQrpH5fB4F+r0QCIaFKGUU3HbiOEa1WsX6+jo2NjZgWRaymgZVlqEqSpK1TcVy+r1cmX4+u3EySduYEJRLpaFI2BIkSW2gSoKtbtIRa8xDPQNxyhhgOQoKdWfzSYfG2rmcbx+6B2xc6b896ilg51BVVWRME34Y4jv/8A/wgwAff+YZjI2N8fj4ZrXK3/+Nr38dn37+eZTLZT5xs5LSmHUmzOeTHIOU7DIbg5MyFIIgQLVeR0ySrpC5bBaFfB75bLblZ5L+1DtNsn3nfBCCiBoErYhpyWOYMhaaSRsLGu0kWa3V8I1vfAP79+/Ho489xvfFPsNeDYX0GD3P4+GcXD6PsclJaNR7pigKSqWSaBctGBrCcyC4rTiOg4WFBaytrcFxHBiKgiIVJioWi8jlcnzl2KthwEsD2WRKfwdBwLUP8rnczg0DtjpN5w3IMkBXxemcgVa5CIqqQqOVAir9zVe9tF6/+Rha0a5skq2WfTqpxLR0UaFGgaHr2KhU8J2XXoKqqnj+ueeS843k3PlBkIxTUbZ7NuhnoqoqCvk86paFKIqSpLm0J4CVVZKkqVPGNIGxMXi+D/nWLTiuC1WW4Xsell0Xa4qCcrG4rQNmw9E1yRk3DKvtWWoDTfbclpcBGvKgQlks9MSNhSBIjAWaAMk0IoAtY+HkyZN49733MDM9jbm9e7eXU/YxRpnmIsiynHhfajVYtVoixTw2BjOTwfr6OjKZDIpDaCsuEAjjQHBbCIIA165dw/LyMnzfhxrHKBgGCvk8z4jnJXvoYhikJtS0lyBNHMeoWxYIwDswDoQkbb+x08nP832+Km413nSSm6ookOgKm+kNsG33YhAAW2GDdnoKPh1PxDwTVADKoM2XFqn8b6lYxDPPPMNzONhqP6TGgZbSA9g65K0EUEVVUSgUkjwRaiDkcrnGJk/09Wzbhq5jrFxGIQyhaRrCIECNGhhrGxvYqFRQokZCc8ih2VBoroSIm1/ThVafVUN+RvIiAOjLWJiYmMDU5CRee+MNfCKTQaFQ2MoPaPWZdbjGWckpCEEul0sqSzwP9VoNvuPAzGah01CO53lcH0EgGBRhHAh2nWvXruH69euIfB9SHCOr6xifmMD42Bi/ATIaJs806Zr/dA5Cm4nSoV0WJUniq+NuEEK4jn47vfwgJUcMbEkmS5KUGAKpn1ZCPGwfW0/0ETpoZahgK+GSJTXKkpR4CgyDn6cr8/N48803sWduDh/96EcbVpps26xCQtU0wPOad7NVIULLIYuFAuqWxctPs7kcF0dqTpokcQxFURCGIfc+lMpl1Go11Op1BEGAjWoVG5UKCoUCxorFtgZd2lPEQjwsLNJLHL5tGWlzFQndfjok0M1YOPHAA/juq6/i/Q8+wEMPPZTkONCGXts8V10qbiQkhhgJQ2SpYJdNu2lKigISx6hVKsgWCrz52Bj7TgkEfSKMA8Gu4TgOzp8/j82NDSAIYOo6JqenMT42xlXy0u531ma3IU6bMgR6TcBiGfQxgEIPq6m0oSFtPcifD8MQHs1mT08siqLANAxINFeiYTKkE0qnssKuNBkord7lui4sx+EeBdMwYBgGn4gIgDOnT+PM2bM4cvgwTj36aMN2uJRzSkdBa9NnQkJTlYEkIZ/LwZIk3rI4juOGzov81VRvAdj6zGVJQqlYRKFQgE17D/hBAMuyEm9ENouxLnH19Dnm+RYdJt2eQ1WpbaY/rxjgba3ZMaSNhQIheOD++/H2u+/CsW1kstmkOZjj8EZV6bbhzSGTZs8F8yBEUcQ7hrI8DgAwDQNuvY7N9XUUy2UEQYCJiQnR+VHQN8I4EOwKy8vLuHjxIkLHgQJgamYGs9PTW5MW8xBQWGJZOuO+oUqhj8xs1/MSRT8aC25L0ySS3gOPK/v+NtU7thJkK7R0IyP2mnYGQE+GQTps0gamLRCEISQAuqoim8s11OgTAG+9+SauzM/j4YcewrHjx1vqBzR4DZo8Hq3GRg+E/87RqgXbtrlAUr7JKJOwJS3cnOQnSxLy+Tzy+Txsx0G1VuNll5ZlwTRNjJXLyLUx9FjFAtJGQtNny/7aaRdMGU1ehab8BVmScPjwYZw/fx6LCwt4+JFH4NGW2kEQIAgCyLLMW2g3rPLTFRupY5AlCZBlRHEMwzBACOHJnhI1TjRNQ61SgVWvw3NdzMzObnWNFAh6QBgHgpHi+z6uXr2KW4uLIL4PU9NwYP9+5NOtepsNA2YUAFtu/QGJ4zjJ0AeQbV5xtskfSI+D1bs3ixCxmznTGki/R1aUrWqEXibWlk+lyvG6GBC+78Oybb4KzqXc+Wnef/99XLl6FU8+8QQOHDjQcZushLFbDX26Y2Ia0zQhS1ISZvA8OJK0bXJSFGVrZdyGbCaDbCYDz/exubkJm+Z0LN66BU3TMFYub6twkNqd17TXhXqlwijq2AOjF6QmI4TlY7BRKLKM48eO4d333sODJ0+iWCggiiJ4tMFVHMdwXReu60Kjqp962puQHjszEGSZG8+maSImBL7nwbZtSNksdF1HLpuF7bq4MT8Pq1rFvoMHRVtoQc8I40AwMtbX13H16lXUKxVIcYzxYhF79uzh5VcAkjItmgsAoG0cfVA8z0tWc0BjglwbCI0ZeykRHIamqjyZsZMnQAIAevPutq9Wj7Eukt2ISdKO2vN9SFJShZDP5bjcMHP7S4TgwqVLOHfuHE6dOoUDBw60nZDZqjsIAsRxnGyLTkKszXH6uPgqHSkjhq7adcNAFomuhO26kGQZhmlyNURJlkEkKdkmO3dtzpmh65iemkpKIGs11CwLcRxjeXW1ZYVDelytIEgMRy7t3EeOQieaEzDZpH7w0CGcPXcO5y9cwKlHH4WiKElnUNNEEATwaPtspq9gU/0F3TC2N6ai21QUJckroeWgoEmxlm0nOS+ahmwmA8d1sb62Bsu2ceC++zAzN7fj4xTc+wjjQDB0wjDEjRs3sLK8DLdWg66qmJ6ZwcTYWCKbyyBJ5z1eukcfjvqpAe8Ac7cSJCvQTjdEvpLzvG369wZt5tRP6aMsyw1aBi3H1zTW5soD5q4GIVutiOlzAdUXYGM1abMpiU62fPuE4MbNm3jnrbdw/MQJHD16tKVhkD7miHZvlGhXS6C99yL9vobJmP5t6Dpi2rfBsqzEaNC0Lf0D1mqZVRo0GR5pJACarmNifBylUinpP1Cvt6xwUFS1o0eCpBo18bCD1Ng5cqekx69rGo4ePYozZ8/i/hMnYFAvCuuGqes6ojiGnwpduZ4H1/OgqiqMFkYpU7Vk3SsztFrBpx01C/l8IvSVSl68dO4cqpubOHrixFA0PgT3LsI4EAyVSqWCmzdv8s5ypXwekxMTKBYK25KtIrqil0ZgGLBVVExX4q06LsZxzD0E6VI9SZL4zXibch96TyJUFCWJp7d6bUpXn50DXpLXtC92PGyytx0HnusCksQVCVuNEwBWV1bw2muv4cB99+Hhhx7qOF4u5UxDCszt3yvtzksmk+E5G1a9DpmWJ0qSlHgPWPvopjBD8yTdUFIoyyiVSigVi0nDp1SFw3qlgiItidXbfO5Ri1yDbcmnQzISGEeOHMH58+dx6fJlPPjgg9zoYyiynHQSNc1EOdPz4Ke0MmzHaQhnAUlOAz8SWUYulwOh0uO1eh3FQgGKovDqCMuysLSwgFq1ihMnTyJfKAz1GAX3DsI4EAyFMAyxsLCAjfV12JubkAjBzOQkSqVSy3rriArMNGfKNyd09UNDS2NJ4o2VMimvAXOZe563LY9A0zQYhgGtKY8gPb5WFQftYKVn3PBgdf6SBMLc3k0TRKfpKAxDWJbFmwuZVFSo3Xiq1Sq+8/LLmJycxEc+8pGekzhZCWS/JXCdzks2m922qmWdISNQFz/zUmxtMPFgtNsffU2hUEiSF20bm7UaAt9H3bKwUakgl8+jVCgk3TolCREVqGpWzty2XbptoNEo2QmGruPIkSO4fOkSjp84sXWdpTxCyW63Kh6YAev5PuIo4mJLiqLAMAwujBRTQ5slhLLk1LplcVlrLSVYZdXrePfNN3Hk+HHMzM3t+NgE9x7COBDsmGq1ihs3biCwbXi2jSxVNyzm80mMOQWbEJVWk++AMd8GPQL6fs/z+CRgGAaCIIBPb7JpVJpHYHTLR+gQD2fPt5pAJCTNfxryKpJBb72v+wEm2fqOk2gKUG9Bp2RB23HwDy+9hFwuh6c/9rHOLuSm886qB7bFuncA05eI63WEzEBIeZPanVm5S+5A8/aZQFC1WuXVEpZtJx08i0V+ztIemeaEwnaPN2scDMLRo0dx8eJFXKEGQrpEEdh+HmRZ5sqSLDfBDwJEUQTbtuE4TiKuRZsysffkczlUa7VE+4B6EFir7kI+n+hghCHOnzmD+uYm9h8+LModBQ0I40AwMHEc4+bNm1hfXQXCECQMUS6VYOg6crlco2HAbrbt6s3p9vp2Y6erGVLvdRwnyepWFFRrtYawAcsj0A2jodRv27Y7jCXdM4C+oe1rZQBQlKT/AH2s1wK6KAxhM0EjGp/OZrMdM+z9IMB3/uEfIEsSPvGJT/BJo+2xpDw4hGbwA2gbqhgUSUp0EGq1GiLahpvtt1NJYSvxoU6wydRxXWzWanAsC47joEZX0WOlUsOxpftANJSypjxRzOOT9kANYiRkTBMH77sPFy9exJGjR7d5Z3giZSqZkaHREsU4juFT71cURYnhGwSQXBeZTIZ7JAr5PGr1OuIoQt2ykMtmE28NNR5qNF/j1q1bcBwHe/bvx/jk5ABHJbgXERkpgoGo1+s4d+4c1peXAc9D1jC4YWCa5pZQDdkSLuqYPd6P14CtttjE3vS+wPfhuC5X2mOGgaHrKOTzKJdKyGQyLQ0DQre3bSxNY+91YmDvkpHElLkbuYfVsOe6qNZq3AOSz+WSnhAdzlMcx3jl5Zfhui4+8YlPdG3E02zkRClVxVEo68myjDxtzhSFIU8YJR2MA9Ax9ltyaJomJsfHMTU9nXhaZBmObePm4iKqtVrX9zODhJUMxnGMmOVHIDGq2Gv64djx4/CDAPPz8y12mmqtTf9vRpZlmIaBUrGIYqGQJMsi+ezq9TrvcyHLMg/fxHEM27a3jCxJQi6bhSTLSfOsWg035ucxf+EC/FZqmIIPHcI4EPQF8xZcungRXr0OJY4xMz2dKAPS8ikmTiNJUoPI0U7itiQdl5Xltu74wPdxa2UFNnW3MrfsWLmMXC633RVPs9QbbvJDiC83jJftCskEJ3UYP4BkpVerwaaqd6qqotRBPji9z9defx0b6+t45uMfT9z2XWgeBc83GNBr0Oq4m1EUBbl8PskpoDX+/ZxxtpLvRhzHCKg88+TkJKanp3li3lqlgoWlJbi0y2E/sGuFVTzE9LGYHnu3seVzORzYvx/nz59va1iwz4V7K9q8TlVV5LJZlKhhDoCXe9q2zfcnSVISiqAVI0BiZOSyWUBKmmy5nodatYqLZ86gsr7e1zkR3HsI40DQM3Ec4/Lly1i7dQtwXRQzGezbu5d3HVQUBfl8nlcfpF3V3SbcdpoAPMmQrbo7GAWbm5tYr1R4XkGxUMBYudyQkNi87QbBJWk4Ggvc+4DWHgZWgiYng2h4zvd9VGs1niyZzWZ5vLjzTgnefe893Lx5Ex996ilMTEx0H2cLD0aUqlQYhF7PoUpj35IkIQgCOHQi62X7LT07KZi4UdQUpjJNEzMzMygXi9AUBSEVU1pdW0O4A6XEdPkj8zBEVBOCGQvNV/fxEyfguS6uX7/efQcp71u7o2ahgmKquZNHryU/CJDLZkGoEcAaakFK+n9kqHfJdV1euXP9yhVcvXSJV64IPnyInANBT4RhiCtXrsDe3IQcx5ienkY+n0/ix9TtXSwUtlznKZgIUTu2xe+RMgq6uJID34ftOPwm5nseDF3n8rvpfWzb35CMgYZ9dEtcBBrCIgrAhZccx0mElySJrwh7naTPX7yISxcv4vHHHsOePXt6HnPz+Q2HlIzYS5hI0zTkczl4VBrZ9byWyo6tkICtKo/UxByRlNpmi9g9K4HM53JYr1Tg2DYsx4HtOEmXyLRy54A09F5Il6xKEl+NFYtFzO3Zg7PnzmH//v29hdTYdZMOTaXeJ9FQUD6X46WPURTBdV3ItJcFiSLYjoNiqiLHNAwQqqtgOQ5kWYaqqqhWKnAcBwePHIEppJc/dAjPgaArYRjiyqVLcKlhMDc7i3w+zyczZhi0khKOuxgG7HUN9OApCIMA1Wo1iclTLQHdMGBmMknSXibTdtUmpX6GQdqV3tUwQIv6fZIoHfp0RWeaJo8V98L169fx/nvv4YH778ehw4f7GnuadP3/TpMRe80fMQwDhmkmglWpc9DTPrAVYoiiKOmI2dT3ot04FFXF1OQkpqanYVCp4tWNDSwsLW2raNkJDV40mr8Q0+/F0aNHUa9Wsbq62t9GU0aC1PQ/Q1VVFAsF5Ki0dBzHXIwqoLkeaTKZDPc41FNS3IHn4fLZs6hWqwMcveBuRhgHgo6EYYhLFy7Aq1YBahiYpgkSx3AchzfJaRUP77U+nLlf0cEY4OOhRsFmtYogCCDRev9yqcSztFVNa1BiTE/GwzQKgO4hhG6EYZhUU8RxckMvlZJEsR4n11vLy3j9jTdw8OBBnDx5so+Bb1/Zs2REJdUxcdTwyhF6/Vg0ibQbLMYfRhHiKOorZyFNxjQxOzODcqHAQw0LS0tYW1/fcVMmTpuQwPjEBDLZLG7cvJkkOg6Y69JcRZFG13UUCwVkTBMybTxm2zYqm5vwmhIPc9lsIidN+5EwojjG1QsXsLa8PND4BHcnwjgQtIUZBmGtBkIINwxAElliJvLTyhXc3GWxHaSHGDLQ2igwDAOlUol3AWQtlPWmpMPmHIhhwDwSg2yXTTphEKBWqyGOIiiyjGI+zxvuyLLMeyy0mzIqlQpefeUVzExP47HHHutZ5AhAY6tlCg8pDLmEsRPsc9c1LTEQJAkWzbZvBSGJsmYYhokYVEonYFDxLFmWUSqXMTczw6sa6paFm4uLqNfrA22zJew6T+kn7Jmbw+LCAg8TpCsi+jIV0lVB23abGNDFQoEn5YZhiBXab4Fdj5Ik8fyD5u6jAHDz2jUs9JIjIbgnEMaBoCW+7+PS+fOJYSBJ2Ds3x8viCCGJfC+SngWt6FYHnl5xdypRC8IQtZRRgJRRkE+53uM4RhAEIEj09xsmjf4OvSskdXMfCEmC53mo1WogAFRNQ6FQaOw7QV/HkxebDCjbsvAPL72EQqGAp556qq+VfqscDyDlObgNxoEkJV0bVVUFIQT1ep2vpmNqEARRhIiGDlomerLtNcXhe0XTNExNTWFychK6roMQgpX1dSzeurWtCddOYOMkAOb27IHjOKhsbja8hpV3cmOhR68C91C0KB2WZRnZTAZTExNcL6FWr6NaqyUlpYRA07TEOCQkaXWOLW+EJElYW17GlQsXtrXZFtx7CONAsA3f93H5/HkE9TqIJGHP7CzvpCgB3OWostVeE52qE9JGAaPV7Zu52zc3N+GljIJyk1HAx0wNA4VOpsNONmRjJ0jyCnaybcdxkhbLksS1F7pO7tQ4YPHjl15+GZqq4plnnul7pd9u7IPKJrejl7JG1l+BwWLkYRQlUsjUQ0DiuKd8DgnJTW0nn082m8XczEyiqCjLCIIAN5eWsLaxMbxQAxKjeGpyEpquY3FxseNre/YqsMob6klo9TpN0zAxPo5cNoswDHnSYrVWg+f73BPo0cqF5lLKWrWKi+fObQtLCO4thHEgaMB1XcyfO4fQtgFqGDAlQZnWpXOvQYueCSzpqhUsYxtAw+ou/eowDFGr1VDZ3OTJjkzwpZVRwPB9n3sN7qQQQvN2avV6YlwRAtM0kaM16P3w/nvvwbEsPPPMMzzMQ5p+2o6hzfNRFPGJb1iyyb2UNabbQQdhCEJIcl1JEkJaez/wviWpo6HaCVmWMVYuY25uDhnDSEIN9TpuLi7C6rHssqf9KArmZmexsLDQ83vSXoUolW/R/LkyQ6nVc6zTKNNAYEanbds8ZNjgPcDW91QC4DkOLp05A6sHMSnB3YkwDgQc27Zx7fx5+I4DSZaxb88eGIbRcINnwiqarkNrsWKNsT1jn99UWk2CdFWSNgpYtnjaKOi0OmbNlEAIF4IZNjs1NmJCkpWZ5wGEIJfLtQ3JdOLmzZu4dOkSTp06hWKxSAcnbdXBtzC6uGhPi6x2RtprMGjsvh1po4VVRLDqAlYdEbMcFYn2jqDaFK7jbGuQ1SsSdu4F0TQNMzMzmJiYgE5DHsurq1haXoY/JNf63j17UKvV4DhO34mVkiQ1GgotDCGu4dH0uWYzGf6cYRhcD4R9No7rwnVdrtDJdUvoe6IowuXz51GtVPo/aMEdjzAOBAASj8H1ixfhuy5kRUlCCTQ5jt1UwjDksddWExshhLciZv+TpiSsZqIoQs2yGoyCdPigo1FAf4Ig4HX1w0qmIxhCbgEljGNUWSIlgAKVvO0X27LwxhtvYO++fTh48GDb10lN405PDlwKmE3YoKWA9Bwyr0GDJwKN9fpcV6DF69i20yV7zAjgwkBN4QY23rTHSaMrW9ZmOBo0kx/YnssxALlcDnNzcygVCtBkGb7nYWFxERtDCDXMzM5CURQsLiwkN+QBjTP2+aYFmPhzAA85MBRFgcmMMM+DrmlcjpnlnVi2jY1Kpa03ihCCa1euwLGsgcYsuHMRxoEAtm1j/sIFBK4LRVUxNzsLvUWXQhZj1A1j2yTMXJ3p/7u5lV3Pw+bmJoImo6DQg6eACdxIAK+N14bgNWhwnw44IaUJaUJlSCsS8ikFu77GFcd47fXXoalq35UJWxshPPlTSj0GQho8B9smAvo/i3ezSZ/JB/MfljiX/gG65lPwMrymxzOZDB+PvYPJp10Wf7/IsoyxsTHMzM4iY5pQJQmbtRoWlpa26Qb0g6qqmJqawsLiYkOuTKuKkl5pUGpsMl5YwiKQhBcUReEVSCxpsZjPo5DLAUhyZDZTzcuaRxVHEeYvXeLfY8G9gTAOPuTYtp18sR0HiqJg7+wsL6drhrl3m0sFgS3DgCccdvIWxDGqtRrqloWYEKia1pNRkN5+uvwxYCWMOzQO0jHVYTjWPapxH8UxNCpKM6ib+8yZM1hdXcWTTz018HF2mhyZe38UzZaSnbffO58Mm2PmksRzMsIgGKgPArBl5PXbuKkdhq5jZmYG4+PjMGjW/9KtW9hoqjjohz179mBtbY175piaYj8hnk65Psxz02z4SpLEvYC+73ONCUVRUCgUkM/lAElC4Puo1evbqjbYAiAMAsxfutS2BFVw9yGMgw8xtm3j0oULiJhhsGcP1DaGASGEly815xowtzNpNgha3KyYt8CnWgW5TCZx1XZrK9ymNDEIw2Rik6SWORC9wMMfGI5RACRhmnqthpjqLhTy+Yas/H5YWVnB2TNn8ODJk5gYHx94TO1cw3FqdTkK8aNuGgT8uRbjUxQFWSoKZdP4d7+kV+DDzKbI5/OYnZlBPpuFpiioVKtYWl4eqE/D3NwcQAiWlpa2PdeL54OpRHYj7U1g17uqaUlukSTBdpyG68Q0DORpgighBJZtJ90dU69hBrtjWbhx5Uqvhyy4wxHGwYeUer2OSxcugHgeNFXFvj17Oq7amddAkuVt6oNxFCVyx2hyxaduylEco5b2FtBOgywJimeWN8EbI7WZXNhKRte0gW783NMxhBACw6KligC9uVLDYJDx+Z6H1157DZPT0zh+/PiOxtXuHPI2zbuojNgACyu0+QwMw4BO2xKz66evzTfta1gNtoBEE2JychJjY2PQJAm+52FxcbFvL4dpmhgbH+9YtdDJ89FXy3NseRNCKiSVMU1ItGIhHSJRqOooa8UOJCWOtXp9K8xAk4olSUKlUsHijRs9j0Nw5yKMgw8hvu/jyqVLiH0fuq4nHoMuq27WyS29wo9Z3D99s21xg3Jpx0SvyVuQLplrXl029CvocNNjyYgDxfFT+x4GrFSRTQyZTKahVLFv84MQvPHmm4iiCE888cTAnodkU+33PvKQAnrzHHQaYzaTgawoILTcbggDamuQDkKhUMDMzAw0XUdMCBaXl7HZZz+CvXv3Ynl5uaMHoF2/iEGTIiVankwIQYYmybqet62sNYrjxNDN5SDJcpJI3CbMsHrrFtZWVgYaj+DOQRgHHzLiOMbVq1dBfB+mqmLf3FxPk0KQCilwuVV0j2PXarVE7a6Ft6CZ5km0mzs6iiKEYZjoG/RpHAzbMGClir7vA4Qgn88js8NOdpcuXcLCwgKeeOKJHW2rmxhRuqfC7aAX40mSZW5o+UEwtOZIsiwPzUAwDAOzU1PIZzLQZBnrlQqWV1Z6nrj37NmDKIqw3EMPA57T04O2Ra9wdUQkBgIIgaIoiTeJEERRBE3TUKS5Qa3CDOz7tHD9OmqiWdNdjTAOPmTcunULXrUKmRDMTE315EZmLYUJktUlk/LttJJt9hZkW3gL2u2rm1HAYKsWTdN6TjYbdtIhkGgEpOWdC4VC66TBPm7glUoF7733Ho4eOYLZ2dkdj7HTsbJcklHLJrf7THlCYpdJVFVVXnpnWVbP+QesjK8dbPIbBgqtPCiVStBkGY7rYmFpqSf55Xw+j2Kx2FUtkSEhMZqGZdwA4Net53kIqG4CWzyw3huyLCOfy7UNMwAACMH1y5fhDFEwSrC7COPgQ0S9Xsf60hJIFGFyYgJ6j7X2YRgCNIFJ13VuFLRaEbXzFmTbeAsYrPSxn5g3W6W3qp5ot49hE1DxpiiKoChKIrk7QIgjTRSGeO2730WhWMRDDz88pJF22N+QlRHb0e7T7ydWbhoG775p23bPn2m3fUiyPNTro1QqYXp6GrqqIo4iLNy6hVoPTZzm5uawuLjY84TPdCVkSRqKgaOpKmQabgmpYqVCz02QmvxZk6ZOYQYmkjSUMJBg1xHGwYeEMAyxOD8PEkXIZbNb6nodYDcoAgAtEhGbb0aDeAuYy5ut5JsbDLWDye3GPeYbDEvQKI0fBKhVq7xUsbSDUsU0b7/zDmzbxpNPPrnjBMFuLuco1dRnN5IR26pkorvngL0/l8tBVhREUQTHcXrdcdeXDKvUkWHSdtAZ04QiSVhdX8fK2lrHMMPc3Bz8IEClB9VB1qGSjVqW5R2rWzK1REhSQ1klqI5F+noB0DrMkKp4CMMQF8+ehT3M7paCXUEYBx8SFufnEXkeFClp9tKJ5kRAFotuKF9K/b0Tb0Fa9IXRyw0unWHfKVbO+yKQnTVLaiYIw6RUEYk3pVAodE0Y7GU1eP36dczPz+PUY4+hUCgMZawd8zZYSGEEssm9ws5br+teWZaRo+WNruv2JGPc67EN+wywMEORNnGyLKuj9PJYuQyZZv13o13fiJ1+jrqm8RwD5j1Ib5cpMDK2hRk8j4cZZCmRd75w9uxw218LRo4wDu5xCCFYu3EDbr2OIIowMz3ddnWbjsenbwjN2eQk9ffAuQWpfTXDPAidYPHmbvLK7faxE1gfCGYY5HtsntTNOLAsC2+++SYO7N+P+/bvH9JoO8OMrFGHFBjtPm+gv7CPpmkwWW3+DuSVtw9m+N08WROnyclJ6KqaePHaNHCSFQWFfL6rccBUKpthHridVLbIsgyDKqR6tKEZsCV4xCZ81qyLlVG2CzOwhm2XzpxBXTRqumsQxsE9TBzHsDc2sLG6Ci8IMF4uc+s+TVoyGNiqmeYa/c0lhtS9OJC3oCmM0I5utehRD8YBuuxjEMIoQpUes65pPRsGIKSjazuOY7z66qswDAOPPvrowPr6DbtEd4OE5RsMo/9ArzSfr15KGVthZjLQVBUESd+JjtUO/Q1wJF6UbDaLmZkZmHTiXV5dbdkGemx8vKNxwLRFOiHRPIRBj4L1/vBp2+btO9gqBeUiWjTE1yrMIMsyojjGpbNnUduBkqRg9xDGwT1KHMcI63WsLC4ijCIYuo6xsbGG16Rvxmyy5j0RUjfHdCw6jmOucjiQt6CPG1an1Q/PnG7aZ4M+Qo/76ZWQGkQxzTHI5/M9TyLdpr3Tp0+jsrmJjz75JNQdJjSm6Ta+21XG2Hzt0Qf7MhAkSUI2l+PyvV4H4aFB/ArD6MfQjKZpmJ6eRiGXgybLqNVqiapiKsxQKpVQrVY7NjvqlU4CY51QFCUJNckyLxtNX0vsL3aOWP5DHMeQJGlbmKFeryfqjGGIS+fOoSoMhDseYRzcg8RxjMiysL6yklj+hGB6aqrhNSS1kk3fnNuVETKxlGqthlqf3gJgMBe/hNZJcoTGQ2NCGgySfo2PfojimCcfqqqayCH3sbrspMB469YtnD93Dg+ePImxHcgjb6PLhMDOIzD6MsY0zddYgxE4wCSWS8srD6mNMkMeYExdtynLmJiYwPj4OFRVRRgEDc2byuUyYmqINtOqkVI3JAxm/KW9B50UGNNGFPMkANgmmuTYNvwwTEIM585hU7R6vqPZvTuCYFdghoFVraJWq8GPIkxPTPCM/vSXnMd6WRihy0q9blnIZjJQVBUZ00wkV7sZBdQIGXTCZgZC+obI+tbLkrSVLMkkXAfcTydiQpJyRVrzPUifhHbTi+95eP211zA9PY3jx47teKw8LySOEW89uOVRSa3OwzBMelwgUZr0aYJbQ5UKfY/tOCBIOvRpqY6dErD1+bYIRaUNAX6tsN/s2mgS9On3M9QNA0EYwvc81G0bxUJhW87KwNcFW3nT/h3DJJ/PQ9P1pOFSEGBpeRnlUgnlUgmSJGGzUmmoKopoJ8xB6Td8w8sa4xhhGHbUqWj2TnCFRVVFMZ+HZdsIwxCe5yGKY2QMA5fPn8ehY8dQbvJoCu4MhHFwDxHHMaJ6HYHrYn19HUEYopjPI5/P89c036ib8wtaYTsOnFR5UqlQ6BrrB4a3kmcGAqFd5dLthdl+htkbIQ0zDFjL5WKhMFjJX5s69Hffew8xIXjiiSdaTj4s4Yv/pvFm3uyKZo43lyw2TADN+Q70f592OlQUpWMtOsHW6tGjN/hth4emhNbU30CjcSQBiJEYMOy6S2eyq6qa9HmgBivTv5BkOelU2KJkL5vJIAxDRFEE13V5p8FhIUkSYuzAyGiDoeuYnZnB2vo6HNtGpVqF53nImCYqm5tgaanxDg0DIDV26gXsqv0gJT0VapbFG6V1eUNDMjOQGDSsusTzPDieB891ISHxLDADYageM8FQEMbBPQIzDOIg4K1fVVXF+Pg4TwBshgAdGwIRQmBZFlzPS27YigLTNHs3DNrsdxAkAJBlgMYtgSTfoN2xDQPWKyEMQ8iyjMKghgHQYBiwiX5lZQVX5+fx8COPIIpjWLbdMOGnvSWtJll27PxmnDIC+AqcTbDs73QuSRBAURQYmpaUr6U8AHyf9D3MIDA1DYZpbh1P2tOQyvdIj4l5LJpfwyYp0FVnRIV3eiFtKDBDQpZlOJ4Hn35emqYlRgU7li5JoZ13mLRQ7mVS7RdZljE1OYlqtYpKpQLP86BlMljf2ADQ2DVzGLAwQy8NrBTaz4Kk9BQ6bReghhQLUWJLv8I0TciKglqtBtd1k34OpokrFy6AHD2K8YmJHRyVYNgI4+AegCUfIgxRrdXgeh4iQjA3Pd0+65+Q9s/Rbdbqdd7fvVgqwXEcLjzUrtRwp2GETjADIaRhBVVVR28Y0OMv5PM9Cxyxag6WgBVRt2w6XkziGG+9/TYKxSLGJyYSLXu0mPABHvKR07/pZCinVtbstUBj+KgdNUmCoqrIZbM8vtwOg8rq6oaBTIuKl0FgRhCQlMeFUYRcJgNZVfmExLwiMfubifAQkqzkmybNKAwRBAECz+PJijwpD8mkKCsK/y2zkEaPyGziG0E1Q7FYhGEYWF1dRSGXw42bN7G5uYlcLjf0fQHgSoidTIQwiqDSfip+EPSs/snCDOmzxBJ5M9lskn9AEx0zpon5ixdB4hgTTblRgtuHMA7ucuI4RlirAVGUVBFUKvDCEJPj49B1na/MGL3kF0RRxF3pkCQU8nlomsbLmnzfh9liMhmlYdCwnzgG4nhktfkEgJUyjAptwigxrfWOoiiZ+MMQYRv3bxSGPC8CkoTr16+jXq/j6Y99DLqu8xVu84TfLeTTbvy9vIMLSe1iGWMD9Fphx61Qg6/bBMS8KiyvIm1I5GgFALtOVVXdcnfHMbb5JWQZiiQlBgNtMsT7h7T6jkgSr9sfhYFgGAZmZ2ZQrVYxf/UqFm7dwszUFIpDEsRqhi0QojbHEwRB0myNEAS+j9g0e/KetcpDAJJ7hKHrIHGcCFilDISrly+DEILJ6elhHJpghwjj4C4mbRhEcYz1tTUEUYR8JoNSqcS9AwxCSMcwApAkqVVpuZ5MY+yqqiKi5ZC247Q0DkZZKZAmog2gZNZBbgS5BpZlwQsCgBBeruj7PvcCRNQoaLlvpuMgSVAVha9SgS0XuOu6uHLlCo4dPYo9e/YMffy9nJN02GKUrZo7wa/NdK5ED++T6WSONuM2dB22bScldTTfJqafWxiGiKMIEdMKiCJEkoQojhNjMO2xoZ/dNm8DzX8gIzIQJFnG/n378O6778J1HGxsboIQglIPkueD0qo7ZUjvKywHJIrj5Lvfj+eoRR4CAKiaBgOA6zjwqdcsY5q4Rg2EqZmZHR6RYKcI4+AuJW0YAMDGxgZc34ckJfLILOmL0Yth4HkeapYF0BLBAu0VwL7YOjUO0qGFUWkKtIKgsUkQu6G1k5HtBzbp1+t12LaNOAxhZjJtJV+ZB4bJNyspQ6CVFHGU8ia899570DQNDzzwwI7GvBPilL7BsHsKDMIwR2DoOjxqzHmeh2w2m9TtEwIt5QFiSZ1xHCOOIoT0N3tMiiKQKEq8DelzJElQWIiHXocq/XsnxgK7luM4hpHJwDQMxGEIRZZRqVYRE4KxUmng7XeCr/SxNZEHqa6nmqrCchx4vg+DKlP2ut3kj8Ywg0S3SwiB67o8rJYxTVy/cgVxHGNmbm5YhycYAGEc3IXEUZTkGNAbfBAESQvbOMaemZmtmvVUUlg3xUHHdWFZFoDkS1vI57n7kN0sVEVJ6rLpTZetIHbLMAC2xI/YarfZM9LrtqIwREh/Alp77XkeXFprnslkuPHR7AVQmjwC/bC0tIQbN27go08+uePuja3o9Rwwr8FuNFvqBDdiWa7EMLYpSciaJmo0mVbX9ZZhIZaYp8gyoKpI+8JYVUiUMh54zkgcI5IkII5BgqBBalxRVSiKwr8rvZ7fdEUKG1upXIbruigVi9ikpclxHGNihKV/LAmVEAKfhtWYcSC7Lm941msn1FbblQDuedF1HQSA57o86TVjmrh59SpACGZG4VkT9IQwDu4yePJhStK0srmJKI6RzWSQzWYBbN1smAu0HUzilE2KpmEgl5IEbi6RMzStwTjYTcMAoGEF0ih+1FAn3yreH8cIaRMZVu7GvA2E3uR9309i1IqCXD6frDbbeAEGOgb6ebzz9tuYnprC3n37drzNndBcDnrbkKSkKVaLEMNO0DSNexBsx0m0KdC78SFLEmRV3XaDbOVtiFLJpiQIErVGej3KsswNBWY4NF9P7aoRyuUyrl27hlKxCEmSUNnchGXbIHGM8bGxkRl2EsAbLkkAb5GtaRo8z0M4gHHAtsvyEGRseTYNXQfiGK7vNxoI164BgDAQbhPCOLjLiCyLGwYEiWysY9sI4xiz5TJ/HSEkcXN22BahNfxshZDNZrfXhzfdyFhoIYqixEDokuW+E9IqjgxWqdBK0Y8ZCUEYIgwChFGEIAi2brxstYdkxaxqGlRF4brwuqYhm832F1PtEUmScP7cOViWhaefeWYksep+plXuObjdxgGavD9D3K6ZySAIAkRhCN/3odGKi53QzttAAN7FMArDxBiNYxAapmDfsbR3gRmf7SiVy3DOnYPn+1zYab1Sge26iNfXMTk+PhIDgZBE9EoCoKVCCKqqwvP9natQsiTblEFkUEE1p9mDcP06MrkciiMKpwjaI4yDu4jIcUDoTYZlpFdrNYRxjKxpbk1qpLvK3LaKhFyuZTlbeiXODI5sNgubNlTRaS35sGllGLByNiAlgETdnMwrEEYRNwDSblpFUaCpKlT6w97veV4iACRJMNPncMjU63WcPXsWx0+cGFor5m30seq+U8IK2xhigqlCP1PbceC4bk/6HIPCjAZZ1wFqhJA43vIsRBFCls/g+/Cx9RmwPgY8FEGN+nK5DEgSqpubmJqaSpJjZTkRTPI8rKytYWpiYuifoUvDB7IsI0snbZLK12B5GYPul3lwmj05eioHIW0gXL14EScefhj6EIw7Qe8I4+AuIQ4CRI4DYOsLFQQBrHo98RqwOCQrJ2xRRsRIVyRItCJBa3PjTAvcsMxj0zTh0xVE3bZRTCkwDgNW+dBMRFdlERVnYpnnafVBlnippV25LRT1AMCnuRqQJBiGMXRVvTRvv/02zEwG999//8j20Q+73aq5IyNMiDR0nV+rruuOzPgDtpfuSbIMTZb5d4tQkaeAerUAbJXCRhH81HaYZ0FVFGxsbGCK1v/nsllIANbX1+H5PpZXVzE9OTk0AyGkJdFAojrJko6590BRECK5h+xosmb3k7TOgiwnn48kNSQpAsD8xYs4fvLk4PsT9I0wDu4C4jhGSJMFGZIkbfcapCbwtlr+vo9avZ645hUFxXy+bdMdnrlM3fXpTONcLofNahWB7ycZzEOy6reNmyZG+UGAWq0G27aT1X+q9EqhhgCP7baa8AhpUIQLwhD1Wg0EyYolR3M1RsHNmzexdOsWnnn66ZHF+NOtsLvBPCosBnwnMOycg/R2s5kMqvU6PN+HoqptDeFh7Q/YnhjKuhbKsgzDMHg4Iq2VwfJiSBwjJAQhku/Z6toa9tTrUDUNGg19ybKM1dVVBEGAWysrmJma2rGBQAjhMtq6rvOE2XTFgaqqSbhup8ZB6rprMKoIgUH3ywwEdiYXb9zA3G3O1fkwIYyDu4DIsrirHEi+TIHvN3oNehAgcl0X9TYVCW2RUlK6KVRFQSaTgWPbsGw7kard4UTDji+mCYJBECAIgm2VCrqucxlnlbaV7YqUyN8SJCVaddpZ0tD1ht4TwyaMIrz1zjvYMzuLuRGXZvV69tPiR3eMccAqY0awbVVVYRgGXNeF4zhQC4WRJdI2G+bpLoWt9slEr9KVK2EU8byFbDaLum0nhgPtGyFLElRNw9jYGDYqFYS0adP05OSOQieu5yGKIkiy3FIFk5UfujQpcSekJbRZNRWhCcKspwOAhhDD4s2byBUKIv9gl7jDAo6CZiLHQez7/EvDbuYNXgPD2G4YNK1c6pbFDQPDMHpqINTppgYk8UCmvW41eTb6JQhD2LaNzWoVG5UKLNuGT8vEZOr2z2WzKBYKKBYKyGQy0DStv+6I9EZkOw5iQqCr6kgNAwA4e+YMfN/HI6dOjXQ//bjmeaz7Dso3YIbbKEStgORalSUJcRRx0Z1RwTwyEY3N94uqKPx6z+fzCKnomCLLvKTS930EYQjTMHhy8K2VlYEn7SgdTuiggqhR9UjChMAGpOFqTRkIAJJrgBrupmnyPATbcXD14kWuqigYLcJzcAcTBwFCx9m2ugt8n+sazNCkpW1TAyvtIwR16lIF0FDu2Al2o+405UgA8rkcNms1+H2GFwiSnImAlhBGaZlnWo1gaBo0Xedx8QpN7BrYQ0EI6pbFVd8KhUJSbz0EEaVW1Go1XLhwAffff//I9PE5fYyfCTLdCZUKHGmr/8EokCUJGSpqZTsOVE0bunHEwnDMRS5LEqIekoM7YZomPKrVoOv6Vt4CrciRVRXFYhEblQpq9Nhmp6eT1uq99gIhBJbjAIRA07SO4QKm+RHQcQwtTJbyULKFEJNaBmiIgXZzvHrpEo7dRgGxDwvCOLhDieMYgWU1dMljVKl2fNY0kclkOjZPqtZqfDVRyOWSjnpd6FVIB0hctqZpwnUc1C0rcZG2cW2ykq6A5hAgdSOFJEFXVWg01tnqxk1oyWFf3oIUjusmrWeBhpAKjxMDvJvcMHjzrbeQy+Vw/NixIW1xOLAV353kOQBGL6Zl6DpcTUNAW1UPK8+EGQXb+i2wUNYODE/TNHkljprSG9A0DSSTSRpNhSEkSeLy6QtLSxgrlXjoTde0tnodTOckotvoJSlXo0JoYRRBJ4M1oep6TpjB3hRicBwHy7duIV8oiPyDESOMgzuUyLKSjnNtvAZBHGN6bKztDTUKQ2xSI0KSpKQioQfhkkFuZNlMBlEUIfB9VGu1huqHMIoSg6BFfbQkSXxFxIRWOhHTpLtBEq8C34dj2yAA8q06LFLvi8Jinzv0JFy/fh2rq6v45Cc+AUVRGuSTbzd3ZBkj/eyH2Zq41T4ymQxCqnvArrtBYJ61dJJrq+u3XYJir5iGAQlJyW1zPoEE8NLcjGkin8th6dYtuL6PtUolMRB0PQmjUKNdo42t2Mrcsu2kp4QkIUcTHbuhahok102+z0z1cFi5K+nE51SiYrOBcOXSJZF/MGKEcXAHEjkO4iBo+YVLew2ybbwAURRxw6BbRUKadMJjK6XBdrCVeK1eR+D7qFQq0A2D10OnURQFmq5vrWZ63Eeccv33G1aI4hh1ahiYptk1yzqd20HoBNDPHoMgwLvvvov9+/Zhanp6ZHF0Rj9bJ4RseQ7uoLAC85CNMkGSVeiYhgGHxrCLfSYnNocORo1hmiC0tK9baErTNOyZm8Pq2ho8z0OdVvYoVOiL+D4Cmr+kKsqWCqIsI5/N9iznrcgyIMsAVYlUadfGoX9yKe8B0Ggg2I6DC2fO4OHHHxf6ByPiDlo6CACqZ+C6LW+S6VyD8Tb66lEcY7NWS+L2ioJSsdibYUC/iGy/8QA3P13T4FLvwdraGk8cYuVX5VIJpVKJJzL2czPhyZGy3F9dPCGw6nXEcQxV05DrU8tAkpImO3IfE9cHH3yAMAzx8COP9LWvQeg3iY+fR0m6ozwH6XK5Ue/DoMl9JI7hUdnwdvCQAS1FTAtr9bzfHRg9TKLc7TGJUlEUTE5MJEawpqFuWUl+TS6XTKKyjDiOUavXUa3XUbftRLypj2uB5R1IksS9gekmbL3Q9RxKEtBCn8SgCwsgkY2/fP58H3sV9MOdc3cQtNQzSMO8BhnTbFlqFFM5ZELrqculUk9feuYWHMQFGkYRLNtOKgwsCwZtpcy6ORYKBRQKBZimCTnV4bHfWyVJyR73g2XbPCabz+UGm3yYzgOdUGV202qxrY1KBZcuX8bJBx/kgjsjXWX2uW0W3riTvAYNjPBcSSwrXpZ5ro7juttCPmmDgKly7vQzZNdOv9vRqAJpNyMmjaIomJqcRIYaQWtUMCmTyaCQzyfhCVYySKsdqrUaajRxuZcxaqoKCUmVEaPnJlPoPXzEPrM0Jq2mIIRgaWkJN69f72lbgv4QYYU7iMiy2vaIZ16DKI4x08JrQJBkx7M65WKx2LM13++qhiCJgXpN9c6SLMPUdZRLJVi2jTAMUavXUSwUoKrqVgXEABM0y/ruJ6SQ7rLYk6ZDH6S7XJJUGObtt99GuVjE0SNHhravLgPpz3NwhyYjjtJj0Go/mq5DDQKEvg/HcbjLftQhA5mW/vaKJEkwMxl+HffzvsmJiURq2XWxtrHBJY/DKIJpGMiUy5AlCR4ti2SJj04qF6idgqaqqokBRXudcMNLkrqGGFpJo7d9rZQ0wIpTZZMscbJOVVLnL19GoVgU+QdD5g67Q3x4CW27bZ4BAFSrVcRxDLOF14AAqNXrPEGomM+3lQxufGPrm2C7m1cQhqhbFvcShGHIy5/y+TzKpVLSzZCWCaqqChCCaq2W9IZvY/j0AvMc9FqpEIYhbMsCAZDJZqGOoD0yAJ5AJcsybty4gbX1dZx67LFdmewGmciiOzEZEaOvVNja0Va5rGkY3NB1PW+gkEHfu0+PoUdM0+zbOEh2I2FifDwpawSwsLSEyuYmJCRN1gxaGZTP5VAqFGAYBiRZRkQIPM9DrVZDtVaD53nbzouiKPwaak407mbA93uG2f1ISo1BURR+H3QcBxfOnhX6B0PmzrpDfEiJOuQZADTT3nEQRlHLXAPLspLJF9ialCltv6ZtrHcmw8uICYHjuqhsbqJarSY3CroCyWQyKJfLKBQK0HW9YfySJPGxEEJQrdd7jpu2gnVO7MVzQOIYdctKhI50vWUIZtgQQnDm9Gns27MHU5OTSY4C0BB+GMXE0++keicmIzYzko6V7LomSVvvmBAossyv237c9jul34Ra0zAG/u5IkoSxcjkJhSEJTQLY1nJZpn0NioUC8tSYZp0mbcfBJm0XnTYEWGllK+ElSZKSduhNELRffHQ4iMQQaTJo0xLPlUpF5B8MGRFWuM1EUZSULba5YUgA757YymtgOw6XF83n89C7JB9yF2CXG1RAG9WkrXFCSKILbxg9ZTZLkoR8Pg+rXocfhrAdB0EQIJfL9e3Wjnv1HDChoyiCrChJnsEucPXqVdQsC0997GP8sWZjicdPabiHdHG/dqXPkAJwh5YxNsPK43awCYIWyZrUMAC2jComMhTR9t69ZuzvlH4EkkzTxOra2kD7CYIANg2bxHGMkOYXmJkMzDZZ/kxHgemS+L7PJc39IOBGFfsOB2GIVmm+XPUyRfPio1fSCdNpIztjmlyJcmlxEYVSCXv37x9gD4Jm7uA7xIeD2LY73uBjksj9hlGEsXK54TnX8+DQTo056iZsJj1BdbvhkjiG4zjcS8AMA0WWkc1mMT42hnw+3/MNlHVzKxQKXJUxCMNk27T1dK/wVs1dJrVmoaPd6B0QxzHOnD2LfXv3otQt7iltKcGlKyDS7uaePQz9GgZkS+f/jlJHbEGvnxozAmLQ46NVBW1VL5uuB1mSEi0BWR7IdT8wtAKmF0zT7NuzQZC42y3bBiEEqqJgbnYW+VwOiiRhbW2tq9SyLMswqdR6PpeDSg2GMIrgOE7SCttx4FHjofVhNi1EBvWeSRJfGKSTFNPCTWEUYf7SJVQrlcH2IWhAGAe3kdC2Qdp9QelE7jgOryVOq5d5NEERQLIK6OA6l9DZY0DiGLZlYX1jg6+6gaRsqFgsolQuJyVVfaw2uaVP/88YRlJWKctJVUW9Dstxep4I0yV47eBCR4Qgm8vtmuv86vw8bNvGAwO2lGWfCwtDcKOhqQSONK2C+4UldcmyvOMmWUOneTxN/7PqAZZnETf9nz4f/RqEBi0XjKJoV+PWvZY4GoaRhPN63G4URQ2S6aZhIEerFMbHxhJ1xTjG8upqz1UDqqoiR8uRM/ReQCQJcRzDdV1UqtVETKnVcdJjZQqkg5J+pyTL/DNvyD9wXZw/c2bHjaEEwji4bURRhLhDHJFNDrZtIyKkwTBgiYFAcuPoVrvfTsEsbRQ4rguC5IuWzWYxVi7zG0q/tCtXVKnuQlrMZLNW60k9kEknt3OHNwsdDauFdDe412DfPhQLheFsNO1NwNYkwkooeQ8C5mmgr2Ohik5jBe68kELDBJ9KsoxobkCU6kjKwwVDpMF7MOKmTIPAmg/10jDKpx1HoyiCLMvI53JcKwFIvt8TExNQVRVRFGFlba0vVUpW/lgsFFDI56EbBgghSadT206E0Dp4BXf02TUbganrOK12uVmt4sb8/E72JIAwDm4bhIYDWsFczEykJYoiXmoVhmGSVESrBLo29KETanqibmUUyFRJsUQ12QftX9BwDK0ep3oD+VyOr9Y2Nzf5Kqf1IRA+8bVc8e5Q6GgnXL5yBY7j4OQuN4KRgC3XauqxlkYg/R2lPAfNz/OfPld2zRN7eoJPV1OkJ/dWzzePFalj2Y3QkJHu2niHeQ9Y11Wvg3HApJBtxwFBokNQaGPca6qKyfFxKJIE3/exsbk50NhVRUE+m00WEYoCxDHCMGxrJJA43nGOTfN9KX0/yGQyXP/g2tWrPOQqGAxhHNwGojBE1OYGlI49u9QwYJnEURyjWq8DhGx1Fey2MxZKoMaGbdvbjIICLUMcRjJWr4lkLGShKAoIkpbStXq9pReBuY+lFjcHYEhCRwMQRRHOnT2L++67b+Stn9MQQtBPvjc7GzE9h83hFin90+SNSO9z20/qsyJNP83vRcqI6NX82A2jgMHagktSIlW8W/LIQHeBJMM0AULaTnZ+EPDJWEKSpJfL5TqeP13XMT4+DgVJtdNmtTrQ2FmIStd1FAsFGJqWGAlUHK1WryOgMs0sB2mYpLeZzj/wg0BUL+wQYRzcBuI2ZYvNNdCO4yAmhCfzVatVXkbYk2FAtxfHMWyaaOjQlUXaKBiWNnm/GeaqoqBE1ROB5AtdoSVT6dgkW3G0Mgy40BEhKORyu+oyv3zpElzPw/33379r+wQaBZj6gbmPe6kUad6+3Gqfd1reQg90GrFhGJCpvHAn1/ioaJckappmUm7Z5DlgImO2bSOOYyiyjHw+D8MwetpfNpNBqVSCKsvYrFZh2Xb/Y6ax/yhV3lwsFKCrKgg1Eur1Ojar1Qap5YGvnFaJpk36B+x+sriwgI319UH39KFHGAe7TBQEiHt0WzJtg2wmg1qtlrSETakfdiOmnoKNSgVuD0bBTtZKbbPDuyBJEnKZDEopfQbX81CpVBLvBiGI6Habj3nXhI5aEIYhzp07h0MHD3YP7QyZQT+nuE1Y4U6g1dXcXLY2DDptj+UeyLfBewC0F0hSFAW6rvNqiiiKYFkWTx6WJAmmaSJfKPSdhFsoFHhp8dr6Otw+QyrsWiIpASlZlpFNGQlRykhgqoatZJF7gbTyHjZVfhi6ziWiz589u+uf473CnXeXuMeJHGe716BFJQELKQC050KT+mEnuFGwuZnEIAlJjIJcDmPl8jYBlJ3Cvno7cRmqqooSTXJSZBkEiYZDpVqFR0Mg6ZvC7RA6SnPp0iUEQYATJ07s6n6BAURkgIZM8TvROGjH0EMLXbZnGAYk2sXwtngP2ozPoJ0kHcfhrnqJPl4sFHhr50Eol0owDaPnEseG8aaUWJsTG1k4tJDLJforhCAMAm4kDNqeu9VU3xyyyFBvS21zE9dEcuJA3D13iXuAyPeBlEY4o9UN0HGcxFWHLXnSYpP6YTNsQt3Y3ITtulxLvZDLJZ4CGlNtl2w4iIXdXLK4U3RNQ6lYRDabTRLEqBFQr9e39NVvk9ARIwwCnDt3DgcPHeIhn44MceUysIhM6kZ8RxsHTedqN/MO2P5MGl64Hd4DYHuHQ0JzjJimAJB8TwqFAp8Ed4IkSVsljlHUV4kjAK5e2PwegsRbxcINhUIhWZikjASbybD3Q4exsTPBvBeQJFyjpcaC/riD7xL3FnEcI2qRUNTOlejYNhzX5e2W8/k8L9VphiCp713f2EhET5hRwMIHPcYg+2XQDovdkCQJGcPYqqkmJKndpkmLNdvedaGjNBcuXEAcRbueawAMnm9wp/ZU2G160hXQdZ570K9Y11CgbnJCkh4H1WqVV5Io1BjOZrND/Sx5iaOm9V3iyPIOml8fx3HDvS1tJGjMSAhDWJaVNJVrsXBqs8O2T6XDFaqqwqQaEfNXrgjtgz75cN8pdhFCGw/1gk+VDz3Pg5HJtFU/BBKvwubmJmzqplMUhTdBYtnXzbQrnxpklTTKaZllH+dLJR4KcRwHq6ursB0HumHseo8A3/dx/sIFHDpypKPw1KgYdCV7J+cbdIIJeO3qPmnlAmuVvNv7Z1LF9Xo9SSCmJbwKzRcaRHukFwYtcWRhzrRx0KkpGFvVNxsJ9VqtZ29NOyOvOfxoGAZURcHy0hLW1tZE/kEf3F13iruUOI4RNcufMld8i4t8s1qF47owDIOLmDRDkJTwVapVhFEEIkncU9DOKEjTcgXaxwp8x30B+kCRJGQyGeRyOYRBwNtas1WV5/tDdd134sKFC4gBHD9+vOf3tGpAMygDJyMyNbm7zDhoVoncMT1eJ4auJ+2VCdk13YMoimDbNqq1WlKpFMeALMPMZEZmEDQzSImjLMu8KgpIrtGoh/PcYCTQc+15Hk9a7ES3rTfkH2QyiMIQSwsLqA5YsvlhRDRe2gXiFpNXOynjMAxRqVQQxjEmx8ZaCvoEYZiooFHVQE3Xkc9m+9bLZxKk/VYa7KZhwPcpSYjCELppQjcMGLqOIAgQRhFCy4JNY8VsxTcKPN/HhYsXcfTw4USYZpdhq7GByhip52Cn4la3g1FULfSyT8Mw4EQRPM/b1nV0WBBCENDmRukJUZZlGKYJjXY1lWR5oETUQchmMohKJWxWq9isVrl0cjvY9431tIj6vJ8osoxcLgctCJIyZhpq0HWdl3HuBBbOWF9ZwdTMDLLZ7K412LqbEcbBLkCalc0IadvjoLq5iTAMIcsypicnG56PCYFt27ykSaJfKkPXd1RzLqH3FSk3JHYxzs8Sm3zPg2EY3B1JCIHrefA8D3EcJ9ncrgtN02D22DmyH86fOwcAOHbsWJ8HMLyJbdCzzj0Hd3jDpTvJ6WtoGjxFQRSG8H2/Z/2AXuBdDpuaFmmaBt0wGvKLCEnaSw+a3T8IhUIhKT+0LKytr0OhlQetSBsHOxmjrmlQC4Wkeys952EYItPGc9LNaJQliV/3mqoikGVU1tdhmibGx8cHHueHBWEcjJjI8xDTWmQASTihzeqNdTojcYxSsdjwhfCDAHXL4qsHwzCQzWR23F2P6/T3MIENo2RxEJiyoyLLDe2iJUlCxjSRMQzeYjoIQwRBgCAIoCgKDF3vKczSDdd1cenSJRw7dqzvSaIf46sTO9kGl06+S4WLmLrjzjfWe309q1ywwxDukLwHIZ30/CDYkr+WZeia1tnrlZrodotyqYSQfq/WNjYwMzXVcpJOJyQyMaRBkWUZuWwWfhDAoRVXlmVBo+XKbRuRtYCg8btnGAZWlpdRGhuD7/tDE3+7VxHGwQhhFQrpC7rdzSXwfXi+D891oeo6L8+LCYFlWfB9n+sV5Fnd8JCQJCmR4+3gERhVZUIvOJ6HKIp4U6htSBLvQR9RN7Dn+0kMl7aV1Q0DhqZxcZR+OX/+PGRZ7t9rcAeQ7plwp3sOWjLsvIM+0DUNrqoiCkN4njdQEmocx8lKmF7HDEVRuLHb6fgkSYKqKAh2secD2+/42BhWVlfhBwGWV1cxOz29bfJnIycAL5/e6X51TYOqKHA9D77vI2BeBNPc8gj2KaSkKAok38fa6ioMw8Bkk2dW0IgwDkYIaco1aJeAyFbGhHafkyQJ+Xw+acvMHicEpmkim82O5EbJdPVJCwPhdhoGYRTBoyWgvRw7MyAymUxibNEbskfDD+zGw7u49XAuHcfB5UuXcOL++wcKVQzFa7CDfIN0rLqXa4fQ90RxjDiKEq0JWifu0Zu1oiiJDO6wchh6SKAdSt5Bn9tg3imrXue5B71MfkxEyadGamqD/PrrJ8mwWftgt2AljiurqwjCECtra5iamNg6B4Rwrw7z8AwLSZYTY0BVE7VUep/UNA2mafKGch33SMfFrh1D17G6vIzxiQm4rntbKo7uFoRxMCLiOEbcXKHQBpv2EvB8H6qqgiBxY/u+n0geUyGjUSbRpBXGWn3ZbodhQKjYUQxA1bS+lB2ZS9g0DIRBAI/eqAk9z57vQwKN8ep6x9Xb2XPnoKgqjh49OpwDG5D0Cs2nPSVYb4kwDJOEsDhGFEVJS3D6N1u1xjR2zSZ9/jv1WvZ3M1EUISYE//AP/9BwLUiyDEWWIasqFPa3oiTGgyxDURT+GPtfpo8pisLfDySJtqZhwA+C5LMzTe7pGJpxMIBsr6aqUFQVIb2G2k0oURTBpyGtuKlmn0kga5o20MpakmVI1DjcbSOBlTgur6zwEseJsTEAiTGQ9hYMKy+Cqy5SAah8Lpdc676fJCKHIUzT7PmewK4fWVESJcjlZRj0GhO0RhgHI4J4XsNqr111gu95iaAPfc7zPCiKAo9OZNlMBplMpiFnYZTJgNxAoDfQ21GZwHA9L0nOBO1MN+Bxq5rGWzmHYQg/COAHARe5YYJKzABJx5Zt28aVK1fw4MmTg5eT9fKZEcLzJlzXhee68Hyf/23ZdpJ8SZ8HGmOqzPUsSdLWZKwoUGmZGUEyQWmqCk3XIUtSwyQtpybxbY/JMuqWhddeew2PnDqFTCaz3aigyWgxNVKYMRFHEW+D3GyUsBg1a9DDJ1Q6gUugQjamCcM0odP8EdM0+WOmYcAwTRiGMbIyTda7wEpVLrDJMEzluDRMjJIEVVWTcJeq7tzVTiXFd7NqIQ0rcVxfW4NVr0OlmgvsmJvLGYeBBHBjln0GmqbxvCzHcRAEATLUi9B1e9RAMAwDqysrGJ+aguM4yOxii/e7CWEcjIA4jhHTCgWuJ9AqnBBFvA2rpqpYqddhOQ6mJid5h7WGCWlECUnNKzI23oZEyl0mpDXfIARGJjO0VSMzFLLYurH7QZCssOlN3rLtZBLVNJw+fRq6puHw4cMD7ZJ1xOSrfPo3q7JgxoBLk6/SEz5LwDQNA7phYKxc3poY6SRp0MlR7ZBLYDsOPNeFbhgdS9I6oW1sAAAmJyZQLpcH2kYn/CCAVa/zlaLHzhEzljyP190zwwlAgyHBzhU3HJoMCOZJGqRBl65pcBWFXx+KLCMMw20GAbtu1CEYBGkkJNcSEzC7HWI+6RLHjUqloYKBJbqOoqJCThlETCGShQzDMETNspDpxYtArxNFlrkwEvse3a773J2MMA5GAGkOJ7S58FhrYlmWYVsWfM+DLEkol8vbNdPZzWCXLmLu7bhN2LadaMprWtIlr7kcdAioqgpVVRORFLq6ZdoJQRiiWqth/upVHDt2DK7r8te38iCw9rm1ahW1Wg3VajVpMFOvw6dNchjpCT+fz2NycrJxRUz/VlWVTwRsYhiEflo131ZoPL6QzwPF4ranCY1vA4lh7fk+XBZeaTImrHoda2tr8OhnyrZPogiarqNYLKJAG30VCgUUikVk0x46SkwIojBEGEWIaP19HMfI5fNJzgVNhmVeglF9Z9IGQT8VRsMmXyjA9TwEUYT1SoUnKLaTUB4GEgCkDAQWMmS5CDH1IsRx3Fl/JPXZ6LqO9bU1TE5Pw7btXe+sejcgjIMhE8fxVkvmDuVXnuchYCsPQuDR0MJYuZw0DEkzYsNg2y2GkIbWqISQRPp5l4wF1/P4DT2XzXLvyij3rigKMjSEw2rQL128CFVVMTc3x/MUfOrqdxwHjm3DtixU6/XE1U+bIjHVt8mpKRw6fBhmJsNXraz5VV/ssIyvwfV7N5PKF5AUJTmvPbiEoyhqCM9UqQG3vr6Oq1evNhhP+UIB+XweuWwWJr0eMtksFGoIyKoKEoaQJQm5XI4bcKNGluUG40DGcJP/eoEZZ+VyObl/RRHPP+A5ByyUOuRzwryZaY+JoijIZbO8ooHpnXRrRsVCb7qmYXV5eaSJ3nczwjgYMoROajzrv8UFF9NwQkhrnWVNA4lj6KbZ+ma327oCTfuTJAmErgxGDRN6ipEYBg3u8l06D7KUSDMvLCxgfGwM81euJBMKbZWbDIVKOmcymJ6aQj6fR6lcRrFY3BYDDXttKNOGnZ51bhzcwWWMPR/jADk3iqIgl8shl8uhPDaGPXv28OeiKEK9VsNmtYpqtQqLdgBdWV7muSign3W+UEA+m4VuGIjjeHcnFEnaVnWym+EFQgiP/cuyjLGxMayurMBxHFg0XMW9XIRAGUVFVVM+FHuMGQNsURHHcaIB084Ypu/XNQ3VzU1EUQTLspDP54c+5rsZYRwMGZKqRW5546C6BZ7nIQoCZLJZaLoOVdPgBsHtEebosdHJbrgyLctK4s6Ksi2TeBS34Xq9jo2NDdRrNdTqdR4OYMlya2trKBSLKBaL2LNnD/KFAnK5HHRdRxzHCMOw4WbFSiZlWYaWCkFIzPW6yxBC+KRyRwsg9VAuy8ttB9p8ch7CMEQQhojCkCdNAkCRfsZAYkwoqgpCdTIsy+KhoqXFRdiuy6XLzUwGhXx+K0xRKGCMtj8eJq1KGSVZBtmh4dkNVtaarmYCANM0kS8UUKvXUdncbGwMN8p7RJtqE8MwICsKHNtGhCRkm81mW4fSmFc0jqHIMuq1WtLnYcidLu92hHEwROI4Tr6sHTL8HcdBvV5HGIbI0aZKGdNEpVIBCGlMqtnlPINkl+1XZqziYlRhBp+WioEQ5LPZhtK9YRDHMTY2NrC+toa1tTWsra/Do7kMhmmikM9jYmIC9913H86fP4/y2Bg+9tRTXVeHURQhDEM+8cQ0c5+FImKqUyFJSWe9hvI++nc7eMXLgOeatWq+XcZJ3+xQ74DlZ7CqiIhWQUR0gkMcN7jjJQAKNeLYZ5M+T7l8HlNTUw37sOp1rFUqcGwbge+jVqthZXUVV65c4bkhpVIJk5OTGB8fx8TERGvxrj6QW1QCSBht9QITNWpHsVhMjOEgwEalkiiHjnjx0Jx/wB+nyaByLgfbcRBTb0C2nfQywA34eq2GfKGAer3ODUSBMA6GCklnULcgCAJsbGwgDALeZdAwDF7aBWC752A3DANqjZMe9zeKMANTgiRA+y50fZ4Lz/e3DIG1NWxsbPC21uNjYzh06BDGx8cxNj4OI2WULS4twXEcPPnRj/Y0KbNJhckqR1GUNISiq1MmlUsI4UZE42G1LitUFGXbiq1f2M39nomnsms1JdLEyihZaWW7q1ICAEWBzow0ahD0e24ymQwKYYhcNotCPt+gOlmv17G6uor19XUsLS3h4sWLgJS0Hp+YmMDExATGx8dRKpX6MtakVM5BGlmSEI8gvJAOI7RDlmWMlctYXlmBRxN5M7ugGyCBhj5bHDPLQ7Bpq2vLslo3Wkq930+9NpfL3Z0qoiNAGAdDhDC99DZ5BqsrKwiDAJqmoZxyPQZBkDRXaVq17JrHgBkGfTDsMIPtOFyXfVtCJttnl23UajWsra1hfX0dq6urqNfrAICMaWJ8chIP7d2LiYmJ5Mbc4dxeunQJY+UyJgZszsKNBWroRamJKy00FEYRV7+MwhDbHMTUU8Oy4llWOPtfon93mmR4st2dfsNj1xGNWROmm8D+Tv0OgqBjMh47J83iTGyC3elEKisKDE2DSxNU03Xy+Xwe+XweBw8eBAD4vs+N07X1ddy8eRMkjqGoKvcqTExMYHxsDFqHkGInj4kkJX0XhnG3aBdGaIduGCgWi9isVmHV67wh2qiRAURofU9g/Rlsx0EUhrBtm5eycuh9Wqbn1XMcmNks6vU6SqXSyMd/NyCMgyHBQwotEoXCMMTa2hp8qoA4OT3d0HUtoOp13Lrd7XACGazTYkOYYQc3hCAI4NFs/3wut+0L3+pmE0URNiqVxBhYXcXa+noSkgBQKpcxNT2NB+6/HxOTk22NjVbU6nUsLS3hIx/5yFBX22xib/aIECoUFLYQFAITEeq28Sbjgf0tSRI810UYRVCpMcIS7Fj8flRtiIGtcFD6+oibJn+WC+B6HhzHgaIoPOmz2/aZkJPaJPrUSdJ5WC54nSo5sn4L7c6jruuYm5vD3NwcgFRoa30dq2truHzpEs6cPQsQgnKphHFqLLBQBNtuN+NAHlJ4YZBSxGKxmHx+rotarYbCbpQFsomdjbdZq4V6a9pWMrDPi77Pc12Y2SwvaxxY8OweQpyBIUE8r+UEG3geNqtVuI4DRVUxOTnZYBgAiWwsSRsHwB2TZ9ALDUlifeYiEEJQt20QJElFzeeG4fs+atUqNjc3eYiAeVvGx8dx+PBhTExOJiuwHXyxL166BMMwsG/fvoG30Q+SJCXu7RZGQxSGCGgOC4+l099sdUeoEcHcwM3eB9txeHijOZyRGkTD5JZW9GTGhE17K9j0Om4wAOjkz/4exEyMqKHADZe0kSPLkAH+N4Dh9nUYAJajwGTPO9bXp5BlmU/+rIlXnWoyrK+vY2VlBZcvXwakpJZ/cnISExMTsG27sUdDE51c7d1IewsGZaxcRrVaRUBFiSZ2IbE6fcyEXnfNTe5Y5ZDrurySIZfKZ2LXW71axeTMDIIgQK1WwxiVh/4wI4yDIcFKGNMXp+u6cCwLjuNAVVWUyuWW7X6DMESMpLRm1PLILRlC/gAvM1KUxEDoEdt1EUURZEnalrS1UalgcXER165dQ6VahUQIstksJicmsP/AgSR2WywOLQvfD0PMX7mCY8eO3XY3PDMaupUfMqMhbTykV+UyrZxQVHUrbt38WTe52ltdCawcs1XORMfjQOo7kQqTcA8HneS1IIBK+w8UupSUdUuU2y0MXUdIxbN6NQ5awUIR9913H4DEEF5fX+fhiPfffz/JrSAEL7/yCvbu2YPZ2dmG/KRBtA8Itq6fnXqQNE1DoVBApVJBrV5HgVb0jBpZkhCRrcZPrY7eoHLXTHG1blnINXkTQ1rBAiRJ4/l8fqS9bO4GhHEwBOIo4iEFhmvbcKh6m6ppyGazyLfJWA6DINE52O0yxhHEBnk2cQ+hhjAM4ToOQAiydEJYXlnBwsICFhcWYDkOdE3D2Pg49u7bh7m5OZRHmE18dX4ecRzj0IBSycOml0+HVUC0g+U1FAqFBldpOvZO0qv99GOpv5lcccY0kWOhn1R4Aux/5gZPja8XSBwj2IWKimFe8ZquQ6Fhm4DmEg0DXdcxOzuL2dlZhGGI7373u7h16xZk6sF57fXXIUsSJicnMTc3hz179vAQRC/aB8zDwwyJYYWWstQtTwCsVyqYnpzclQqZXkIqGm3eZNk24ihCjVYysPHJsoxapYLJ2Vk4joNarYbxAXOO7hWEcTAEiO833BhZx7wwDCGrKnRNSzTtW3wJWbIakFjfu+41AEZjJHTJR2DhhCAMUd3cxOVLl7C4tIQgCJDNZDBLb3oTExO8N0Arr8uwiAnBxUuXsG/fvqFnXA8sVrPDzyU9wTdPAP3mGzDDVeuzO2bf9FIts5PNY3gGgiRJ0A0DEe39MOyVpuu6+M53vgPLsrB3715sVCp4/vnn4TgOFhcXsbiwgHffew/vvvsuSqUS9uzZg7m5ucTz0uY8jtLrIgEoFQqoWxaiKMJmrYax3UjuY+EoSepoJLC+DJZtg8QxLNtOcpxoaGJjYwP7Dx3ivU583789ujN3CMI4GAJp4aOQZseyxCud9h5vl+DCKhVkSdrdJJhdyCgGWucjOI6Dq9eu4eaNG1in+g7lchlHjx7F3NwcSqXSthj4qMd869YtWPU6nnjiiaFve9DsbYKdTYRxyjC7KzQO+mDQSX7YV5Cu63Co9yCKoqGFozY3N/Gd73wHsizjueeew8VLl3guTSaTweHDh3H48GEEQYBbt25hYWEB5y9cwOkzZ5AxzSQJcs8eTE5M8O9STHZeqdEJCUklR7FYRLVWQ71eR2YX2iLzkGYPRg+rZKjTHhm24/CeGjFVy8xkMrBtG7VaDRMTEyMd+52MMA52SEyTCSVJQhyGvLNcFEXQNQ2qonRcibKyLPV2hBTSseAR3zRq1SoWFhZwY2EB6+vriAGUi0U8ePIk9u/f37HxCf/yj2yESSJieWxs4PLFTgziORhGq2zuNaDx/TsZdnZ6HeXt6kzYjCzLbcsaB2V5eRmvvvoq8vk8nn76aZimiTAMW3aT1DQN+/btw759+xDHMVZXVrC4tIQbN27g4qVLUBUlCVHMzWF6ZmZHybq9Yppm0qTKcbBRqWCGNmcaJSzM1cs1wdQQa9VqEtr0vKSKAcDaygqOnjgB27YTFdshGnx3G8I42CHMaxDHceJOoxcn8wJkcrmOrtKQGgeZ3TIOdqlMksQx1tfXef5A3bKg0BvV3r17kS8WkctmUSwUum+MGQcjmgyqtRpuLS2NxGswKMM4Uiasda95DQCM3KDtB90weIfInbb/nZ+fx9tvv42Z2Vl89Mkn+cQU+H7XiV2WZUzPzGB6ZgaPPPQQVtfWcHNxEYuLi5i/dg2KLGN6agpzNPww9BV96riLpRI82gKdNWcaNZIkQYrjbb1hWqHIMjKZDBzHgef7SSMmXUe1UgEhBLquw/d9npz4YUQYBzskphmutmUhiuOk4YgkIaIJhmoXq5N1HxxpHLcbQ7rJkjjG0q1bWFxYwMLiInzPg2EYmJubw8OnTmF6ehpxFKFarSImBLlslidHdVRiY/kLI4qVXrp0CYZpYu+oyhcHOL/SgCWBaeK7KaQwSAlel+um1etHAWvjTcKwr7LGZt5//31cuHABhw8fxqlTpxqeC8OwN68EvW5iAGPj4yiNjeHkyZOwLQuLi4tYWFjAW2+9hbfeegvjY2OYm5vD/v37kR2CNkFa7lxRFJRLJayurSXdSw1jxxLSPY1BUXq6TzBdGSZz7rhuokOiKFhfW0Mun+cdWIVxIOibOAgA2kucTfIGjVexrn0dISQpY2zWOBgVbW6OO71lOraN+fl5zM/Pw3EcFAoF3HfffdizZw/Gx8YaVhT1ej2RSE7lYaTFclrd7FnC0Chu7X4YYn5+HseOH+/Y42BHDLDKHYrn4G5ouETpW6ETSbikn/M6yrOwk7LGOI7x+uuv4+bNm3jkkUdw5MiRba8JggCFNpU6LatOmEYFfS6by+HI0aM4cvQofM/DraUl3FxcxNmzZ/HB6dOYnZnBwUOHMDs3t/Prhe7TpP1KavU6KtVqslgacVhDApWU7nZd0OeZfH0YhjxBcW1lBROTk9jc3ITv+0lI50MoivThO+IhQnwfvufBc10QJPXKrusCoLW1Xb5kBEhqawkZaSZ+srPhTq0kjnFreRlXrlzB0uIiZFnGgQMHcPDQIZTL5ZbvCXyfG1HpPIxmg6DZSOCKZiNY+V2lzXIOHzo09G3vhJ00W2Jw4+Bu8BxQ+jnifpMSR5mjMGhZo+/7eOWVV1CpVPCxj32MKyk202qCai5J3IYkQaE6AGl0w8D+++7D/vvuQxSGuH7jBq5cuYJXXn4ZZjaLQwcP4uDBg/3nT7S4XouFQqJSSMMLUyNO8JN6MAyan82YZiIyRRMUsbYGx3FgGAY8qtxZ6CX8eY8hjIMBIWGIwHHgOA4IwOuMwzCEJElJPK/LhOb7PgiSm/fdkvTiui73EliWhXKphFOnTmH//v0tE6bS2NSIMk2zpbhPs1QsV99LZVsPk5gQXLx8Gfv37x9tRnWfk/wwDANgK3v7Tk9GbKCPsfZdsTDCPIVByhrr9TpefvllhGGIT33qU22NaiDxHPCcAxY66EWNVJYhRVHb86SoKg5SY6BSqWD+8mWcP38eZ86exdzsLA4dOoTpmZm+vAnpfUm0OdPKygo8z0O9Xh+pmz6tktjOGGx+nHl5LWoguJ6HG/PzOHD4sDAOBP0T2DYsy0JMCExdh2EYqNVqABKvQUdpV3pxspDCyF1WO70hEoLlW7dw5coVLFAvwb59+/DRJ59MZEZ7uHF4vo8wCLikaTfSimdySiJ1mCzduoV6vY4nR5yIKN2GkAJwd3oO+kHqc7IftYnUT1nj2toaXnnlFRiGgWeffbZrPD4IAsiK0th1sscJW5Zl3rq7E+VyGY8+/jgeevhh3Lh+HVfm5/HSSy8hl8txA6KjEd3Gw6frOoqFAjZrtSS8YJrQRx1eaJOnxCXHm2AN3yzLQhiGWFpawuzevXzBN0yRq7sFYRwMQOT7qFcqiOjEnslmEQRBo9egE/RLHdCcBX2Uq9Zebp5tJJs918XVq1cxf+UK6paFQrGIRx55BPv37+/YQa7V9rmHhWqd9wpvKCRJSQOiIa7+Ll26hLGxsZErod2unPq7KSGx31LGgfYx4uqGdFmj5/ttG37duHEDb7zxBsbHx/Gxj32s66TDWoCrtIV33/SonMhQNQ0HDx/GwUOHUNnYwOUrV3D27FmcOXMGe+bmcOjQIUxNT28X1gLafjfz+Twc2gSpsrGByYmJkVyXTPhLkiRIhCBG4zXV6fpSFAWZbBaObcPzfVy+eBEHjxxJZPAdRxgHgs4QQlBbW0MYhoniFlUj47kGhtHoNegwmYW0p8Ju1B53JP0lJwQrq6uYv3wZNxYWIAHYt28fPvLkk9uSC3vF9X1EYQhZlgdy3/MmQLT2nzSNdxBY+eKTTz450Pv7oV+3/jA0DlhXR+DuMA4G/Rz70juQZaBD86JhwMoaPVY73/TZnz9/Hh988AEOHDiAxx9/vO21wfoeEELg0zydbmG7TrAeBH0hSSiPj+Px8XE88sgjuHbtGq7Mz+PbL72EfC6HQwcP4r777oPBvtMdrnNJljFeLuPW6mrS3MiyUBqBq57pHbDxyE3eg26hSU1VEZsmPM/D2uoqJqanoWkaHMdBcYTS7XciwjjoE7taReh5kCQJuXwekiQ1eg36SCxkcfWR5Rv0cDNgr/A9D1evXcP85ctJ45RCAQ899BDuO3CgPy9BizG4jgNCCLID1oCnS6TS/wODGwqXLl6EbhjYs3dv3+Ppl35WrFxueqfJiHeRAFKafsfaV97BLugiqKoKVdNAgqChrJEQgrfffhvz8/N44IEHcP/9928bG+swGKeMQwmJFgqAna1c+/QeNKNqGg4fOYLDhw9jfX0dly9fxgenT+OD06exd88eHDp8mC+U2p1nVdNQKhaT5kxUiXAU4YX0NcEXE6mKjm4Yuo44jhEEAa7Pz+PwsWOIaCXKh0lOWRgHfRDHMdxqlScgsknddRwALbwGPWxvp+2S29LjTWB9fR0XLlzAjZs3QQjBvr178ejjj2NyYmIo43JpDFZRlIErMiRZ5vHDZknhloZClwnWCwLMX72K4ydOjK58cQd8KJMRd2Hi3q0zYeg6wjDkZY2sedLy8jI+8pGP4MCBAwBo8hx1fRNCeG5K8zhZhc9Oc5MG8h40I0kYn5jA+MQETp06hWvXruHy5cu4/u1vI5/LYc+ePThw4ADa+QfzuVzSlM73Ua1WMTmCkF7zETaUNvZ4/Oxzsy0LGxsbGBsbg+M4wjgQtKZeqQBxzNW0gOSLG0ZR91yDDpPV7biBb1QqOHP6NBaWlpAxTZx84AHcd/DgUC9+EsdwXBcgBBmqXz4I6SqGmJ7/lq/bekNHQ+Hq/DxiQnDo4MGBxtMvfQn1DGmf0V2WjNhvkh2njxyU3cr90DQNiiwjDENUq1W89tprsG0bn/jEJzA5OclDBmntjk5HzVpk7ySskOxkZ96DZjRdT7QTjhzB6uoqLl66hAsXL+LK/DzuP34chw4fbukVLZZKcFdW4DhOoio55DLuVt6kXpMy+TaoF9hxXdxaWECpVILruigWi3ePwb1DhHHQI1EUwbcsAEnjE3YTa/AatLto2sUVRyVS0+HLX63VcObMGdy4cQP5fB4fffJJzM7O9uXx6BXHdRHHMVRV3bHRwRo49Xpja2coxHGMS5cvY/++fSNvCDMQw1KrZPkGd8mNjH+uA4QV7jQkSYKu66hsbuLdd96BJMt49rnnkM/lBuqIyDwHw8hNkmU50VYZ5nUhSZicmkI+n8fmoUO4dvUq3vvgA1y4cAH3338/Dh482HB/0XU9qQxwHFRrtaEbB+0Mxn7PvaZpiQS072NjYwMTExPwfX/0mjR3CMI46JHa5iYQx1AVhVvwPXsNgOSC7aUueURYto0zZ87g2tWrMDMZPP744zhw4ABkSUI4giStOI4Tcagdeg0YrLRxICnirY1gZWUF9XodTzz55ND0BIZJc9hkUO7WvgqDHHvfegcjhhCCjfV1vPnGGzAyGTzz9NNtKxd6IQxDEOw8rMCQZXnomiFAkudiGAYefvhhPPDAAzhz+jTeevttnL9wAQ888AD279/Pv2/FQgG248D3fd4ZcWi0ObaelBObMHUdtm1jc2MD5XKZiyN9GBDGQQ+EYQjXsqAh8RqwG1hPXoM0TXKvvGveMCeoJje64zg4e/Ys5ufnoRkGHj51CgcPHoTaXFExZBzX5RoOwwhVsBXwTm9pN27cQLFQaNkIZiSfRx8Mo0ohvS0AI/EIjQI+3gHee9s7NKaSCQkhuHLlCt555x1MTU3hxIkTO/behGEICcNLXJZGtFDhJYSyjFwuhyeefBIn7r8fp0+fxuuvv45z587hgQcewL59+6BqGpdWZt6DURqyPKTQZym0qmlQVBUO1bXRNO2OXFSMAmEc9EC1WoVMkv4HqqoiJoR7DbqW56Xj3U0XJY83DvtLIUlwfR/nz57FpcuXoSgKHnjwQRw5fLhlI6hhr7yiOIbHKhSG1Wyli+pZL4RUKvb48eNtdtH4hWcKjTv2evT4umF/BsDd5zkYaMIaofJhK0gLA5/t/7333sPFixdx9OhRnDhxApbjwA+CHYWwAtaueVgTEtUOGbb3gFVfpUdZKBTw1FNPobKxgQ/OnMFr3/0uzp0/jwcfeACT09OwbRtBFMG27ZEpJ/IKIAzgPSAEpmHAsm1sbm4ik8nAo90373WEcdAF3/fhWhZM6h5nl1XPXoP0c22MhKHZoLQm+sKFC7hw8SIA4Pjx4zh27FjneOWQb66ObSf6Dbo+NOEQdo53ckNbXFxEFIbYv39/b/tMdtzw2ChXDcPoxMjgOQd3iXGwUxGkUX4uLY2BJqIowhtvvNHYPImKf4W0sc+gYYEwDIeuhdKvumQvEKqt0eqaK4+N4ePPPIO1tTV8cPo0vvPyyxgfH8fBQ4egqiqq9Tqy2exwrtf0sRGCKI4br6t+kljp8WiqCpd6DxzHEcaBIOU10HUo1NoOfJ97DTrGn7okWQ2za14YRbhw4QIunj+PMIpw9OhRHDt2DMYul96EUQTf8wBChhpH5Df+HbRtvnbjBsYmJpDbQXtartsO6nEZYihimIbByJJdR8UOzqME9NahsZcJIWWgMc9RN3zfx8svv4zNzc3G5kmSBE3XEdPGQ4MaB8EO3tsWKhA0TO9B3OV+BwATExP41Cc+gVsrKzhz+jTefOMNlMtlzO7Zg1w2i9IQhIbSntC0ZgSjn+Nm16NhGKjWarDyeVSrVZTL5Xs+tCCMgw44jgPfdWEQApNOdARoVEPs1WvQ/Dhzde1Q5yCKokTe9MwZhGGIg4cO4f7jx/uybIcZVmBeA90whnpDk7Gzcbqeh6WFBTzy6KNDGQ8XqWlXidI02fUSFx/W6jdOxVfvFs/BTlex/V4b2z6LAb029VoN33n5ZURhiE998pMoN+Wy6KoKj3YjJQOKgI2qZTCTGB7Kdz91PrseoyRhZnoaM1NTWFxawvvvv49zZ89iYXERjz788I7lzHkLa7T4nDHAPYR+j0zDwMbaGrLZLBzHGV7I9A5FGAdtIISgVqtBCUOYmQwUWko3LK9BmkFuGHEcY/7qVZw9cwaO6+LAgQM4+cADg63Wh+RiDGnZz7C9BgCSZE4MHla4cfMmiCRh3y4oIgLbP1MJjWPv1qZ6J9zuxMpBGFjngL0NrW/67LwSkigPDlJK2I61tTW8/PLLME0Tn3z++ZYtjlVVhSLLCKIoCQ8MEGYLfH80uv5D1D1Ib6Nnb5UkYW5uDnOzs/jg7Flcu3oVL/7932Pv3BweOHkSxQHllZkeSqsGSwA17GW57fPboMem6zrqlQrqtRo2NjaEcfBhxbZthL4PXZK4dnhfXgOg842OkIEb42xubuL1117D5uYm9u3bh/tPnkRhB67yYWHT5kqmYQxdElqmMsA9f6GbuH7tGmZmZm6fwhm9EW+DrtziVNLUTif1iJYx3onqj23ZwQTFk+DSq+BUchxfSQ7RMLhx/TreePNNTExM4GNPPdVeoEiSoKlqopMyYGe/Uer6Dyv3gLvvB0woPXLoEErFIm6trGBhYQELf/d3OHHiBO6///6+74+k6XfLXQINssrdxsdyKbKZDFZWVpAvFDA7Ozs66fs7AGEctMGyLKhRhGwmA3Zp9u016MSA3oJz587h7NmzyBcKeO755zHWoQd8rwxjfRlQ1ymAliuoncJWI/2onDHqloW1tTU8sQtNlvqG6jekP4N0WR9p9kCkVfW6hTTuIuOg+ZjS36D0BN/qfcxr0Bxfbj47wwqdnTt3DqdPn+7aPImh6zo8z0MYBIgJ6TsPxLJtTM/O7mTI7ZEkKNLOZZV5pcKAhq1pmjBNE9PT09i7dy9Wl5dx9vx5LC4u4oknnkCpVOp9LOhN8Khnr0mq7DNjmqjW66hWKqhUKpiYmOh5XHcbwjhoQRiGideAinoAyQXn9KNr0OV5dpPo1Z1c3dzE66+/jsrmZqNFfTvru1PYrpt4DUwT8gisaWahD7L6u379OhRFwR6WKHYb6PoZtwk5bHsXE4MCWvblkJjrnBBuHGxLrEutmEYRemgYF/1bavM4ez1f3ZPtbXa7rQD53yMuaSSE4K233sLVq1dbN09qg6IoUBSFl0D3kyQcRRE8zxt+mC7NEHIPCK0I2Mn1VCgU4HoefM/DoSNHsGfPHrzxxhv4xje/iftPnEg0I3oweHu9R0jAtmut9QtT30dJQsYwUKvXYVmWMA4+bHieB5kkAj7ssgjCcHheA4A3EWJZw+2I4xjnz5/HmTNnUMjn8dxzz2GMJT3dIYaB5/sIgyD54oyoxEeWJIDFEtGfdsC1a9ewd+/e2+sCHEEyYsvX03PEVoRA65LM9P/tVk/plRWhTcIaJnK0nsSZiz+1gwZ3P5r+Jk2G0aDTS6ekxJ1+U8IgwKvf/S5WVlbwxBNP9FwOy9A0DWEU9W0csAXJSOPbNOFuEK8cg3kOdlIdY+g6MpkMHNtGtVrF9OQknn/uOZw9exZnzp7FwsJCVy8CQX8LCLnX3IOU4ampKuq0pPFe5u7xO+4ivu9DIqQhjuj7PoDERdi1QqGPL4jcLhaNxFvw4osv4vQHH+D4sWN47tOf3jIM2L5uN7SWm3sNRuTKTnsj+kkq29jYQLVW6/tmPnQ6fFY7cce23F6fXgGJXoPNPw3P0c6YDe9r3k6Lx/plJ+8fVQKm4zj41t//PTY2NvCJT3xioGuJ3TcCGlroFZtOQKMI1TXQ4T7UCyyks9PPoJjPJ+eJyipLsowHTp7Ec88+iziO8Y1vfhNnz55teQ8ghCT5NiO+L+q6jjCKYFkWD6XeiwjjoAlCCDzPgxTHvNc4q1IAAL1TQlEqqayX/XBaZK6fPXsWX//GNxBFEZ57/nmcfPDB7QlmQ/IcbFvp9YHr+4jCEPIIvQYMdvz9GAfXr1+HaRiYmpoa1bB6ouPtasgeIK6fcbfkHNzB1RWVSgUvfvObyffw2WcxOTk50HZkWYaqKJBlmd9LesGxbRDsgnGAHWpiDMFzACQelnwuB1mSUK3V+LVcHhvD85/+NI4fO4bTZ87gxRdfxObm5tbuge1iRz0gof/cHEVRoMoy6rUabNvuc493D3fJ3WP3CMMQcRhCkSQo1DgIgiBpfKIo/LGW9OE1aK5UYPHSarXKvQXHjh3D883eAsawQwqDfKkJgUtlkofRXKkbcp/GQUwIrl+/jn379t32iWc3A0B3WynjTvoqNDDk411aWsK3//7vkclm8eyzzyI/YGkdg3kP/D6MA9u2R5bHs40deA/iIV5z+XweCu0eaadc93LKixBFEb75zW/i7LlzvER1J3vu+v1sCn0pmgbbsu5p40DkHDTB8w00jV9sfhCAEAK1U6xwEDGjVJyZEILzFy7g/Q8+QD6XS3IL2omB3CG5Bn4QIIoiyIqyK53KZFlOkoh6NA6Wl5fhuC72Hzgw2oHtkGE2DUrnBNw1ngPGDieWYXpnLl++jHfffRdzc3N44oknhpKvotF7ShjHiOK4p1JT23FG7pFLM6juwTArZBRFSdo/V6uo1WpJxVhqu8yLcPbsWXzwwQe4dv16koswqC4Cesg9aLq/65oG27Zh2/ZIpbtvJ8I4aIKFFLRUlUKYyjdoS58XR3q1VK3V8Mbrr2NjYwPHjh3DA/ff3/lmNIKs7E7JXO3gmg/d8jCGRL+egxvXr6OQzw+l3HOUDPPmkpawvVuMg6F5DtBhcuv1/BKC995/nzdPevihh4bmkZAkCZqmIfZ9BL4PpYdJ37Ht0VYqNDHodciaGw3rLpDL52HRpky1en2brLIsyzh58iRmZmfx+muv4Zvf+Abuf+ABHD92bKDQRtfKhaZt6qqKehyjVq3C9/17so3z3XH32CUIIfB9HwrAxUpYSEGRpPYT9iCWNl09LCws4Ot/93cIfB/PPvssHn7ooaRzYrtt9pHX0Bd9fqHCMERIPSrmLn0xFCqE1ItxEIYhbty8iX23OxGR0u7sDrvVMFv93JXrmCFMwjtZuUZRhFe/+11cunQJp06dwsMPPzz0UIWu65BkGX6PiWy2be+6Et8gkyurvhqWQSpLEgqFAiRCUKvXEYbhttfEcYxyqYRPf/rTOHLkCE5/8AFefPFF1KrVwfbZx9gVRYGsKNw4uBcRxkEK3/dBogiyJHGXH/vgtSEr69UtC2+8+SauXruG6elpfOKTn9zSFG8X+7tDwglA4mFhPRR2JR4KQKJGUy8lV4uLiwiD4PZXKXRhGBneaQZV3byd7MZV3c0I8zwP3/72t7F86xY+9tRTOHz48EjGoakqZCQTW6sJrxnbcZDZZeNgEL2IXpou9Usum4WmaZAlCbVarXF/tLQWSK71Bx98EJ969lmEYYi/+7u/w/z8fN/760fvgCWY1ms17kG917h77iC7gOd5kOOYew1igK+OOxoH/SqeWRZef/11SEiMjltLS/ibv/kbfOvFF3Hu7FlUNjdb1/KP0HXfz5ZJHPPOi7vlNQAS7w2k3iSUr924gfLEBPJ3gKx0J4Y9MXLPQTfjIHXzbzUGlrsQs99RxLUO0u/rZfzsWm5V+tgw5tt0fddrNXzrW9+CY9v45Kc+hdlRimVJSadGuQfvgef7iKhK667Sb0iKXRMjiL2XikVISETWAmpMpfNq0oyPjeH555/HgYMH8eZbb+H0mTN9e+Y6fm9S22LGgeM4sCxrqD077hREzkEKlozIQwq+n7jKqMJZS/pMRNxYX8d3Xn4ZiqLg4YcegqyqmJ2ZwdLSEm7dusWlWc1MJulcNjuLqampofdz30YfqwXP9xFTkaiRNIRpA/NQxHTSauf+dD0Pi0tLeOThh3dtbIPSVha4hxst68eQfl1aJKrrjbGLgQBsTaoSq8SRpG2v7eWqaXXrJE2/039Lqf/7mW5Y7kzze9qNcXV1Fa+88goymQw++clP7krJoK6q8D2va6dGh2bC77bnAKCTJO3R0Q2+iu8i6DYITFbZcV1Uq1WMj493nIgVRcFjjz6KXDaL9z/4ALZl4fHHHx+OJ63pXq+qKiRJQnVzE77v99UJ925AGAeUOI4ReB4MQhpKGIEuiYh9sLS4iFdffRWlchkfe+op3Fpehu/70HUdhw4dwqFDhxBFEdbW1nBraQlLt27h6tWrgCRhbGwMc3NzmJ2dTWJxQxnRABAC13V3NdeAIdEVDStdahfOuHHzJhDHu9aBsWckqUGnvZW+Rau/226uxTZ4SdldFFZoZZS2Mhha0WwMcEGpHuSAb1y/jtffeANTU1N46qMfbd88acioqpooEoZhx06NuyaA1IZeKxeGLeLVDJNVdl0XruP0dD8+dvw4zEwGb77xBlzXxVNPPdXTQkYCQGQ5+Z420yRXr8gyFFnG5uYmPM8TxsG9ShRFySpMUaBIEg8pAB2Ej/rwGly5fBlvv/UW5vbuxRNPPMEFUSBJiMKQeyYURcH09DSmp6fxMJIQxNKtW1hYWMDpDz7A+++/j2wmg5mZGczMzmJ6amokvd7bwcsXZRn6bcjQlemKJo5joI1xcP3aNczMzt4xGcR88mI3nJQbtrlZEGPgrHEmgHQXlVbtRJehlfeh+X/C6uBTXo+zZ87gzOnTuO/gQTz22GO7W4pGqxaiOO7YqdGxbUiSdNuuY1nqrSHTMPoqdMLQdZi6Dou68LsaB9Q43L9/P0zTxKuvvIK///a38fGnn4bZg6Elo7Wnq8GwB01KlGVYtRrvhHovIYyDFHJqsmchhXTTlIEgBKdPn8bZs2dx+MgRPPLII/zGzbLvwyhCu8s9l8vhyKFDOHzoEKIwxMrqKpZu3cLS0hKuzM9DlmVMTk5ilhoLhXx+sHH2iOd5AJJExNtR2yt10Tq43R0YCVqHBAiwLXTTr8u8F4YpRrNb8FDCsMbcdJ6ZBDQzEt56+21cm5/HAydP4v4TJ/iKMK05Mmo0TevaqZFXKtzhn2UYRfxeOWzY9ymXz8NyHDieh0IYdl4QpT6/qakpfOpTn8J3Xn4ZL774Ip75xCdQ7EUPoZ33oOEliXfuXk1IFMZBG3iVwg68BiSOk4qEq1fxyMMP4+jRow03QOYW72Z1sktdUVXMzs5idmYG5OGHUbesJPywtIT3P/gA7773HnL5PGZmZjA7M4PJycmkLLIHZNCJpcMxRVGUGE2EIHObVjPdJJSvX78ORZYxN6oWt5R2RgDQfpIbREuiX+466WQMV+eAbWebJ4EkHRFfefVVrK2t4cknn9wqc6X7bzAK6GdLpFQjqB5CFb2iqmrXTo2O696WfAMOC+N1mSTZ872IOvUDSW1b13XomgYvDOG4bl+LoGKphGeffRbfeeklfOvFF/H00093lcFu6T1o+l6z71gURbBte6va7B7h/2fvT2Lkytb1UOxbu48u+4zsyGSX7MmqYhXJIutUFauurp/0YMPvGbBGhgfywDYMDa4G8oMEaKaZYAHGBTR41xppIjz4StZ9tmDfe5o6555DsorFYhX7Nklmksy+jW73y4O11s4dkdHsiNgRmUnmBxAkMyP23rFj7bW+9f/f//375CAEyfcBSYIPwOPK2JohrAbEwOUT0fLSEi5fvoyDBw5se43M0wpRSpoqz00AZNJpZKamMDU1Bdd1sbi0hIX5ecy9fYvpFy8gSRKGs9mAUKQaTDREkurumkzLAkV3yxcrIdXxOqBgHRjHJyZiSbUEIesmSUBNVEYOOrBD3ZPuiDGXwVU7SrFYxPXr11EyTXz5q1817pEgUhDVCAGp6BzZImlo1KmxWCwitcPVNgRouBHyeUo2zjEXJgYCyWQS9sYGCsViXXJQ7btIJBL4+to13Lx5E3/84x9x6eJFTFSZkyuPU/k9h++D0EABQL5Fb4XdjH1ywEEpZWkFVKQUJKn6g1/ngTFLJVy/fh2FQgFffvllzYlIVhSW16sTOai5gIiFhv+tKArGx8YwPjYGCmBzcxPzCwtYmJ/H3V9+wS+UIpNOY2R0FKNjYxgcHAxa+kaBKF/cCSFiGPVcEtfX15HL5Zh5TZMQC0G177RTIfrYyxjDJV67PBQdRkfSCiGsra7ij3/6ExRFwbcx9EioRwYID0dH+W41TYPJqxaqpRaKxWLLjZ5iQ0gEXBXcd0RUdbULUR5brVw5mUxiM5eD5/swTbNpAaCqqvjyV7/Cj7dv4/tbt3C+VMLx48drvl6qEGRWSwMG5CCfb+pa9gL2yYFAaDDajeyS6xCD3OYm/vSnPwEArn3zTd38lugV4NYiB8INsdakWUEQgh+D1Qf39vTg5PHjsF0XiwsLmF9YwOybN3j+/DkURUF2eBgjo6MYGR1FkpdU1SIjlm0zP3hZ7mr5YiWEiLMaOZiZmYGu68hms5GO5VdGBDq8oIYFcQBiN7UKT+B7UpAY4zHFWJ6bm8MPP/yAdDqNq1evdlzcR0Oi03DFSLXnStTK+74Pp8KC16esqdmOphU46pkihZ/DttMKnHTVmoMkSUIikUChUECBN6RqFkSScOniRSQTCdy7dw/FUgkfnT9fnZhWmVe3XRMnRLl9cvAegw9ySmkQ5heLYLWwYjUsLy/jxvXrSCST+NUXXzQsQVJkmYnrapCDQMRWD+GyuBoPlaYoODAxgQMTE6AANjY2MM+1Cj/fuQMKlpcbyWYxnM1icGBgWymcKUyPdrhcR+biMZ9PJOEStnfv3mF8fLzqg17NN32nRXuxN2wJiRF3+rM1g051kXzx/Dl+uXsX4+Pj+PTTTzsimKuJSqItSHzFc6qqKhzXheO6ZeTANE1QoPsGSNXA0yjVZhcR9YxDbxClMiKdSqFYLMK0LLjVhIlR/FoIwdlz55BMJvHL3bswS6VozbWqjE8RfS0WCg2vfa9hnxxwEE4OHNeFysVCNQd8ld38u7dv8cOtWxgaHIxcUyvLMitljMtdK8KDQQD09fair7cXp06ehOU4WOTph9evX+Px48dQVRXZkRFWLjkyAlmW4bkuiCTteHkg4ZEDcOW5+I42NzeRKxTwERciVt6F3bZUdqKTm9jF7SViUIaYrpv6Pu7evYtnz57h+PHjOHfu3M472InPFipjhSSx1IJpbkstCAOkbvdVqAVSo2uhmLvaSSlQMJIRZdyqqsqEiY6DQrG4rSFTM6LfI0ePwjAM3Lp1C3/84x/xxRdflM3bBABtMKcKQmFZVuBZ875gnxwI8EHuOg5URamfUqjA6uoqfrh1C+NjY/js4sXILFqSZRBCAlvQRufpBHRVxcEDB3DwwAFQAMvCgGl+Hj/dvg0AyPT0oLe3F2NjY+jr7d3RxScwQvJ9ZoTENSFz8/OQCMHQ0FBXvPpbQgRHwnawFysVgHgjB67r4sdbt/Du3Tt88vHHOHrsWFdKE5sGX3TEeJYkCa7rQtc0UEpRKBZBsXMGSJUgqL7w+m1EDsL6gma++1QqBWt9PahaaGe8j42P46uvvsKf/vQn3PrxR1y5cqUsJScRUreMPXAOBbCxvo7hiCnNvYB9ciDg+/ApDaoUavZSqBjEZqmEmzduoL+/vyliACAIiVFK2QMSzlE2efll19fiZEgA9A8MoL+/H6dOn4ZlWZibm8PM7CzezM7i9evX0DWNiRpHR5HNZrsaSQhbtFIwrYZws1yYn8fw8HDw/12J8NjpZKXCXoscxCSiNE0TN27cQG5zE1evXt2aqHf5/VAUBY7nwXXdwHAtn89D17StOWInLxBgqYUqmiTf91mlQpORA4ot++9mYRgGZC7kNk2z7ehK/8AALl+6hOs3b+LRw4c4e/Zs2XWWX3h51JiCESOf0n1y8D5CDHAhDFQUJZKS3/M83LxxA5Ik4fPPP2+aPcuSFEzkrudBFe9vd+FoYxEKTwBC3JfJZCDLMlzHYVqFhQXMzs6CEIL+vj5WKjk2ht4ORBXEtVAgcCiTZRmEOzUCzOZ6aWmppSqFbkIIEqOEK1vBnrRORjyCRCEE9inF19euoa+vjzWLAhqa2ew0VEWBZVllEcSN9XX09PSUVZ+E9TU7AUJIuUugqFRoooxRXHvUNEI1SJKEZDKJXD6PYqkUS+olOzqKs2fP4v79++jt6Qk8MLY9q1WuWUQXCu+ZKHGfHABBgxHRS6Gu8REQ7M7v/PQT1jc38c0337Rc3icc/1zPqy2AbAeVuc5GLw+fn1JYvHwxkUhA6+3F4NAQzp47h1KphAXeLOrps2d4+OgRDMNgOoVsFtmRkZbzbwEhqLxm/lnkCvOopcVFeJRipMPGR3Eg8P2n1W2T28FetE4GQqWMLb5/aXERN7//HslEAl988cWWwl/c6w4QsTghGvhQz4PneZBlGRubmxgNj+dw+WSoEqKrn6qirDHQSvEeA40Q9i5odxORTCaRLxRgOQ5sx9myuG/jez4+NYWN9XXc/uknpDMZ9PX1BddaNzXFP0vhPRMl7pMDAPB9eL4Pz3UhSdL2lEJ4YPCB8OzZM8zMzODy5cvo6+1t+dSKECWKXUOj8sUOI3xWy7aZ6E+Wty30iUQCh48cweEjR+B7HlZWV1lnSS5sJIRgcHAwcGvsiRBVqFXfXAmZV3kIcjA/P49MKoV0h62j40K1euk4sBetk4H2NAczr17hpzt3MDw8jMsVQmBBdKOMqR0FIVAUBZ7vw3FdUEqRLxTQUyG2C7+eVsxJ3YoqhMsahflRI5V/OymEWlBVFYauo2SaKBSL0Pgc3NYZCMGFCxeQy+dx48YNfPvttzAMo6HIUegxSrxR1vuCfXIAgHoe4PuQCNlS5YYGfLDz4JPXwsIC7t27h5OnTuFAA5etRghbcAboxOTeQqpBOCLqDcoXJVnG8PAwhoeHcf78eRQKBSxwX4XHjx/jwYMHSPBmUUKroCjKlgNdk5OGMKbyfR++52F+fh7ju60DYy10cBcb7Mr2UFoh/N03Qw4opXj8+DEePXqEw4cO4cInn4BULlJC9IddkLNvAFVVYTsOXMdBkVcq9EbddISjCoTUbhwUB0JljR4XEtZKKQjBod9GCqEekqkU69RomvAzmViEuLKi4MqVK/jud7/D9zdv4suvvgqqyqp5yng8dUW4SDroh/EeYJ8cACxyIBgwVw1v60nAB0Q+n8cP33+PsbExnDl9uu1Ty4oCws8JdGkSq7NAiUfY5RMVIaSqtWs9pFIpHD16FEePHoXveaxZFE9BvHz5EoQ3ixrh1s6ZdLopQkQkKfClX1tbQ7FUwujISFPXuFMgQOtNvBpBCBL3EDmoFpVr+BbPw+2ffsLM7CzOnjmDEydPVre3Fq+P4TI7DZFacFwXG+vrAKXItOLkSCkjBrxUshP3QHRrDCoVqkQOKPciAToXyUoYBmRFgeO6KJZKSKdSsXzORCKBK1eu4A9///f4+eef8emnn25VLYQrjkKeOMJ/JZ/L7ZOD9wW+7wM816eoKiiY9qBMQ8AHhOM4uH7jBgzDwMWLF2MZ9DJ/gD0uiuwa6u1gCQmiBoamtbXYEElCdmQEw9kszvs+8sUi0yrMz+PBw4e4f+8ekqkURkdHMTIywioOIiifZUmCSwjezc9DliQMDg62fI3vC/ZqWkEgynU7to2b33+P1ZUVXL58uX7kLjyh7/J7IkkSFEmCL0lYX19HKpVqz7RJ6C1C/yfYqvhpC4SAcDGiT+k2vYEXcwqhHpKJRCBMTMfYh6J/YAAXPvkEt3/6CX19fTh27Bj7RWjeFLbRQotBASZK3CMblUb44MmBUDJ7rgtZlkF5OWNlXpgC+OHWLVimiW+//TY2C2FZltmDJpTV3UQtsSLvowBKWy5VLBMVhoSc6VQK6WPHcOzYMXieFzSLmp+fZ82iZBnDQ0OMLIyO1mw8o8gybMfBwsICsiMju7uEMQxCOvI9h01+9pIgsZlKhUI+j+s3bsA2TXz55ZcYjNB3QCIE3h65H6KkcWNzEz1t6JiqQhAF7q3Qbm4+3PxMkmUWsSCk6/qOVCqFXD4P13VhxWxCNHnoEDY2NvDL3bvIZDIYHh4uu29CJxZEmd+zioU9MqN2EFxU43neVsc/SuG4LjSx4FCK+/fvY2F+Hr/68stYhW8ireC57s7tcCpIgogaqKradHdDKiahcMlTDciyjLHRUdZemVLkcjnMc63C3Xv3QH/5BWnRgnpsDENDQ0EUQ1YUuK6LtbU1XLhwockPvHMQu7fYKxVCUYO9FDmI2ihqbXUVN27cgKIouPbtt5Gfwb1zJwBFVQHbRj6fx5HDhztzkrCYsQ0hoyj/FmK8bkYLwpBlGQnDQLFUQrFQiN2h8Nz589jc3MQPP/yAb775BsnQZsXlm0hFluHyzd37ZKP8wZMDyisVABYC11QVlmXBdZyAHLyemcGzp0/x0UcfYSRmkwtZuCR63s6HPjnzt0wTPqUNhYgCgbCQ/y2O1ey5Mz09yPT04Pjx43AcB8tLS5ifn8fbd+/w4sULyLKM4ZERjGazGBoexurKCnxKMbKXwngdCnN3onlRNxCUMda5H+/evsWPt26ht68PV65ciTwu+YHbu8AuQpFleI4D27KQ6kblTSjtIO5/1OXd4+SfNnAQ7AbS6TQjB6aJdLV+C22AEILLly/jd7/7Ha7fuIFvrl1jYmpK4fo+qO9D0fXAI8e27ffGRvmDJwdCbwCwhVpVFFimGRiSrK6v48fbt3Ho0KGtvFOMEPk6z3W3uSTuBFzPY3lEQqBxDUat6TUgBRGiBM1CVVWMjY9jbHwcoBQbuRwW5uZYs6hffgH1fUiKAlVVkcvlYOh6LC1jOw2KzmgC9mKlAlC/jJFSihcvXuDu3bs4MDGBzz77bO+kj1oBISjycrhui9oqowk1l3v+ukCItwvIl6Zp0FQVtusGlspxQtU0XL16Fd999x1u3bqFK1euBJ9fWF8DW/ewkMtBew80UO/xkxYR3N8AYMxd4VoC33Xh+T5u376N/t5eXLhwoSOTuqIokAizBDZtG4kd7npo2zZACFRd39pNhCbwYBKpEDt1FIQELahPnDwJx7axsLiI73/4AdT38cc//hGaaBbFKyCMXeJJ3y008gqgvGQ0HN0Jl5JWKysVP6eUomSaAFiDGZP/m4iwdOhv8W9xFWVpjmopjxqRJur7uHf3Lp5PT+PkiRM4c/bsnkqXtIpisQi5Xm+XTqMimhAIGMWY8X1QwqyLCXZPZUwymYSTy6FYLMZODgDWX+bipUu4cf063r59G+hdhE9NGLlcDv375GBvo9I2WYT4FVWF67p49fo11tfWcO3bb2NpSVoLqqbBdByYlrU7yAGlZeWLYVe/QHC0gxO1qmlIJZPwPA+ffPIJEokE1tfWWLOoO3cAsBrx0dFRjI6MoH9gYNdMYu3cN0pZJ8rKv33fR4nXeyuKsmUbjCouky1CuIfath2Qg3YgFnqXj3uVW5YTSYLvefj5l1+wuLiIjz/6CEePHm2ZGOw1QpHP55FOp5lAeYcjiYJQEvC5kpM/AMEYizOE3w6SySQ2czm4/FnoxDw6NjaG0dFRPHj4EFevXAGlFIqub0VW+bMmfCr2OnbHN7tT4KRAaA5E6ZDCDUmePH6M8YkJDPb3d+4a+EJcLBZhmSYQt0q5CTiOExibiJSC2EmIzmllSucdLBGbX1iAqijo7euDpqoYHR0NmkUtLCwwT4XpaTx58gSaqjJb59FRjGSzzeWs40YVNXe1Rd/j+Uw/9LN6C73LI11yqL68Fsp2++Lf4ShA5e+AwJ5WVVW2qw1HHLAVhUAo2lAWXarymQEmZPO57sd2HNimiV/u3UOpVMJH589jcGgIG5ubANgulfB+JMKwLPy3+H3Fierei92G3OYmMqkUJEliouidjCCEv0sOIagVPRUUWd4VPhKSJCGZSCBfKKBYLHZsk3Xu3Dn8+u/+Dm/evcPY2BjzW+HPtLgPxfekYuHDJgd81yUWPhEdUBUFc+/eoVAs4ourVzvqakcBaDyEb9t2R84RFbbjMEdETgzCZYjhHViZwnmHCML8/Dyy2SwrVQu5S+q6jsnJSUxOTsL3fRZRWFjA/Pw8Zn/8EQAw0N/PnBpHRtDf39/x3SXlhjGu78OxbTiOE3nRr4ZgQeTVNSIqIskykobBLF/D31conN/qZxXRAsMwWsqHh8dS+DObpgmJMPtgx3Hw4507AKW4dOkS0qkUu0f8GL7vszRgg3OJexImOcI4a1dHEijF5uYmhqemWL8Vx+kuORBkgP+71u8d12WpI36f3Q45IDYDQkjQb8E0TWZk14GoRm9vL8YPHMCr6WmMj4+Xp834PbMsK/bz7gQ+bHKALdtiWZKCRY4CePnyJUZHR5HsgmpY5+TAse2g8cpOwLZtthtQVRYhaFCzHJ4Qurl7sCwLqysr+OTTTwEg2MVUTlCSJGFgcBADg4M4c+YMTNNkngoLC3jGm0Xpuh70f8hms9DaaEEdJgGe57F/e16ZB4EIF1fer/BkG170q/2sGoRbXSvlp91AuO99+FuSZBmKoiCXz+PnO3eQSiZx9YsvkAhpRkQ6yxd/h//NiYYgWuL14rn2hYcI/52IMMiyHPy9W0hDvlCA53no6+sDIYSJ3rpBvvm9iVp1IO6tpCggsgwZ5T4bOwUR1bIcB4ViEb21elO0CDHHHJuawtz8PN7OzuJUyCU37lTeTmP3zSLdhO9v6Q1CE+r0ixdwHAdHjhyBbVlbXd5ihhhCsiSxnZPrwrQspHbAftNx3WAyasXgaVu9dAcntYX5eVAAY6OjTN3NF+VGSnbDMHDo8GEcOnwYvudhdW0N83NzmF9YwMzMDAghGBgYCHpA1GpBHYUEVILwULgSWoiiLPpRsdPlZC2DUrybm8PTx4+RzWZx6fLlbeOPEAIiy2h0h7aRCO5X4rouPJ528cV355XHH3YDacjlcqAA+vr64PKW5K7ndY7sNUkKBISAW1WUIE0mTJF2imSJT5BMJGDbNqwORGEl/hkNXcfo6CiePX+OY8eOQQ6NV0FU3wd82OQAW4xXlMHZto0nT57gyJEjSOg6LMuCpuudESSGHkpN01CyLFhdJgcihBgYH2laWQi6WRYckIQOThILi4vo7+uDYRiscyQXlTZT5ibJMoaGhjA0NIRz58+jWCwyrcL8PJ48eYKHDx/C0HVks1kMZ7NM1EhIJBIgSxLbEfMFRiw2gYAwLOyMCcE43i3CywiglOLJ06eYnp7GwYMHcfGzz9oS4G0jEZRCVZQghQOeK/c5qfP4d7lbSMP6+jo0VUUikUCJRzw6Eh7n6YFWFnPRT8CnNLguIlJ7Ijq0E0SVn9MwDGBjA45td+TembyF/fGpKbx7+xZPnz3DmTNntr1uJyPAceGDJweCBYsv8snTp/ApxanTp5khieOgFLNvd4DQg6RpGmQgFiV4FAhS4HM1siOqFMItb1sgB8F7xXkqSqHajSZQ38e7+XkcO3IEAFNLu65b3tWyBSSTSRw6dAgTExOwbRvLS0tYXFrC0vIyXs3MQCIEvX19GBwYwODQEFKpFBRZrkoCal47/7tuHXmrENqQPUIOfN48aXZ2FlNTUzh54kTHrj0srhSLe1l0opI08G6fdUmDJAXfu6IoZWnJVpHL5ZDJZEDAxrXFF7g4EFQdhMWFLVxvkFIIabRESWOZRkl0t+0SxDwlvlvKowdxkgPXdeE4Dqjvo7enB1NTU3jx4gWO8rkIQOALQz2vrLPvXsQHTQ7CuUlFllEqlfDixQucmJqCoevwVBUOZ6GWqjbdnbDBycv+q2saJFnuuCixrOcB2EAOL65q6DMKUWI7u6RwjXvYI0H8rFmsrq3BsSyMjI4C2CJ1XgsTkQjbuo4TqP0Fevr60NPXh+MnTsAyTaytrmJ5ZQWvXr7EixcvkEomWank6CiGhocjTUKCFHQiprKXHBId28aNmzextrqKjz76qMwWO07Q0Niri1ZIg+/D9n2Al3gSnjKSFYX5pVSpf2+EjY0NDIn6eUUJns2WSxor0gZxEFJBVirHe+VGgoaiEp3OwVeeW9c0OI4TexRWpCpUVYUsyzhx4gSriHr6FEeFQR7fDLm+v+cX171+/W3B5Q+2xMOEjx89gizLOH78OACmBdATCZilEkzThMaFenGAVoTfNF0PclqdsN+sJ5YRVQpqxecTufC4Hu6qRIFdVOSJdH5+Hrqmob+vD0C5w2QjiDCtyENXSw9IXP+hKgrbEXLyMcrJiOe6WFpeDoSN0y9fQiIEQ8PDGOVahXSNVrs0xkm68rhh45rdjEI+j+vXr8O2LHz55ZfQdH0rJB03woSplXB3HdLgCmLJiTXl+gbhrEqALaLA/673GT3fRy6XCxYZMQ5934fjONGFslXKD+OE6CFQmcKrpc0Rv6tZAdEB6IaBQqHAUo6+HwvxdLgGxPd96IkEM4pTFJw4eRIPHz7E+Ph4ufHaXtUAhfBBkwOxoEiShFw+j5evXuH8+fOBSyIAGLoO27LgeR4s2y5v5dwOKgaPRAhUVYXFGW9c5KAyUlANokqh6jk7lEOsWenQgCjMz89jZHR0qwFTKHJQORF4nAQEZKDK5xBhYUEGGk0ksqIEEYOPKOvCNs+7St6/fx93791DOpUKfBWGh4aCiTR89nYjMmGUdWTcxWmFtZUVXL9xA6qm4Ztvv0UqncbmxgaAzpOaWFM5vPRSURRA14MIg+u6cD0PHh9rrhD58tK2MFGQFaXsMwsxYm+IWCqqGozdRuSgWgVM3BB6AxrSGwiIaptq80xVh9WYrysMneumKKWwHaftOZtyTZZPadDCXpzz2NGjePHiBaanp3Hm7Nkt/44205y7AR80OXBDKYV79+4hYRg4cvRo2WsIITAMI6if1TStYy1xVU2DZJowLQuZGrvPZlBpl1sNQpgVNrrpNqIShVKphLX1dUxNTQU/E4p/j0dcAASTcjUyoIR3cxHIQKPrTmcymMpkMHX8OFzXxdLiIuYXFvBubg4vpqchSRJGslmMjIxgeHgYyVSqvDY6BohPuZtbNb998wa3f/wRfX19uHL1arDYdTQdUpFf71h4OxRhEMuQx23ZBWHweWVUWLugSBKLLigKNtbXAUqRCZmgqYoCE6ySaBtpDj3b3dqjunyeIJJUVaBNJInl2msgcFyMeZxWCisJIdB0Hb5pwrKstsmBbdssTRLaQAkyJMsyTp86hbv37mHy0KEgjbFPDvY4ROQgn89jdnaWNXapMug1XYdimnA9D5ZpltVgt4JaD7OuaZDQvolGMGlEmAwtnlJQVLVqXlOImLoVrq5HFBYWFgC61YXR57s1y7JQNE0Wfq2wfRbNtAQZ6OTnUBQlaBZFKTO0WZifx8LiIn755Re4noeenp6AKAwMDsZSBbMbLK1rgVKK58+e4d79+zh44AA+++yzoDIonA7ZzRGPViBLEmRNC8ajGKsiteX5Plzfh8uFc0srK0gkEvBcl4n9QiJXQSyUKvetmxAtitUa+hoSYb4J6xDiSFnWItqGrsM0zbZLGn3fh8UrFBJVzMUopZg8dAhPnz/Hy+lpZHnX3vfB6+CDJgcud/qae/cOhmFgcnKS/aKCpRMAiWQSuVyORQ/aLW2sMXA0XQeRJKYBaEGEJAakqECIAocP/JpRg5h3uc2gkigsLCygt68PPqXI5fNMORwqU/N9H6qqlukFdvLae3t70dvbixMnT8K2rC2nxtlZPHnyBLIsI8sbRY2MjLRMOsMGP7sJ1Pfxyy+/YPrly+rNk8LPQQeuvcz2d4fvjSRJZcRVhOiFB0NuYwOpdBoWL2cmksQWYZ6vd2wb0g73XQnEiDVU+MKRMsqyWBbJ6UDqMq6SRkEuJK4xCCPwKyEEhyYn8eTpU7i2DSWRCEzJ9jI+WHIgDH8IIVheXsbw8HCZYK4SqqpCVVU4HSxt1FQVsiSBUNp0h8awCCnqNCjU+gSoW4nR7bKkSviUwrYsLC4tYTibRaFQCH4nSRJSySTTDqhqRzqyxQFF0zAxMYGJiQlQSrG2toY5bsB0584dUErR29vLDJjGxjDQ3x95Nx1877to9+26Ln744QcsLCzgwoULOBIq9xLo9OJdFsLeZTs5wjVGqqrC931sbGxg6vjxwAzNFykJx4Fp27AVJUhB7AQor9igqN9sSaT4mj12q1GEWu+Io6TR8zw43EclVYW4h0Xe/f39AKVYXV3F+MTEe2GE9GGTA7Cw0dr6Oo6IUpQ6SCQScBwHtm3D1fXWHtQGD4Cq6011aIwiOKwFu0FKQaCs2VKX4HGVtm3bcFwXFu862NvTwwSEvLRUND5xeMhW5B93eqe4DRULYV9/P3p7e3Hq1CnYto3FxUXMz8/j1atXePr0KVRNw0g2iyy3djbqjAW/SVLYaZilEq5fv458oYAvrl4Nyk4r0ajNdDvYltvmO9VdNy4ArK2vw3FdDA8PI5FIwOBRBVFTD0phmiZLNUgSFE4quhkZE5sIYQpVEy1cTziK0LQ2pM5r2y1pNCuEpJXnIqG51zAM6IaB5eXlfXKw1yHIwfr6OgAEuaJaIGBsVNc0WLaNUqnUkmiwsoSxErqqokhIpA6NjTrfNUJQpRBBiNiNidXjjYks7jsQRi6XAyjFwQMHtjX+kXgI1uH5XE3Ttgkxd9uiEA6/apqGAwcO4MCBA0FUYYGnIH66fRsA0N/fH1RA9Pf1lU3QuylysLm+jus3bgAAvvn6a/TwktNq6Gj5ZcUxxcK2G+IHYUJPCMHK8jJkSWK7T5RHFRKJBCiAomkyS2jCGrTZts1epyhQNK3MlrsTqOVvUImWy0Y5fN+HJMuRI5X1iIQoabRbKGkU6R7f95FskO4T5Hygvx+LS0vsZ/vkYO9CDKqVlRWk0umGA0CE7Q3DCHazZitK2AYPjW4YTHfQQEjTSje/METpVdQqBSJJHQnNur4Px7Jgi7KvEBRFgaaqUDUNs7OzSKfTNTsCKooS1J1rmrY1SXGU3StCur7Ljvpdif4OAwMDOM1bUM9zUeOL6Wk8fvwYmqYFOoVsNhtMpDutOVhcWMDN779HOpXa1jypGjoZOaiKDpXlNkIlGdi6HPbv5ZUV9PX1Vf3+CCGsMRulUA0DsiTBdRw4vKTQ5i6uBLzpFm+8Ffc9reVvUA0SIS33+hAN36JEEBrpG0RJo0eZB0VUEzvKIzUUjLjXIxVEkkC5X87g4CDevnkDs1Tar1bY6yBg5KBR1EC8FoRAkmUYiQRKpRJKxSITGsVYAigWNmG6UWnH204aIQzHtoP8oVRDYBRGnKVgnu/DtixY/DOGoSgKdE4IwqLP1eVlDA4O1jymoqqAZdW0m62cLLtNFihaC/vruo5Dhw7h0KFD8H0/aBa1sLgYNIvq6e3FwMAAJsbHgw6f3carV69w584djIyM4NKlS5Gad3WbHHS0nDGERmSg8rUry8uYPHSo5vFkWQYkCb7rwkiloKoqDMrcXR3HgctbgAuiALCKAlXTWmqitu0aRYM6SmuKEcOI49sUm596UYRG32S4pNE0zUjkgFKKYqkUiJwDEWkNYhm2pB4YHAQFsLS0tF+tsJdBKYVlWSgUChgeHo70HjHoDcOAz02Rivk8pJ6eSA9NlOEiSxITProuTNNEKiR8bDdaEIbFlf7d7Bfv8miLzYkJgMBHXuMTWbUqENfzsL6xgQOimqQKRLjT5W51jRacmmRBKK5jXLiEr/2281fWrjeAJEkYGhzE0OAgzoH5PiwsLODNmzd4/fo1pqenkTCMIP2QzWY77l1BKcXDBw/w5OlTHDlyBJ98/HHk9Ea3PA4EYj0P/+7KXC8jkIFKFAoFWJZVn/jKMgjKa+dJyIiJGsY2oiDcGkWVhNZAV1QPrutCAlhTqwjHEJqfOMoU2xVCN1vSaJpmkE5IJZORonHiGhO6jp5MBkvLy/uRg70MypWlADAcjhzUmbDDYaxEMskeQsdBPp9HJpNpXN4Y8WHRNA0lboaUSqVi7xPu+T5cHoqMungQQuCj+QmWUspcH7lPhICiKNB5HXijB3B9bQ2e72OozgQqczMk0SSnVi12LVRO5JW+8JWTXRykgbRZ551IJHD48GH0DwwwJz3bxvLyMubn5/H69WtIkoSBgYEgBdHT0xPrLt33PNy+fRuzb97g/LlzmDp+vKnj70haoUUIgic8BkjFTrIyjRUVKysroJRicGCg5mvCLqDVSpzDRAGJBFyusnd4rt3kO2eNP2/NdgsUKYVmnqm4ozTVfBGiHL+ZkkbTsuA4TqAzKLtPNUhKOHJAJAnD2Szm5ub2NQd7GZRSrK+uoqenpzzcVOcBD4sJCSFIJZPI5/NwPQ+FQgHpdDqWvK+maZAJYeYbABCzNarDWbTMuwpGhdTEA+9x8xCL244C7EHSNA1Gk5Ueq6urkCWpoQBU5V70rus2TQ5qodZOkHISGY4yVL6m7LUdBKWUCdqGhzE6Oopz586hUCiwFtQLC3j06BHu37+PRDKJEe6rkM1m2yqLsy0LN2/exPraGj6/fBkTBw60dN1Ah8hBi2Vx4e+zMgJFgdhTUCsrK8j09NQN/xNCIIfHdoNonyLLUBIJGIYRCHx9zwuEjCJSF1WbIMyPokRHw9ccJwTZaJZ0RC1ptB0HtmXB930YhrH9dTXSChQo0/wMDw/jxYsXzPFyj+ODJQe+62J1fR3jY2OR31MpgCGShFQqhVw+D9d1USwUkOL2uNvQxIDWuRmSaNkah4teGEFKoVkxZQRBl8PLMEXuE+ANrDQNumG0RJ5WVlcxMDgYqe8BYmxzWw/VSENZA6TQv7tBDviFBD9LpVI4evQojh49Cs/zgojCwuIiXr16xVIUQ0PMV2F0FOl0OvKEXsjn8afr1+HYNr786isM1InoNLhw9ncnOjLWSCsECz6lAdkP60HKnu8uRDRWVlbqphQEFFmGS1i3v6iJIpFz13QdruvCtqygV4PLnRg1Xa+bchBdKAE0FXEgaCwYbAXiGZOaOHajkkaXp3B9nmatGk2t8QwTACJGQCQJw0NDIABm37zBx59+GvEKdyc+WHKwtrYGyzSDFqmtQpJlpNNp5DY3mWq4VKo6ABuVMIYhuiOKkGCcbUd9SuFyfwOthdB7tU9AKYVl2yxfFwqnqYoC3TCgtaGejiLYCp8PQFfIQTVU1tWXLTjVFiuu6iah1zR7n8qaLtV4ryzLTIfAbafzhQIjCvPzePDgAe7du4cUbxY1OjqK4eHh6gsBBUq/vMHzpy8xYCg4/b/8EqmBvqaut+xwcWoO+GJPKGVNiMLhZ5QvVMHvOql5iADLtrG5uRl0ga0HmbslRuk+Wg0i7eB7Htslc3OfIOWgqsz5teJ7F/4GIm3XDDplnhYsyBHnVJ33xqlW0uh5HorFIkubyHLV6rN644NSGmwEJEJAFAV9/f148+ZNk59q9+GDJQdv3rwBAZre9VQTu8myjFQqhTwXF0mEbC/jikgMxKuMRAK266JQKMRKDoQYUHSGawoVn8H1vCB1EBYYarrOUgdN5jaroVAswjTNSN+TMIXxedObpj9fh0DAyWEl6HafisoqiuD9ob/DJCK8wEYlFulUClPHjmHq2DF4nofFpSXWgnp+HtPT05BkGcNDQ0H3yVQqBRQdXL0tYXD9Bdg3YQM//j2c40OQzo5AOpcFGW7OnVKkm+pGk0KLPoCycRYWA5JQFACSBITFexV/7xascr1BlLGtyDIkRBfc1oIkyzBkGbqus920bW8RBscJvFwUvkERZKSVFFSn7ndlF9JGkTmd65rcipJG3/dRLJWChb2m0VgdEuL7frAREN9JNpvF23fvuuIN00nsjtlzB/D23TsM9vU1VepTbwJWNQ1JSlEsFlEyTUj8IWsWImyWTCZRzOdZWQ2lsdWwO6LUqZUqBa5CFlECJ7SLkSUJBg9hxllvv7qyAgrUFWxtXR4TZvncRGm3kIOWQ6sVC2I1EuHzChYiSWUpDYgoTzjlUEVcKckyRrkL48cff4xcLof5hQUsLizg3r17+OWXX5BOp3H0Zwsj6xXfq+ODPlyE93AR7v8EkGwK0rkRSGdHgKlBSKoUnLvSypjyn4nxXjbB11r0w59ffCZUWYTqLBbdKmeMgpWVFeiGUdO7IwxJkkBkGcT34fl+28SbEBIIFF3PYykHXlpcLJUgmSZUTQtcVFt5ljq1MMqSFBBLIVSsJwCsVtIoShap77PeOYlES9cbENxQNGJ4eBgvpqexsLCA0RruoHsBu2P23AGsr63hyPBw8+y2DovUdT1IBRQLBdasQ1UjLQ4UWywUYCU4kiyDeh5M02xo0hQVoqdEK4I9y7JQLJXKwvaaSB10qGRudXUV6XQ6csmloihwuElMe41aY0STJYvNHTok6hOEQZwToYWzUuUdijyEryzFW1Afn5qC47pYnJ/Hg4cP0f/SBPVY/IISoHwPzx+LxTy83+bh/fYFoMkgJ4c5WciCDCTLF3lKt4Sqlfem1qIfFXXu9W4jB4ODg5E/pyLL8LhzXxxRufBxlWQS1PdhOQ4cLiIulUooFIuQFaWspLoZNFq4W0FlitbnFRz1Uhg6L2kUrrAlYVREad2SxSBSV+taOLkIj+H+vj5QSrG4uLhPDvYiaIsTdiORTSKRgO/7sG2bVTBEKHGk4Aw0LHKTpLLUQhzkwPP94EFtJkzoOA6KxWLQeIWA5fF0XYfSYcveqIItgbDfwW5Ao3EWFsO1BP59knCkoFJhX/3Cqr6mUifx6vVrFAoFKLICSG7oNQ2u2vZA783DuzcPDwDGMpDOZllU4dgAiCwF2oBqng9lz1m4fDACdsviXw+u52F1dRVnzp6N/B5Zlrd0B82KiSOA8OifrmlweYk2AQDfR75QgCFKj5t45uOmxH5ovIfRyFUxYRjY2NiAbdtbc5nwMqjzeRp9Up+PTblCoLx3kwlb+GDJgZiQWvkSG+WSkslkUHaUz+eRTqdrEoTKiEHlcQr5PArFIgZjSC2IkqSoDVtc12WRAlF5QAgShgGjS8ZJrutibWMDhw4fjvwesaPyPa+qw+SuQpVJpJZyvlb0waucDGOKUIjmSYViEV988QXo2iuQG7OtH3AuB38uB//XLwBDATk1DO9kBjjaD5LJbBNt1hK+Cv1GWEdQU7Oxi7G+vg7P95sivnIVM6ROgBACVdOgaBqM0Fxn8lJIjROIncinh1MKlahX6ijLMlRFgV0qoVAoQNM0JCq9DKpBkmp6HABbkYNtJeF7WGsg8MGSA4CHp5r8EqMIvwghSKdSyOVy8DwP+VyuagSBgu3max3P4OphJ6bUQlCv3CBq4PO8Y7i/g67rSCYSgXFPN1o4r66tRRZsCUiSFKQW3F1ADsK738qUFEWNRbDagWqFPWuF5tvARqh50tdff43e3l6s/Tmw/nAGAxsxnMd04f88B/fFHADA7euBfIalH3C4H0SuvV8TJKKaDiN4jRibofstSNduEYitrKxAliT0NWiuFoYsyyCSBI/7FjTjUdIsfM9j51AU9KTT8DwPpmXB8/3A5dTQ9a46rAKNq77qEQRCCCtbJAQ9mUwsXiiCqMRdbr4b8EGTAwK0xvAilNAQSUI6ncZmLgfP95HL5ZBKpYIBKVIJ9SaruFMLjfQG1PeZM6NpBj9TNQ1JwygTJHWrhfPqygpURUFPk90vFUUJarlbEYW2g8rFmlK6ZWJVZcy006QmjLgWvYX5efzwww9Ip9O4+sUXWwrujIbrF338efYcEs82QR8uAkWn/sHqgIarPmc34c9uwv//PQOSKsjpYUhnR0DOZEEyrYXPq93vVlOJncDKygr6Bwaa+t4IYe2SCSHwOkwObFGlwC2TBem2udupTymKPIdf1TQodM1x6TxqpRQqUfY9c9GraZqglAYt3aOQGspOWje6HFzTPjl4/9DKpNpIdyAgyTIymQwK3EUxn88jkUhA0/XID4tILbRbtUBRu+2q6DNRLJWCCVVRFCSTydpRhiZ8G1rF6uoq+vv7m/6OVEWBCbRcEx4ZoUqAyoZY4b/r3qU272NQdRDDovfy5Uv8/PPPGB0ZwaXLl6u4xAHeuWEoXx4H9Xzg1Rr8B4vwHy4As5tNncvnl7ttSi06oLffwbv9jj1ok31BqSQO9oFIDT5nvfvN71E1Ur4tvdPBMjTh3XHkyJGm3xuYIXleZDOkVuAIL5SQ0JgQwizPVRWWbcOyLLi+j3yxCEWWGUmoQljiIgf1UgrbECoTLhSL8FwXhDfJk3i5cyPtRKPlPtzrZqc7onYC++SgxfdFnTwkbvtbKBZZoyfupphIJCLtYkTVgmNZbaUWREpBkqSyHYcgBYEFqCQhmUw2ZNadVn1TSrG8soKjR482/d5AlMjb2rY7yVeW4W2boBqEOeuh3SmlLITe8kEo7j94gGdPn+Lo0aP46KOPGu6EiCwBxwYhHxuE/L8+Dbphgj5YgP9gEfTxEmA2IGb8ckm920MBvF6H/3od/n99AqQ1kDNM1EhOD4OkqozRCGRLqhL52vaOCpFkeW1Ge8jn87Bsuym9gYCwPO6k4Nbjeh1KadXNASGEpRRUlfmc8BRevlAIqpc6EWZvxkgOYCnbYrEYiAYzmQyzc+fC7IbCykZ6g9C1hOfDtkXGuwQfPDloBU3nLgkJVLGlYhEWdxJMJZONJ2FJQiKRgNNmasGpiBqEKxCC8/AKhCifLXD36xBjzufzsCyrJWteSZIgyzJzg3TdptvWBsK30P9bJkIdjq606zIomie9efMG586fx/GpqdaqeHoNkC8OQfriEIsqTK/BfzAP//4iMJfbfl5BDpqRruRt0B/ewPvhDbvGI/2sAuLcCDDRRFOpiItMNeOqOCb+lZUVEAD9Ebw7KiEqFvw2zZDqweFN2RRVra/k53OTxssEHdeF7bqw8/myyoY4rtGvo82q9RlE62VCSND3RpFl2HxeaKe3CFCeQpQkaWsuFaW4ezya8EGSg/CX2vIX2CSLpWCiPkmSUCwU4Ng2cp6HVDrdUDSXTCRQyOXaSi0IvQEBsJnLlVUgGIaBhGE0fS+iuJO1ipW1NYBS9Pf3t/R+RVHgeV4kchCeZIXYsqqjYQtouJi0ef+CHW0L12tbFm7cvIn19XV8/vnnGJ+YaOtaBIgsAccHIR8fhPzfnwVdLYI+WIT/YAH08TLgeIHmoG7koB4oBaZX4U+vwv+fHwO9BosqnMuCHh8ESXQu6F5ZRdLsvV9eWUFPT09LgjhJklh4ne/u213gqkGkFKJenyxJSCWTcLlw2vW87ZUNiJaKrYWoc41IkVrcHlpVFBiJRNDrQVYUgLezruGHyI4DRNYbdHIe3El80OSg4odN7ZiiDPawh7v4t6qqSPf0oJDPw/M85DY3mVCxThjfMAxIitJWasFxHJRKJbieF+QFwxUILaHCpjZOrC4vo6e3t2VzJUWWYaFOnwVKa0cG4iIGPO9Z93iElDVpaukcaJ4c5PN53Lh+HbZt46uvvsJAC7vYqCADSZCvDkP66jCo4wHPV+A8mAN5uQSyUIznJBsm6I0ZeDdmAIkwL4UzWZCzWWC0vKlUrCmxyhRFmGiiOjFcXl5Gdni45VPKsgzHdTtCDrxQo6VmI26KLCOdSgXN10Rlg2PbrOlah0V7wtzIdhz4lMLQtG2RUFVVAwJTD4JM1EMt+++9HjEQ2CcHbR6n4UCgFF6Fn4JoP1woFOA4Dgr5PIxEAkaNRb/d1IJl20FZpWEYVSsQWgJfVDvxMKyurra1YIXNkER+MSiD6xbLbycyFRGtkIOV5WXcuHkTuq7jm2++QSrdXE+EdkBUGTidBTmcgWIdglbwID/bYFqFZ8uAE0OJrE+BZyugz1ZA/8sjoD8BnM0ysnByEFA7WN5aQRYqnw/TNFHI5zF46lTLp5BlGRLpjBmSzVMKchvN0lRVLats8ChFoViEqqot+SNEeV5930ehWGTpFt9nuqkKchMu424kViYRIsMijbCN9LwnUYQPkhwECJvQdMDvAEDQdW/7qVkerFgqwTJNlHh+LJlMVjdECqUWmoFpmljb2GC7DF49EVdtMiGEWTzH7HlgOw7W19dx9Nixlo8hjJ5ESWMnwq+NEJmEtFGxUK1dcz28efMGP/74IwYHBnDlypXWemzEgIDUDKYgTQxC+uYoqOWBPl+Gf38B9MEisBJTVGGtBPzxNegfX4MqEuixfvinhkDOZkGyHSZGFWRheXkZPoChoaGW9QsiDdkJUaJr2/ABJNq0QxeVDaqiwDRNWLxlsuu6SCYSTUURGlkwC7M28Zp0nVStwrtbNrx3EfwUBDlQKojUfuRgDyO2EHKdASRqahshyV26SoUCbMuC53lIp1IgFYM7nFoomiaStTqIcXicSTuOE3Qo7O/ri9+0pAMPwtraGijQUuQgnCJQFAWe78PZIXIQNV0gEbLd6TAiIpdSUYqnT5/iwYMHOHjwID799NOO1sk3QrWIB9FlkLOscROlFFgswL/PKiDwfAXwYiChrg88WgZ9sAD61w+AoRSLKpzNgkwNgGidHSdz8/PozWRgJBJbUQVeWhf1SRJmSNR1YzVDcl032MzE9byI6ifFtpErFJgpXD4PwzBimYts24ZZKsGjFLIsI1mnTwLAPxd/LuttGnzfr1vK6IesvyVJ2mZu9j7ggyUHFAisL1v9Muu+t4nJXtc0KJKEfKEAz3Wxmc8jnUqVGw+FUwv5fF1yYDkOCoVCmc7BkOWOuJkRSuEj3tKd1ZUVaKqKdMRwd61qAlVRYNs2HMdBogGZ6gSijoCWJ5OKUqpa8H0fv/z8M169eoVTp07h9OnTO24G1KhdMyEEGElDHklD/gfHQE0X9MkSSz88WADWzarvi4TwOZcLwO9fgv7+JagmA8d5ROFMFmQovlbpAABKsbCwgMmDB/llVHTO5D9rpMwnhECWJHgxmyGFhYhx7341TUMPISjwhkcl04TrumzTUyeKUOvZFsZGluOAUgpVVSOJqoOKBS5WrlWqSRrM3yLyICKU4Vf7nteVlGKn8UGSA0mS0Nffz3aoTQoRK1Et597Q+KYKZEUJdAie6yKXyyGRSEDX9eD6GqUWKIBioQCT2x4rsoxEMonc5iZ7gDqxeyaE1Y3HmGdbWV3FwOBgzYer0mioFlRVZSFE12VEcJe6mLXjtREco8a9ch0H33//PZaWlvDpZ5/h0KFDLZ4tZohrj/idEEMB+XgM0sdj7HO/22QGTA8WgBdrTZFxQmqIBW0PeLDAogoAMJrmWoURkGP9IEp7i/D6+jrMUgkjVTr1hUWM4f/XIgqyooC4bmxmSJTS9tq5R4CsKEglk2y3b1lwXBduPo9EMllzbqqWUvB9P+gOSyllzaKa0F7IigLJ82qnFiKk+QJDOUHMQt/R6toaZEXZ0x0ZgQ+UHMiyjPHxcbx89Agm9whvBdV0B+30HZBCQkXbtlEqFmHbNpLJJGRFqZtacF2XRR74uQ3DQDKRCPqxS5LUscUxbkOk1dXVbeZHrYgJJUmCIsts4nPd7vvAR73WKFUNjY5f5b1mqYQ//elPKJZK+NWvfoXhbLap43cSgelWK54KhAATvZAneiH/N8dBizbo42X4DxdB788DObvxMaLEDOfzwHwe9DfToLoCnBpiEYUzWZD+5iuG5ufnoagqBhuky4gQz4r/V4kwKDE3YXI9byul0KF0k5gvdV2HoiiBQVGxWISmaTAaeKyEyxQp/3+qnotrDaiKAsuymGtitbmrwXMb1htsE3UTgsWFBfT19bVkcrWb8EGSAwCYnJzE/Z9+wtLCAg5OTrY0OVciIAbiOC0umKlUCoqqwuTsOLe5CV3XYSQS21ILFECJCxoBNtmm0+mAiQuG25GogQCfvOIIolm2Dcu2kenpiaW6QFVVuK4Lx3G6Tg4ig+edm4UIzVebUEXzJALg2tdfo6eJBj/dgKDPcYwZktRAPh0HuTAG3z8PvNkEfbDI+j+8WqvKAZpOJ1ou8Ms86C/z7H0TPcCZLGsWdaR+syiB+fl5ZLPZpkj6NqLA89uSJAVmSHHA5dFGVVU7Fw7nCzEhBLIsI51OwzRN2I4D27YDsaIQE4ZTCo7jwOQ9HXzfZ/4FDdot14IcEiVSStk9Dm3ofErr6g3CZkfCCZKEnsXllRWcv3BhP62wV9HX1weoKhaXlhg5aPGLFMyzsqa93Z208DAvlUqwLYt1QuM5MhlAkefu8sViQAB0VUUylSrbjYnQW6cFeXGlFnK5HHzfRyqViqUKQlUUlAhh+dQOlV3WQjOEqS3tS8Vnmp+fx61qzZN2CYJnBYg9mkUIAQ72ghzsBf7RcdC8Dfp4iRGFh0tAQUQV2rTlebsJvN2E/3fPgYTCyjPPZJmtc+/2+22ZJlZWV/HZp5+2fs7QMyaaIXlRrYDrQKQUKJr3NmgGBFvkBmDfVSKRgKIoQbVWvlBAgosVRYMp0zSD6wNYerWd61QUpWz37/t+MH81ozeoNqfaloV8odBS34zdhg+WHMiyjJHRUcwvLLS9aIQ9teMEIQTJZBK6pgU5NlAKy7bhuC4WlpYCk48Uf13ZdaF2s6XY0a4hEhc2rq+vgwCRxYiNoCgKC7/yyaBbVQvCgjkqWknNVFP8v5yexs+//ILR0VFcunRpR6o0GiHcDyJWslbl/pG0BnJxArg4AepTYGYd/sMlkLvzoK9X4zlvyQV+egf60zs2B0z2AWdYZ0lM9oLIEhYXFwEAIyMjsZyScFtiArbTldsg50HfFaDjLc6rjXNVVSHLcmDBXOLdHmVZhmVZoISA+j50ri1od8yofE4o80AJlwRH1BuE75V4x+LSEmRFaaknzG7D7ps5ugRZljExMYFHd+6gWCohlWxNmSyGaSeNdYRYMeyHUOT6gonxcWQymaqNTjxhmUxI+4ZHDdDKPqwaqcrn80imUrE2blFVFT6lsB1nVy6WraKMHFCK+/fv49mzZzh67Bg+On9+1wowxfcd59VFEQETiQCH+yEf7of03x6Hu14EfcijCo8W2SIfB2bWGQn5/z4DUhpwOgvL2MRQTw/0GKM4kiyDOA4z/lEUtsBX0Sc0QiBE7GRKgUMiBNW2EKLk0bZt5PN5FCwrIASapiGRycTmsChMpHywhV6kG6MYH4m0BlBdm7G4uIhRPifvdbw/M2WTEKLEH65fx9LSElItqri7WdOqGwZs14Wu6yjk80E5kOu6kKvk0x2+I+io3iCEytxdTfCJvFoJZC6Xi/3B0lQVtuOwfhItNq5qFs2OiygT07Zz8Hvtex5++OEHvH37Fuc/+ghTU1NNnr27EJNrnOSllcgL6TFArhwErhxkzaJeshbUeLjI0gZxoGADP77BEQCHCUAfXQfODANnsqxZVKMW1HUgEQLw1AIQEi4iVP0QQVwXpBR2WJPj+z5cIRIEX4gpDXrSxAlFUeA5Thk5iBJBDrsiVrum5ZUVXP3yy1ivdafwQZMDXdeRSKexuLCAw22WeHUjn13I5+G5LtNLAMgXCsjlciCEwNI0pEJiHqCLKQWOynrfSojfiW6O1e7W5uYmxsbHY70ukZ8MhxE7jab7JbQQeaJgJjD3799HLpfDlStXYr93HUE4hLtTqDg3kSVgahDy1CDw350GXSuBPlwEfbgEPF5igsR2T0kBvFxjf/4/T4EeHfR0lpGFk0Mgyeby6DKvWKgmSqysdqhVEikasBFe/99p1PrGbdsOBIeUEPSk0/B9P2i7nGyhKqEeFEWBxUtBg2sLEcxa17mthFG8F2w+tmz7vUgpAB84OQCA0bExvJmbwyU0r5ym2KpD7mRnLkopCoVCMDBTqVRg2WlaFguZ2zYc20YimWRmIOieGDF0oVWvXbR3FqhFojzeOyLuyIGY+HzullipzegEmh4JhLDe8U0smLl8Hrdv34bnefj6q69aagG8E2hkgNQKWnn26hF60p8A+dUh4FeHQF0P9MUa6MMF4MEiK3GMA5sW8P0s+yMR0CP9wFlOFsYyjQ19+BxW14lVVBJJ0lbqL3SvRNRA76AQsRJh7wLP84KGcJTSoMOjmE+F1qpYLMJIJFpuxFYJRVVBhI6rAkSS2LNYBTVLGMFSCqqq4vDhw7Fc407jgycHExMTuD0zg82NDfS2UO5V9QGOkSRQSpHnHRwJIQExUBQFGxsbUHyfVTBIEmzHYd4IlgVd1wNL1q6RAy4wC+d/faCh+lcgl8+DUtqRfJ0WKmnsBjloCU0slsvLy7h54wZUVcXVq1f3DDEAWmsWFTcImqgkUWSQk0PAySHgf3MWdLkI+miR9X94uszMk9qFT4EXq+zP3zwG+gxQkX44MQRibH+GZX7/PN+PZPK1zWDJ82CLDUQXyYGwi7YsCzbv5QBKmaWyqgbpSUIIkokESrxaQZRrx0EQglJvETkIRQ2o7wcCzTB8XhkCVNcbLC0t4dCRI00ZMu1mfLDkQNTajo6OomTbWFhYaIochKMG4njB4Gohf1wNvu8jn88H5wk3FBH/d9fWkM/ncWBiArZto1gswvU8FDc24DoOkslkdydhriyulzqohVwuBwAdIQdBw5UulTS28u1HFXXOzs7i9u3b6Ovrw9mzZ5FKpVo4286hI+SgleetwhM/KsgQa0EN3oKaPlthROHBIrNjjgPrJnB9lv2RJdCpAeD0MIssZFPs2ZIkyHwX7vk+lIjpMvGJHaG652Zh3YDQOBQKhUDcp6lqmY1yWLdECAmszwVBEELFdiDmUY+nGmVZLnv2pCpzeKVlctnnAnNG/Ozzz9u6rt2ED5YcAOxLlmUZ/UNDWFxcxIkTJ5p6f+UAidMp0PN95HO5YCFLV6lISGcy2NjcZA9NsRjYkBZ5VYNtWQAQ+I53OoIgTKAoWpv4c7kcUyd3YBejKAokQuBSCtd1O1rPDaC1RafR+KEUT548wcOHDzE5OYnjJ0/C5xGlvQQx+cd53Z0kY3WPocogZ7Jsh/+PAbqYZwZMolmUG0OzKM8HniyzP/+vR8BgEvRMFjg7DDJmBDvxZmHZNgjQFXMwyquFLN5czvV9yDwSGhZMV/tORATBJAQWt16mPNLQKkRlBvV9uFV0SNW0B16VEkaBjfV1+L7/3ugNgH1yAAA4ePAgZl68gOf7kUroKqMGZb+LgRyIzmWUO6Gl0+mqIjpZkpBKpZDb3MTG5iYSySQIz9k5tg3X8yBJEgvfWRZUTUPCMOJfGPkOgPC0ggSUaQyiIp/LIdPTE++1haCKLo2O03Fy0JJjZJ2Ik+/7+Pnnn/H61SucOn0ap0+dYlElxJu77waCUsadvu6YInxlh8ymWRvob4+CWi6e/M9/QmqmiPE1lbWOjgMrReDvXwF//wpSWgI93APv2CDouXGQoWhRJJd3dKSUBmS8mk1zu/B9nzkgcm2UGK8JXqIYnkMpr06oRRoN3ljJFBbKnCC0SjIVRQm61qJGA6bwvQjMj6qQg/nFRRjJJA4cONDStexG7JMDAEePHsWz+/fx6uVLHDt2rOH7aoWlCWFNiFpZGAU83iOBUtaCNJVK1VXX92QyrKyxVIJt21u7AEKQTiaRSCTgeR5s2w5Ei4qiMJJQ8XA2Ba4rEGY/1cJszR55Y3OzpTbNUaFqGixu09pxtGDHXevVDm+etLy0hM8++wyTvLJmN+TuW0HspYwtPm9xRA7qgSoET9V1HP9Hx4GTJ4GFPPBgiZVKTq8CXvtnl2wf5OUavKcrwH96CjqcAs5yrcLUQM1mUSKqqGta8D3EKaj2fB92haZAkiQkNA2apqFaS/so41jXdRBJYvMdF1NG6cZYDYqiwHbdICJQiXD0wONECtgeOXB9Hy+np3HuwoWOm0h1E/vkAEBvby+OTU3h0aNHOHToUMPwe6dy1q7rIp9nSmhZUZBOpRqeR1VVJBIJuJ6HjY0NDA8PA9gqb9I0DTJX6pdMExb3Rsjl85BlGYZhNO86RmnVsJsAIQQy3yVEhe/7yOVyHe0aqArdQQyWs/XQ8vioMjmXikVcv36dNU/68svg+xXnAbCzJYEtIHZS04EIQBxYXV2F47oYGR1ln3U0w/78g6OgpgM8WWE6hUdLwEZrLaglD4BG4Is1aakAfFcAvnsFaDLoySGmVTiTBRlgHh++58HmJYxandw9kSRQ3n44KlzPY6SAL9yUUiiSBI1HLAPTuIr3NUNMRKTDLJWYgROlSCQSTY8nRVEgUQqnjrOrqFyopzeYnp6GaZr48z//86bOv9uxTw7AWOHlK1fwt3/zN3j67BnOnD5d8z0+GkxqLU5UtuOgWGBiJkVRmrIP7slkUDJNFPJ59Pf1sc/Fd64iTSLxdEMykYBpmjBNE57noVAooFgqIWEYrCtavQVTVCFE+Xw8nxf1gS0Wi/B8v6POYoQQqLIMylMLu05VXDF21tfXceP6dUiShG+uXduWcmlUk71bEXcpY6u0oNMRl7n5eei6XlXoTAwV+HgU+HiUfY/vcltE4eUaq16IAIkHqFxSJVpne8C9BfYHAB1LM7fGE73AaBKKKtclyGGb6yiWwsLWHTw9oHAvmWombJV3vlm9lqaqkAhBsVhkZm9cc9XMmJIICayZa0H8TpQwVm4cLcfB48ePcfzEiT3fhbES++QA7IvP9Pfj2LFjePr0KY4dOVLT5jTKgtdsuNJz3YAYqKratPpcNwzovFRvM5dDD+9oSAjZbvZCWLOThGHAtCyUTDPoj14qlWDoOlMOV4THhNgw6oTarPdDJysVwlBVFY4oaewQOWhnDyvGztz8PG59/z0yPT24evVqVfFVZWnaXgD1/WCh2em0QqcxPz+P0ZGRxvMFIazL40QP8N9MgRZt4PEyaxT1cBHI125BLYesMXwCyPVuxVwedC4P6xZAdRnaxCBwegQ4PQRUaRbFL67ctKriXjtcZOjy79WnFKqqIqlp9SsgYhiziqIgmUwyLwTul9C0DX4UszJCapofPX3yBL7n4dN2GmrtUuyTA7CQNiUEJ0+exKtXr/Do8WN88skn217fMGoABLX+ojd6I1BKkW+DGAikMxmYloXNXA7JZBIUDfqyEwKDRwssx2HtoT0PJcuCaZrQdB0JwwhSEkJs2Aya2Q3kcjmosoxEh+2NVVUF4Q1eOpUeaieXTQjBixcv8Msvv2CMN09q2BdjL5GD0EIT273fheSgWCggt7mJ06dONf1ektSAT8eBT8dZs6g3Gyyq8HAZmFnbNrhkD3AJgS9TyA3kNLbK3i6bHtRfFoFfWEMoHOhh6YfTQ8DhPlbmWQlRqg3AsW1WecCrk6jvQ9N16JrWUl+UVvUOiqIglUwGJnEW93iJArEBanRu13XZeEW53qBYKuH5ixc4fvw4erlr7fuED5ocCH9sYW6h6TpOnjyJ+w8eYGpqqiy0Lx6AhhMaD+dLEe1zC1x8KPHKg1Yh7EVd22bmSIoSLadOCHRNg65prCTSNOHycqGSaULXNBiG0VJ/hmY9DtIdrFQQCOcMO1bS2OJERynF3bt38ezZMxw7dgzn6zVPCjtOtnS2nUHs7ojN2lR3CfPz8wBQphFpBUQirMvjZB/w354Azdss9fCQpyCKDiQePfAkoNFotvm6qVkVv3izyf783QsgoQKnhphT4+lhIM17D1SpPACYu6Km661/p22SO1mWYSQSKJVKMC0LsixHL9uOmDIR5wnj4aNHUBQFJ0+c2HVt0ePAB00OAAQ7Y8/zoEgSjk1N4cWLF3jw8CE+v3y57LXN7HSi7JpF4yQASLXZopgQgkwmA3dtDZu5HPr7+5tm8KqqsrA7Jwk2LxmywxUOTXZui1q9sZnLIRNTm+ZGUFW1oyWNrUx1nufh1o8/4t3bt/joo49wrEHzpPDY2nNpBbz/YsT5hQUMDA7G3syIpDXg0gRwaYLdy1frkB7OAS8W4c/m6r7XkQGPMDKp1oswlBzgzhz7QwBvshfO6X5YR3tBs8lgF62rKnRu1d705wj9HYMTRJkDarFUQrpBlVdwDSFxdTWUNaYKzRUbuRxmZmZw/vx5KPw+vG/YJweyzGpdPQ8Kz5OfOXMGP96+jeNTU0FpXdzTj+M4sEymUI6rRXEqncbm5mbgRd5KG2rRiyGTTsMzDFbhYFksT5/PQ5YkaLwcKWo0oVFZI6UUuc1NjGSzTV9vK9BUdUs8tQtgWRZu3LiBjc1NXLl6FSMjIw3fUzah7SFyEEQOdkE76U7dNc91sbiwgNN1hM1xgEgScHQAysEM5MIE/LwFvC6xFMSTZcAsH9825ymaw4SM9eATwFEARwXc1XXg79fh/xGQDQXaoSFop7Igp1lUpJk0atn1Iz43WYCVNPqex5o1cf1Bzd4ZobRWvbOL9KMkSWXRiPv37yORSODI4cMApfvk4H1EWJRIeMj54MGDePbsGe7fv4+vvv46YJhNgbPraqxUdBoDWClRXI6AMjdM2sjlkC8UMBp1sQ3lEsuOx6smEokETF6z7HkeSrzaQZIk6JoGTddr6huEKVI9di481rvVA11VVYBSeDxitJO1yblcDn+6fh2+aJ7U3x+04K2HTjX56jTiLmPcjXdhcXERnudhdHS0K+cLtFNJFeTKEGtB7frAq7XAV8Gbz8Hhs/22lAIHBSMDjgI4CgUomxqoDyg+kLAAddMFFueBW/Os+cDhPtAzw4woTDSZFmxQKdAsCCFIJpPIFwrwPA+maVbVMDVTAuzYTAyq8p4P8H0sr6xgfn4eFy9eZHqgfXLwfkI8WK7rAsIpTJJw9tw5XL9+HQtzcxgZG2tJvFZLmFYUJkeKgmTMArx0Og2FEORtG7bjNG6oEiFnK8tyUAbpOA4zVHIcuJ4HzzRRMk3IihLkHiujIPVaxgK8UoF2puFSLaiqCmrbsGw79u8g6oK1vLyMmzdvwjAMXP3qqyDSE0XQuBcrFYAOeBzsQpI0OzuLnp6ejrp9hhHsgimF73mQZBlEYS2oMTUI/Hen4MyvgTxZgPJ8HfLDdcBhpXkUgKtskQIKCuozQiD7gOYCqsP+vQ0+gOl19uf//Qzo1YHTw6CnhkBODQFVmkWFEe7OGBckSULCMFDkJkmyomzbfIkqKklUc9UYQx6PQgBbGwoK4N69e+jr69tyQ+Ti7vcNHzw5EI6Ctm0DXBBICMHoyAiGh4dx/8EDZIWJSbOoMvCKxeJWh8UWwv6NIEkSNF2H6jjI8cqFquDCyXp2pZUghAQpBUppQBJsy4LrOCydUSpBURSmXFZVSLxVrCSsWasgl8sBktS27qIZ6LoelDQi7gqJCAvWzMwMfvrpJwwNDeHzzz8vy2c2U+Wxt6jBljtiXGmFdiMocfZDAVhKYW5uDidOnoztmI0gyoZBCDzf316G7PuwDQJ8PAr96lEAEtxnS3CeLMJ5uQJ/tciGrM/SDZoDKC6gNNtscsMCbr4BufkGkAEcGWCixjPDwOj2Z7tT0S9VVaF7HuvDUCpBkWv4OQhCVeM4dihqIOavt+/eYXVtDb/61a9Yefd7GjUA9skB++I5g3U8DzIQLJznzp7F7777DjOvXweWtQ1RQQjCgjwh7gPQ0Ba5Vfi+j2QqhZJpsvrfatGDkJlRqzs4Qgh0XWetoZPJLXtmx4EjiALY/dU0ra7wL5fLsfvRxV2wxt3aPM+D67qxNqWqp7GgdKt50qFDh3DhwoXt4yBKHnaPWieLBSGW73oXRg3m5ubgeh4OdtljX5ZlSK5bdScu5hyARUhLjgN/3AAdnwT95iDIhgltehPqkzUoz9YAJ45mUQCer7I/f/ME6De2qh9ODAJaC6k8nwLfvwF+nGOhjY9HgV9NAsr2eVTXdfZsex4KxWLZ/BLFPMynNNAkiciD7/t4cP8+stksssPDLM1A6e4zU4sJHzw5EItciVtxBkOWUvQPDODAxAQePHiAiQMHouWm64SoSlxnYHSwQ6InWqDqOnwA65ubGAo7dwl1boyLiiRJzDPBMFgfB9F9zXVZZMFxIBECRZahcqIQXtQ2czn0dDGlIKCqKigQVGPEhhr31/d93LlzB69fv8aZs2dx8sSJlhf3vdpXIcgxx0GMYxCzxR05mJ2dRX9fH5JdbqMtSVKQvgvD41bpjmVBUVX2Wfn4VBUFqqpC6ekBDmaBa2Cuis9XmQHToyVgJaZmUWsm8KdZ4E+zoDIBpgaYp8LJAWA44r36nx6AfP9m6/8v10FvvQP+TxeBTHlVCCEEiWQyaHlvmiaSiUTVVEa179/h9tKicy8AvH71Crl8Hpd4FZtI/9WzoN7L+ODJAYCAHFiWBUPTyib2M2fP4m//9m9x+/ZtXLp0qfnJmE8+BWF0pCgdzU9R3welFJmeHhTyeeQLhTJLZcqvqVOQZRkJWUbCMOC5LiyedvA8DxalrE2sJEHjEQVFUZDb2MDEwYMdu6Za0DUtIC+JmAyRamlTguZJy8u4ePEiJicnax6DRFisglfsMXIQu8/BLoJtWZhfWMDZs2e7fm4RffI9L/AjEBVRpmkClELjintNVWuTYU3eSgUAwGJhiyg8j6dZFPEo6yvxZBnE80CHklsGTFMDgFplEza7WU4MxLHeboL+5ffA//kiMFCeHpR46rZQKDCtlCyXtaeuFbmllJYJEQFgY2MDd+/dw+TkJPq4Hba4E/uRg/cY4su1XRdU01i4ie9K0qkULl28iB9u3UImk2mpPKlUKgWCvE7vKAQrTqdSzNaUuyb2VfF37zRkRUGSiy5d12WtVi0LvufB8n32b1650Qn9RSOIRjAepZ0zRAJQKBZx4/p1mKaJL7/8EkNDQ/XfEGFHvBerFUSOFoiHHMRxD+KkKO/m5kApxYGJiRiPGh0OJ7rhEl3LsiBJEpLJZKRGbtuQTbE/3xxmpZHPV7it8zKw3lqzKAEClscny0Xg718Df/8aVJUZQRApiEG+4P88V/s4SwXQ//tNRhDGyiOQorlcyTQDg6RqEeBwIzbX8wItlqooME0TN27cQDqV2nLODT2j++TgPYYsy4H5j+u6UBUFhO+yKYADBw4gl8vh4YMHyGQyTfXsdl03YKHpdLrjYWChriWShEwmA8u2sb6+jkwq1diGt4NQFAVpThQCIaNts7Ijn1m8ra2vs9QD39l0KvUShqZpoFwr0QlysLa2hhs3bkCSZVy7di1yRUajcPeeTCvsUW+GqHgzO4uhwUEYHbYAB9hi5vJ2w67rwuVk23FdKKoKVVEgSxJbuHjTtbbHiqEA50bYHwCYy3GiIJpFNX9ICeWVOcTxWJTi0RIAgI6kGEn4ZaHuccimBfqXPwD/x8+Y/XMImqbB5doi0zQDJ9pa7qNhIaLv+7hx8yY838dXV69ClmVQft0Cehe+753APjng0HWdMW/PC1r7ItQk5tTp08jn8/jx1i2kkkn0c3OkRiiVSsHxu1FP7/O+4zKfEDY2NuB5HtY2Nsq1BzsEiZDAiTGVSLAW1YTASCRYODQkBCKEQFEUqJwodIIs6JoWCEVbaftaD3Nzc/jh1i308uZJzewwovZn2EtLrB8iNO3eZ9EIrG2SEdP3bZZKWFhcxIULF2I5XiUoJwNikRNdAgEE4mJJlqFJEtKpFFRVRalYhMSthDtiOjWWYX/+wVGg5DLjpUecLORqN4sKg0UPao90slAAFgrRjlVyQP/dLeD/8Alwqty22tB15Hn5tRAgB9ErXrEgAYH3CcDIwY8//oiN9XV89fXXgWdCQCr4eDb2IwfvN3RdRz6fZ0IU8WXz3RsBm4Q//fRT5AsFXL9+HX/2Z3+GRINQuMtZPeF1sHGLn6pBXC946U1fXx+Wl5awubmJnp6e2AyXWgYvu/L5xO55HiQA2eFhUEqDkkjHdctyp+ytW2RBVdVYyJaYJFwAruO0b3fLv9/nL17g3t27GBsfx6WLF5u/1kaphT0YOYizjDFw19slePv2LSRCMDE+HsvxqO8HC9k2MsCjmoIwK4IAyDJsx2HzDH92KKXdeeYTCvDJKPtDAbzdZP0fHi4Dr9drM912upRVO5zjgf4/7gD/u/PAhTEA7H7JsgyFR4etkACZiBJr3wckKYjyKoqCp0+f4u2bN7j8+efo7+/fOgm//2L07ZcyvufQNA2E1wkHrnkVlqCyLOPqlSv43Xff4fr167j2zTd1d7MiaqBpWnA8Hx20beVixHB0L5FIQDcMeKUSVldXMRrBmrebsEwTasg4Key0KCZGh/9dRhZKJZYT5CkIVVFaJguapsEzTVgxkAMK4O7du3j+/DmOHz+Oc+fOdWQB38tphViueZf1VJidncVwNtvy+KFc9+KFCIFYuCjvfChIgMLHemVYnAhXVt+H7XnMX6TC9rcrIGBdHg+wFtTI21tRhUfLQMEJvTRmdgCAeD7of/gFKLrArw5CkmVQ34eh68Hmo6x8WTjEhqKWi0tLePTwIU6fOYPxCsIXGCiB6aq6fn+7hPfzU7UAUdJoeh5cx2ELTShyIGAYBq5evYo//P73uHXrFq5cuVJ1sgs/4HooEtHISrgdUN+Hz33Awxjo74fFfQ+KxWJtY6QugYScyUzLgl5jQhUToQF2z8TE6XDRFfX9ICVAwMJ9qqIEOdeoO1RNVQN7aNpGbtbzPHz//fd49+4dPv7kExw7erSl4wBbTWFqhb2bsYDdLfB3IaERFuftXFMhn8fq6io+u3gx8nvC4zmIDIR0TpRSKHxhF6SgZodODomnazxK4VgWKKW7w7kvrQEXJ4DPxplXwewGIwkPF4FX6x05JaEA/p8P4C/mgf/+VBCxrIweCGoiOvNSSpHL5/HznTs4cPAgTpw4se3YEvc3AN5fMSKwTw7KoOs6zGIRjutCfOXVpoy+3l5cunwZN65fx/3793H+/Pltr9kWNQgdr1P7HbG7livCiIqqIp3JYGNzE6trazAMY8cb3whzKMs0I4XlREpB4aWglFIWduVEwXVdUM+D6XkgnCyIyUBoFmp9ZkVRIBMCnxA4jlNW7hQVlmXh+vXrWFtfx9WrV2Px1Q9CnlWwe/bM0RFnWiFOgt0uVXnz5g0kScLY2FjN11DRy0MICHnJYfj3Eie3MicElU6HjSDIg2PbjIjxyNqugPi+JAIc6mN//tEUsGkBDxZAH7JmUaQUbzM06Q+vQefywP/lEgBsix6EybXtODAtC3fu3EFvby8+/fTTqqQxvGHcFeSrQ9gnByEIFui6blnIsprj3djoKM6fP4979+4hk8ng8OHDwe+qRg0EePQgShvjZuDznQhQffLt7e1FoVCAbds7VtoYhogemJbV0gMmyoxURUECW2FZEVXwXJdNxqYJi4dbwY2YZD7xin8TQqDpOtxSCbZtN00ONjc3cePGDXieh2vXrsV3b+uEzqO4vO027NpUSGgn2ApmZ2cxOjYGRVGCck0x/kQ7eJ+nBgAEEQKJkIAICM1AOyChaJymqntjV9ujA5cngEvjrELs5XpQAUHm6regjgrybAX0hzfA5QPbogci2uJ4HhzHwd27dyFLEq5cuVJ1Hq0cu9o+OfgwoCgKZFUF5QuMoijBA1cNU8ePI5fP46effkIqlcLwMFPImrwVc2XUoFMQE48Ii1WrIZckCb09PVhdW8PGxsaOlzYCbHI0SyWkY+ipIPQHYqckSr2ccNiWV0MQxwl2DCIdQcC7Q3JCF3z3DbC0tISbN28imUziq6++inVCjhJlahRqDtBmGkI464Wb/IQjG5Xpt2rXHZvHQVyVChytRvN838f62ho2Nzdx7Ngx5LgbH0JEIHx/JElihJST2nbJQCUkQhgx9jxA0/YEORCknYBtcKSpQdCj/cD/6gTougn8366D5KNVPtTF9VngMitBD0cPRBm1bVl48OABivk8rl27VvXe0YrxD+ynFT4o6LoOm1sph4Um1aIHBMDHH3+MfC6Hmzdv4ttvv4WuaXC4YrjmwCG12zk3C59uFQKJsG0tQpLJZJAvFGBa1q4obSSEwLLtjpQCSZIUNIkCtnK8wR9OHqjvg3oeKE8peNwARdc0yJIUmKaIP+HdhGieNDw8jMuXL0NV1TJVeduoJ7qrjBw00CeUva6N6wkbGVWO30ZHFuNzWymjuHbauENocJ4uRh/CY0dEAsQ4efn6NSRFQbqnBy6vqgEhbLxIEiRJgsw9ByITuRZB+Bimvr+nFi1BziRJAuVmcZRSwPHiIQYAcps5zD17htGREWQymSB6IFILz1+8wPLyMi5dvFizm6bEReVhdLNZXLexTw4qYBgGTEliD7qoa61TgihLEj6/cgXfffcdbly/jotclNQoaiCmtnboQVmoEhEEX4SUlTZm0ukdnUREH4ZulAKFNQth+L4f5IJBKQrFIjzHAZVlOLycLBCs8d2fJEl49eoVnj9/jslDh3Dhk0+C77pe06VmUbPXPaXw+R/x/7K/K7Bbgvji80iyvP15qiQaNNRSFwiMZ4RYrx4ZahZicaKUlqUBwoSg8s6Ka5ifn8dINotEIhGQSdEhsdsQxABATZFvNxG1dDv8OvFvSZLgxyhWXDqi4/GjR3hw/z6SiQSGR0ZY+o8QrKyu4s3btzh54gTG6pSiCmM8MVZlWUZvX19s17jbsE8OKqBpGqgsw7NtZqkpws91dnG6puGLL77Ab3/7W/x05w4++uijxotum9EDr9776kxMiUQCRiIBr1jE2tpaLMK5VmHbNiTsrKhHLPbCOwFghCGTyZRHG0KLxf0HDzA/N4ejx47h8KFD2NjYgMz9EigQ7BIlrpAOWupGRLA75/Xq1b7NwH9jt+XvayBcYhslrRCkLELj3A/9btsYFwtMgwiKKA0UZb/CT8BxHPieV1YGLM4vTMUkHg0QBGBzYwPFQgGffvLJjgvTRJkvgPa9OmJC5Lmt4nWCIJA4ukMCoMcHcfR/fwmHfR/Ly8uYn5/H/MICZl69gqQooGA+K0frVBcRSSqvEKIUmd7eHRd2dxL75KACkiRB1XX4tg03pFxvlJfMpNO4cOECfrx9Gz/dvo0vv/qqcclgi7XavpgEKxBV8NXf1wezVELRNFk70x0qbTRNExSMsLRbThYHZFmGqiiwuZOaoetlam/btnHz5k2srq7ikwsXMJLNMtEZ1zX44Vx4aCIh4GVmnCjIfGcpiAkJkYjgHjQgpXuulDE8Ztu85mqLTlCK5vvwwb0BfB8erqFUPQAAjGhJREFUpaCeB0/8rMp7PUrh83SQqHIRqQDx72qE5s3btzAMI9Aa7SSsUIWOzBeynX6eooLWGuMHe1uLevUboGkd6DOAXx0ETrJeJpIksXbL2SzOU4oHDx7gxfQ0iCTh0KFDdSs7qs3/UV1y9yr2yUEV6LrOOnm5bmTluk8pMpkMLl28iHv37uG3v/sdvrh6FQMNBlC1FqL1EIRVq/0uItEQpY2bm5tYW1tDYodKGy3LAiiFoetBb/SdhqZpTLlcoYUoFov40/XrsC1rW/OkMg8G1y2rmaZCsQ62OBFK4YjJsMrkTfiCFI46iEk/HI3Yawh3Y2xm0QruIf9bRHDCP/fDi37o2CK6UiaalCRI4LoHfj+Fm6gsy5Gvj1KK2TdvWJOlHf4+qO/D4kRbtA/eUXLQ5KYnCNdX/nyyF/SLgyDXZ6u+j6oyMJ4GxnuA8QwwkQEZ7wHV6ws9fUrxy88/4+XLlxjKZtHf3w9d12veL2EsVfm8DuwCO/pOYp8cVIFhGMhLEhOsVTxktXLKLnfWymQy+LNvv8X1Gzfw+9//HpcuXWrYqKkZtbTveQ13XlEmhd7eXhSLRVg7WNooqjoMwwjUyju921FVFSiVWGc23qlNNE9SFAVff/31tuZJQs8gKwpUsXCFfu+HSILIa4d/JhY48F2uz0mE+J4D8ih0D4Qgx1uAEy5+C983QShE+D2sCA9+X/E7AGVpiij9D0QYPxAoYit0H44UCOc527YhyTJKvIXwtveGjil0FWHCKxZ7L7zo8/MRQoKFPUyiwpEZQQTE5wqeZUoDbUnUqMby8jJM02yqCVunYFkWAECRJEiqCpsT1D1DIuuQCfKPzwInhkAfLQGWCwylGCE40AMMJplvAkcUQiRapy8tLeHs2bPw+Xev1IsahFMKQBDt7EaDrZ3EPjmoAlVVQWQZlKvXwz7cAKoOZEEOVEWBbhj4+quvcPunn/D9998jn8/j1MmTNSceQpghUKNpyavCXssg0goNjgOwnWhPTw/WVld3rLTRNE2oqhpMYrsheiDLMjRFCZwX19bWcOvWLfT29uLKlStbWpLQQiImjjKRYAgSIUCNVrFhhHfHgkRQz4MrwuWhklXPdUEJCQRzbaHK5BwmCQCQy7Gac9EoK7zwB68L7d7DkykhBJbjwDZNyKoaWGXXBaVBuFks+GJnrwDBYk9CBKBZYrlNFBwiVeI+1zrm7OxsUw3YOgXf82BycqAbBmvRjPaEzm2hlVRpve+NEOCTUZALY7UFusFLa5uGAUCxUMCfrl9HyTRx9epVqKqK5eVlKJpW1ymzchwQ4L0WIgrsk4Ma0BMJ2Ja1raQRqB49ECVsYoGVFQWXLl1COp3G/fv3kc/nceHCheoLBCGQaoTWBBouAC0sqpl0Gvl8HqZpYm19vSxU3g1YFQZIBOho74moUDUNtuvi2fPneP70KSYOHMBnn322VZEg7nXMRKYWiSjTmFDmtOfySENS2D0LsZ24rtAOPhzJKCtDFO8RC3HoM4mJWLxPRFJ8z9vKz4cnUkGWxALLvSNEFMP1PMiqCk1Voel6WTSjLHoRimpI2EqzlN2PdslQDQRkoUoKhHLiRwiB73l4++YNjhw5suOaD8uyAh2Lqiis3TAXsu4IWnkmIrynrMSx6iHqRw1WVlZw8+ZNyIqCb65dg6woyOfzkFUVCh9f4Y2gQK1j7jQp7Ab2yUEN6LoOU5JgO07QqhOovnBRgJXCAWWDixCCM2fOIJ3J4PaPP6JQKODq1atVdQx1Bz46tBMgodLGXA6ZTKarpY2WZZWdj5DO9p6ICkWW8fTJE8zMzuL41FRgj71T11WWduLhc9GgSte0+BeocLifEwjH80AIQTKVQk9PT1PpBwBAsQhZkmAYRlvK/k4Rg1oIf+dClLiwuAjbcXDw4MGuXkslfM+DxbsIinsqrrHb9wlAywLraIeuf2ypIvQfxszsLH66fRt9fX24cuVKQAx83y/rWVFJDsIdbiuvpTfcpfE9xR5JSnUfhmEAsgyflznVg+u6Qd6zWmRg8uBBfPXVV9jY3MRvf/vbIERbhjqTrNip1UV4d9kERO5M5rn1bsKM2FehWxC58Zvff483b97g5MmTODo1Ff39Hbquru9NebieiPK9UDQjbAYlwvmREDJAahWVWo7Y0OQ1Tb98if7+fvT09rLFeIdIo2makICgOROwFbHZiSvqOHnm47LaeWr97OGjR/jx1i0cOHAAX331FTRdZ5oXICDZKicEbsU8K/Hme5XI9PR0rBPjgQMH8O/+3b8r+9n169eRTCbx+vXrjpyzFvbJQQ1IkoRkOg2KLeGcQGVuyw1FDUTuqhJDQ0P49ptvIEkSfve732FxcXHba6pNUQ11BhxlZ2xysuvv64MsSUFpY7dgmuY2d8SqNewdRFgvUCqV8N1332FpcRFXPv8cE+PjsC0reh+MTk2OjXKyewBBtUIbIjmRoogbzRyzUCxicX4+qIkX4WwJ3Z1MPdeF7TjwgbJnSDw/XU8rtPPdNDOG6faus7V0Ard+/BGPHz3CmTNnWBMlSYJj2/BcFwRsvqbAlr+JEKWGzlUNfR2MGnz++ee4detW6BIo/uIv/gL/7J/9Mxw6dKhj562GfXJQB+lMBpDloItaGOHh6FamFGoM9nQ6jWvXrqG/vx9//OMf8fLVq/IXVEQPWgoNtrBYiNJGRZKwurratZCkaZpVQ8yNhEXtQpgMiTyy5/vY2NjA7777DpZl4etr1zA+MQGZkz3Hjmbh2rFKi4pJas95HCDUkXEXXnMzO95XL19CUdVtVQoi9UfAJtVOf0rTNEHABNBKKFq5I2mFdqMnTb63Ul9Q+Vkt08Qf/vAHvHv7FpcvX8YJLganvh9s9HTDYBoSSlmfCz7neNX0NBXn7uug3uDKlStl5OA//If/gNnZWfyLf/EvOnbOWtgnB3UgyzI0bhAkyoUExAIW1huoEUJNGndTPHz4MH768Ufcu3u3jOULgVazIVTx2lYXqN7eXiiKAtd1sbm52dIxmoHnebBtu6rGgaBzi4hQoYcrCxYXF/H73/8emqrim2++QR9XIuu6DhACMyI56FbkYOfdIJpDuB9Dp/sLtIKoZNTzfbx89QqHJifrVp4Ez2L4T4xjQ3R9pdjuLirub1fHSJufrekqkxAZqdS8bG5u4nfffYd8oYCvv/oK4xMTwe8s3spaliTomsYqfUKpM6AxOdB1fVspc5y4cuUKHj16hHw+j0KhgH/5L/8l/vW//texNKdrFvuCxAZIZDJwcjnYtg3DMMrKsAhCegOEGh41qJeWJAmffPIJMpkM7t69i3yhgEuXLrHIAxcm+jF2nYuCcGnj+sYGMul0R0sbbb7g1hVANlF3XvMQKF+cAJQdUzRPyvLmSeF6Z11VUQRYOSHv0tnoXJ1CNcHq7tuDV0d4Z9dOWqGTOe0o9/Ld27ewLAuH69js1j4BCXZi7e7pg91vlbJQkXrpVuQgav+Eemjp3aEIiVjIFxYW8P333yPBO6QmQs6vvufB5hs8wzAAQgI/EXEPHd6lsd5n6rQQ8bPPPoMkSfjpp5/w61//GsPDw/gn/+SfdPSctbBPDhogkUigoGnwbBu2ZZVVLgBsQAHbqxQagRCCqakppFIp/PDDD/j9d9/hs4sXg11rLeFNLUS1Tq4HUdroWRaWlpc72nfBsqyqOx8BQgjQ5D0QCPclqPVuSikePXqEx48f48jhw/jkk0+27WqJJEHXdVimCdOykN7BFtc7JTKLA3FFDer5DnQD0y9fYmhoCJkWd3GVEYVWqpBcx4HPNyTViLUwehIGUp2+X3EQtlauUJg8EUmC5/t4+uQJHj58iJGREbbRqjA1ElbtiqJAUVXmJ8K7sUqKEkR2GnVV7e8wOUgmkzh//jz++q//Gn/1V3+F//pf/+uOmVntvhjfLkQilQLAFrSwOI0QEgymVtWrY2NjuHbtGjzfx29/8xvcu3sXLhfMNIU4dlWEYHBwEIokoWSaWN/YaP+YNWByl7xYSycpa+4TWOrWeJnv+7h9+zYeP36Mc2fP4pMLF2ouXKK7neM40YWJHUYcRLCbiEtv0NHP2+DYG5ubWF5extEjR2I5nRhJUpNCPtM04YONy2qLRtgdc6dLgqOilesUUdqVlRX85je/wcOHD3HixAlcvXJlGzEQtuYEQIJvRoL0AZizpHAZFWZjtdANf4MrV67gL//yL/EP/+E/xDfffNPx89XCfuQgAox0GsXNTRaaqvDcd3mr33ZKW/r6+vBnf/ZnePb8Oe7fv4+Z2VlcuHABIyMjTacX2p0+NU1Db18f1tfXg74LnfA+CFsn10LYgKYewt32Gt0rx7Zx4+ZNrK2t4fLlyw3tb0WbZ9d1YVeYNlW5kLrHags7WDLXLsKmQnsVL6enoRsGxuu09G0Fwb3h//frpNIc2w4WtXotmUXFQkctlGMcj02TPkJgWxYePHyIFy9eBPNnTzULeEqDuUbTdVaeCDATMUKC/wOMcHhgc3o1L5p0JtOVjpcff/wxVFXFv/k3/6bj56qHfXIQAURRoGsaSqUSq83XtPIBzetv24Esy6yf+Ogo7ty5g7//4x9x6NAhnD9/PlLzJ7FAxrG76unpCbo2Li0vY3xsLPZJxrKsMuvkWmi0AxLiwiglkMI+1bZtfPnllxiM2DjFMAzkCwWYtg2d94Gohlrd5WKHiBx0/kyxwI8hrdDpXXC9cea4Ll7PzGDq2LGOCSrDkQSgii6B0sAm2dD1us+NxJtLdeyO8Xu1E+OPUop3797h559/hus4+Oijj3D02LGar7c4oZIIKSNUPk8Hh++jBLAGaTtQwhjGf/yP/xH/9J/+U0w14bHSCeyTgwiQJAlGOs3yVr4PJ9TKOU74vo90Oo0vv/wSs7OzuHv3Lubn5nDu/HkcmpzsqkBxcHAQ1twcHNfF2tpa5IU0KkzTjByRIJK0Zecr9ARhBXyE+7K6uoobN25AVVVcu3atKfWvqqpByNFxXWi1okTdXsD2yE48SCvswkqFAHW+u9nZWXiui8MxpRTqXgb/W+gSBEmwbTswQ9PqNAkCuuB1EDMxiPrUlIpF/Pzzz3g3P4/RkRF88sknW6LDKtVd1PeDKjPdMMqIned5TEQeFpjLMlBHc9DJlILv+1haWsK///f/Hs+ePcN/+S//pWPniop9chARkqIwS2XThGVZZeRA5PjC/2+2w2Blo5rJyUlkR0Zw7949/Pjjj5iZmcGFCxe6VtIiKwoGBwawsrKCjc1NJJJJJGPsQuZ5XqTST6BcvLWt8iAC3r17hx9v3WL2qTXsq+uen+86TNOEbZrQan0HHY4ckBZI0W6AWKRavt4WvvOmUeO7o5RienoaI2Nj28TInYaoggJQtsg1jLYBnbtnHRjjjco8fd/Hy5cvcf/+fciKgsuXLmF8YmJb9LbyukzLAqWUlaRXPPOe74OSkKNtmExVGaeyLHe02dIf/vAH/Nmf/RlOnTqFv/7rv0ZPT0/HzhUV++QgIoiqQjcMmJYV1BmX6Qwoa14TDKsmJ8JqYjdD13Hp4kVMTk7ipzt38He//jXOnDqFEydObAtvdmIiSKZSKJkm/Hwey8vLmBgbi628MSp5ChsWeU2q1SmlePH8Oe7eu4cDBw7g4sWLLe9edV2HaVlBuVPVzoKdXsD2qOagXXfEbqRralWDrK6tYWNjA+fOnu3o+euhZJqg3BmwntYgQKdcRjuVTqhzrRsbG/jpp5+wsrKCI0eP4tzZs1Xz/qIyQ8yDwkcFQFVS53keCL+nlFLIDe5Zp1MK33zzzc70w6iDXRzn212QVRVSiIEKf+7wcCqbYJosQ6wXAhzJZvG/+PM/x/GjR/HgwQP85je/werqatXXxr2b7O/vh6aq8D0PyzXO2RJ8v2H+1q/YJTdqeRwG9X388ssvuHvvHk6ePIlLly61FdaWuQc7ISRodrPtnC0fvTnsNYpA26xW6EZ8pNY9nX7xAqlkEtmRkS5cxXZ4ngebk4NEIhH0vKi3GRD3K85xQoHY0wn14HkeHjx4gN/+9rewbRvXrl3DhQsXagoCK+c9MT9rqlp13vB9Hz54rxBJ2upmWgMfQhfGSuxHDpoAUVUYug7bsuA6zraa2Eadw2ohSj2yIss4f/48Dhw8iNs//YTvfvtbHJ2awrmzZ1ndbvgaYoQkSRgYGMDi4iIKxSJyvHtju/ApLVMKh9FK6iAM13Xxww8/YGFhARcuXGCtdWOArutweNVCooowseMTp/hu95B98m53RxSoFjmwbBtv377F6TNnduKSQAEUSyWAEGaTzLUGFCw/TsDMfWo+8zFGW1qd21rB4uIi7ty5g2KxiJOnTuHkyZORo4wETJ8h+ifUqi4SkYOAGIRQeSZZkjA4PNzKR9nT2CcHTUAyDEiWBUVV4TpO2Q5y2wCLOHELN8RIryesxfK333yD6elpPHj4EO/evcOFjz/G4NBQE5+kOeiGgUxPDzY2NrC8sgLDMIJmJa2iKiHiPgW1QpcEjKzUC7+Zponr168jn8/jiy++wEiMOz5RXSE6dYaFYd3czYdFa7sdcbgjdqNev9q9fP36NSjQ9YY3ArZlBYZH1ULjFKxzYMebLHWJGFi2jfv37uHVq1cYHBzElatX0ZPJRPYXEWmFcP+EaoQ0bIAUpQQ9OzbW9ny3F7FPDpqALMvwNQ2GriPvONv6LQDY8mmPOKCbFS4SQiBJEo5NTWFsfBy//PILrt+8ibHRURw9cqRjg7ivtxeWaaJkWVhcXsbYyEhbYXrf94Mwc7gzolBp1wIBC09XmzA2Nzdx/fp1UEpx7do19Fare24DQphYEqLUHZgwdu/euzrajhp0Q4wIrmsoOy3Fy+lpTExMdKQyqRF83iRIEINa948CgGjTTGksluPbT9J5Lc3MzAzu3rsH3/Pw6aefYvLQoWCxb2Z+NE2zrH9CNXieB9HkrlGqUiIEEwcPNvVx3hfsk4MmIRsGFNuGrChB29TKRSKq1S2llPW5b/FhTiaTuHrlCt6+fYuf79zBwvw8Dh89itOnT8c/oXH3xLm5OdiWhY2NjbasRMMComABiXwp23cyi4uL+P7775FKJnH1iy86piwXFSuu68LzvPJ+Gl3CXowcVBVwRkGXdq2V93JxcRGFQgGfXbzY8XNXQ8k0Ab54aRFKfikQzCOxjosO3n8KYGlxEQ8fPcLC4iImxsfx0ccfB6kAgu2krR4cx4Ft25AICfonVIPr+wCvYqi8nkoMDA/XNz57j7FPDpqEpCiAosDQdRRcF7Zt19ytR2K9LRCDsuYqhGDiwAH09fXhwYMHeDk9jVevXuHo0aM4fvx4rANbUVX0DwxgdXUVa+vrSCQSLR/f5+y91V1hOL3w6tUr/Pzzz1WbJ8UNSZKgqips24Zl21vlnd3M/+8hzYHfbhljl1A5Cqenp9HT04OBHRCiOa4L13GYCDHUPCgSuOpeisE+uVMWzBTA/NwcHj95gtWVFfT29uKLq1cxEurlEnWDJeB7HkqlEgDmhFhvDnAsC0SSaldeCZJFCA58oFEDYJ8ctAQ1kQB1HBbqoxSO42xj1xIhqN/Co/1carjVrKbrOH78OI4cPYr5+XlMv3iBFy9e4PDhwzh54kTzk0wNpNNpmKUS8qUSlpaXMTE+3nR6gVIK1/fbspwWYdRHDx/i8ZMnOHLkCD75+OOuiN50XYdt27Btm4V80Z28OIAyQrC7l1uGwACpiUqTMLoVjwnfy2KphPn5eXz08cddOvsWKKXBIqcbRmulw6HFrZ2dv0jzxQWfUrx7+xaPHz/G+sYGhgYH8cWvfoVsNgsvpJugTZJfSimKpVLgadCo3NOybcD3a75OnLWnrw/pDrZn3u3YJwctQFJVEFmGruvIYav9cCUIIXXTBu1OfGFmL/L3iqLg7NmzOHHiBKZfvMCz58/xYnoahw8dwokTJ2KpNBgYHIQ9NwfLcbCyuorhiGJISinTCvDSzXa89l3Pw+3btzEzM4Nz587h+PHjXdudqqq6lVay7Wi153Fil+/Cw2i3jNH3/a6ToFevXoHIMg7uwK7RtCxQXuZrtNjTJLzrjtqfZPtBSENzoqjwfB+zMzN4+uQJNvN5ZLNZfP311xji80bltUmhTU8UmKYZWCSnksm6pZ7U91lUhpDtmqEK99FGfVfed+yTgxYhJxLQXJe1DPW8qi19CQDRPrXqwhXDwycmAsJFSUKop6oqTp46hampKbx69QpPnz7F61evcODgQZw4cSJoDd0KJElC/8AAlpeWkMvnkTCMhs6NYWIAfp2t7vIty8LNmzexuraGK1euxN4MJwp0TUPR82BZVvfJAcduD9UDgNemAVK3P6FPKV6+fInJgwfbimy1AtHci1KKZJ2cedPgaYbIJjsx6Qxcz8PrV6/w9NkzFAoFjI2N4bOLF7d7BlREw5o5s8MjeABgcGIA1BYti+6qkiRt02WFX51KJtEfs2X8XsM+OWgRiq7DLRSQTCaRy+VQKBSQSCTKhVd1bG7jCkMTwfBFCNHzgp0HwGyQj01N4ciRI5iZmcGTp0/x29/8BqNjYzh16lTLOdVEIoF0JoPNzU2srK5C1/W62ovKiYk26XYokM/ncf3GDdiWha+++gqDg4OgddozdwqiEZfneXBdt+WweUvYQ5oDyr1AdntHRnF1c3NzME0zttbMUUGxJUJUVTUW3Qyt+HejMmCAzSftphMc18XL6Wk8e/YMpmVhYmICV69erW0JHJoLm3mOPdfdSsHoepkde63jmPwebyMGlAa+NbIsf7AVCmHsk4M2oCQSSJgmCoUCHMdBqVRCOpUKfk9C7LVSnBjrYlaRY6zmEyDJMg4fOYJDhw7hzZs3ePzkCX73u98hm83i1KlTLDXQ5ATe39cX9JpYXlnBWEhQBCBYtKsRIb+FtMLyygpu3rgBVdPwzTffbEUrQo2ZugUiSdB0HZZlwbQsJGPSdLxP8EOkravkqQVQ/tw8e/YMg4OD1dv/dhC2ZQUi3US7IuKwjXsIIsJIQxG8ba9pgxjYto0XL17g+fPncFwXhyYncfzEicZRRWyfHxuB+j7TGQBQFWWbMLranCMJd1NKt0X7vBCJTSYSGMpmI1/L+4p9ctAGJL5bThgGisVi0JApnMsKhnuVOupYrwVbEQrf92tOxkSScHByEgcPHsS7d+/w+PFj/OEPf8DA4CBOnTzJFvioDykvb1yYn4dpmlhfXw/SFUEaoQaaTSu8efMGt378EQP9/bhy5UpZR0eCzimr68HQdVimCcdxmCNblxwAw2VruxnhhkutRImimt/EheWVFaysrODq1atdPa/wNADYmGp3HIl0Zs3ft6pDqAHTNPGca5s838fRI0dwfGoqugia0qZ1BqVSic1zklS1bLlyPiCEwHVdOI4DCmyLHLgiaqAoGDtwYHd3EO0S9slBG5AkCWoqBSWXg6pp8DwPxWIRSk/P1q6Y5++25dLinvh4TbTIqUV5/fjEBMbHx7GwuIgnjx/jxvXr6O3txclTp3BgYiLSAqRpGvr6+rC2vh6UN6qa1nAnH1WQSCnF02fP8OD+fRw4eBCffvoplCrER+gtupmHF93ebMdBybZZnrgbELu7XU4Q2m241O1GU08eP0Zvby9GKyJgnUaJWyRLPBoVCxrdO07Y2om4FYpFPHv6FC9fvoQkyzh69CimpqYit2IPLhXNRVIt04TjugCAZB2DqIAg8HlBaBNkRdmmJ/Fcl0UUdB0jY2NNXf/7in1y0CZkXm6kaxoc1wX1fZRKJaQq+oyLaVyw2WbDaFEg8aYsTU2qhGBkZAQjIyNYWV7G4ydP8MP33+NhJoMTJ05gcnKy4eSe6elByTRRKBaxsLiI0ZGRhs5jPhoL6nzePOnly5c4ceIEzp49W/M9wu2s253NDMOAbdtwbBu+pu3vOELw26xU6CbWVlexsLiIy5cudfW8juPAdV0mQgylJNuCiAxEemnzUYRcPo+nT57g9cwMFEXByVOncOzo0ZpNkRqiiXO7rguTO9MmGpR6hsXaFIBp2wAh0Cv0HD7lHV8lCRMHDnRdiLpbsX8X2oQkSdBTqTIzJJtb66qqWhbeali90CaIJJXpHJrF4NAQfjU0hPW1NTx+8gS3f/wR9+/dw8SBAzh06BAG+vtr7lYH+vthWRZsx8HS8jJGstn6n7GBINFxHPxw6xYWFxZw4dNPceTw4YbXL6yXu7nfVBQFqqrCt22mPeiQM2M17PYlNzBA2uV6A4BFDdLpNMYnJrp2Tkpp0D1QN4zYdBlBVCnCPBC8osHrbdvGm7dvMfP6NVZ4f5WzZ8/iyJEjbS+mNKLlM/V9lIpFAKzbYqMoS+VGzOZ21JUkRjRpkmUZB3eoj8ZuxD45iAFGTw82eTtjVdPgOg6KpRJ6eIvfbcO+Q+HScK+CdnLwfTyvn8vlMPP6NWZmZjA9PY10Oo3JyUlMTk4iFd7lUApJUTA0NISFhQWYXKBYz/9A5AuroVgq4caNGyi00DyJ8MZI3UwviOiBZZowDKNrO+XdXsoYWCe3cJ3d1I9sbm7i3fw8Pvroo67eU5OXLUpteBq0Aypy/cLCvIIgeJ6H+YUFzMzMYH5uDj6lyGazuHjpEjM/a4PMlEVOI6YXi8ViUIZoRCDhFFvPiOe6cD2P6Q0q7rXrefABjI6M7Egfjd2KfXIQAxRVhWIYcE0TsizD8zz4ngfTNLeJZYLHoAMNUoTXQVgI1s4km8lkcPbcOZw9exZLy8usFPLJEzx8+BCDg4M4NDmJiYkJKJoW5OsGBwexvLKCQqEAVVXRV0P1XasT5frGBq5fvw4C4Otr12q+vxYImI9/N8VsgSkSF5Z1K3qwu6nBluagFYFd5E6lMeDxkydIJhKY7GL5muu6sPhONplOx68f4enMeghbLIfnipWVFczMzuLN7Cws20Z/Xx/OnjuHAwcOxGLHTn2fRZOaeEYty4LreSBgHgRRxobEU6yUvx+EQJNlKBXl5qJ98+Eul6/uduyTg5igpVJwTROu4yCRSKBYKMC0LKiaBkWWy2qHCSFB6VScE6DESxnFuQRzbnsXRgiGh4cxPDyMTz75BHNzc5iZmcFPP/+M23fuYGx0FJMHD2J0bAypVAqu62J9YwNrGxtQFKWsvFOg2uQ/Pz+PH374Aal0Gl988UXrJV0xmbg0g8RORA/2SOSgFR1Gt4hBoVDA2zdvcO78+a5Wm5RME4S79MWd445y7yrFiPl8HrOzs5iZmUE+n4eu6zh0+DAOTU4iU8ufoAVQUaUUCvk3ghPqgJtIJCJFLAg7eECQhE5BrYgaUErhex56+vpi7+K617FPDmJCIpVCcW0NruchqShQVJWlF4pFZKrV+daoRW4HYnILIgf85zTGxVKWZRw4cAATExMolUp48+YNZmdmcOPmTWiahgMHDmBychLpVAr5XA7Ly8tQZHl7HTLKa99fvnzJmieNjODzy5fb7rsgExK483UDKp/kXceBVSVi1BGIevVdSBIot8gG2qhW6AKePHkCVdNw+PDhrqUybMsKmqZ1suNfrU9DwIibE9IRrK6uQlEUjI+P48Knn2JoaKgj92ObYLqRKDncUEnTIokeg41R6GeWbYMC28SIouph4uDBXT1OdwL75CAmaJoGomnwTRO+57Gujbz+3bQsNglUPmwx73Al7pZYqdeXgG0/axfU92HoOqaOHcPUsWPI5fOYmZnBzMwMXk5PI5FMYmhoCL29vVhcWsLY6GiZgyLlnvmUUtx/8ADPnj7FkSNH8PHHH8fzkMZQqtUsDMNAwfNg2TYTmHV60Sakro/8TiJcNdJsFKBbn6dUKuH169c4c/YsZFmGyxeKTiLwNKC0zO43boSbslWef+7dO6YjmJ8HAGSzWVy6dAljY2PlVUYxe4dUjWLWOX5lQ6WoRKqSGDiOExhMCU2BKH32XBfJVCro87CPLeyTg5hACIGWTMKybTiOA8MwoCcSMEslmKbJ8tJiZ9/BawAhgO9vS1nEaRLke9v7TWbSaZw9cwZnzpzB8vJyEKKcmZlBMpXCwuIizp4+HTgJinz0D7du4e2bNzh//jympqZiDScL8tGtnbWmqijxXhu2IIQdxk6YP0VBkMvmFTRNvLFreoNnz55BVhQcjVAJEwcomDeA8CTpmPhNEINQ6H5lZQWzMzN48+YNbMdBf18fzp8/jwMHDtT0JSDgAt84CHaNcVpv5IYbKiUTiYZjQog7g8/Nfx7oDVQ12HiI1KvreRgfGWnam+FDwD45iBG6rsNSVdapT9ehaRoc24bneSgVi0il09vL7UT0IIZFLOxE5/l+mVmQKG+idbpERkE1YlB2DQCGh4YwPDSEjz7+GO/evsXz58/x+tUrvHr1CuNjYzh06BCo7+PF9DTMUgmXL1/uSAc0Apa68LpVvcCtbwvcLVPX9Y6ct6NmWjGhVb1Bq26KzcKyLLx8+RLHjx+HoqpdKX81eVQRhHTebptSFIpFzM7OYpbrCJKJBI4cPYrJgwejd2flC27bBKHWOK3x87KGShF1BlJlJJbPqRYvFw3IGCcGjuNATyQwODRUsy/Mh4x9chAjkskk8vk8PG7TqWkaEokE8vk8UydbVvWSpYoSxHYgyTJc36/60AU7gRYXlEbEoBKKJGHy4EGMjIzg7du3WFxawtraGr6/eRO+76NYKOA47xDZCVMooPvVC5qmocR3PIIkfoho1QCpW1Tn+YsXAICjx4515XwO77joU4p0h9IJnutiZWUFCwsLmJubw8bGBmRFwcT4OD777DMMDA62pnNqszS63rNd7YjbGipFWLirHp1vhmzHAaW0TG9AKYXtODh28iRSqdSuLwveCeyTgxghSRLS6TQ2XRclnkqQZBm6rsO0rCC9UHUgih1Tm4uYiEzUY/qtPOjthK51TcPo6ChkRUF2eBiqquLGjRtQVRVPnjwJDGiGs1lks1kMDw3FHpLvWvidi8yKxSJM3mujYxMPTyMF6ZNOgNfAi/EU9TytRg66QQ4cx8GLFy9w5MiRrQY8HRwbgggDrHdCHB0X+YGxvrGBxcVFLC4uYnl5Gb7nQTMM9Pf1YXJyEoePHIEiyzuTeuI797rjv+K6fG5BTwEofO6MdCpUJwiO47A0lSSx+87P5zgOhrJZpNPpho2hPlTsk4OYkUqlUCgU4Pk+ixQYBjRdh81FMUWeXuiYEZLIqdUjB0BzYqOQ8rxVJBMJ9PX2Yn1jA7brQtN1HOENWpZXVtgEt7CA6elpEAD9/f2MKAwPY2hoqL3qBU66Ojo9hoidzqMHfiiC1NFTo/aiSn0fPv/+/PC/xf3gKS0a+n/gdR/KWefzeVCwkjcx2QdOfJygkNC/S6USq0snhAllJYnZexMS/F0NPheqdhIvpqfheR6OT00FP+sUgaMAitzVT2pCVFf9YBSFQgFLi4tYWFzE0tISbNuGLMsYHhrC2bNnkc1mmTC2WAQhJDZiIL5fP2JasrJksebrQv/2PQ+FQoEZHckykhH8DISRU61rFnoDXdPKTOIkRcHo+Dgymcx+1KAG9slBzCCEIJPJYN3zgi6NkiQhmUwil8vBcRzYllV799Bm9ECQgygTQlSr4bj6FfT29MB1XeRyORBCUCqVoKoqxkZHg3bPJdNku6ClJbyemcGTp08hSRIGBweRHR7GyMgI+vr6WsplS2BajI5MBhXlWYauo8SNsGpGi2I4l+/7cD0PHu/r4XExakAEQiHhssiUuJ4QaQpKX8U5xOvDBCB8GQCIIBf8/2IHZzsOqO/DdRyUQh4fwbk4SagkDqJHRqcmbNd18fzZMxw6dCiSy167EKI6Sikzx2ryc1mmiaXlZSwtLGBhcRFFvugPDAzg6NGjyGaz6O/vL3sebNveWsxp6y2YqyFKdcy2sVb/xcybpYIYREm91CMGABtrgb9BaHPhuC4mue3zfqv12tgnBx1AoDPwfVi2zRqE8BBZyTRRLBbRk8kA9QZ/iwLFcNvmuq8D8z8g9XbUtH7b5VbQ398P1/OgqSpyuRxsxylrcZ0wDByanMShyUlQSpHL57G0uIjFpSU8efIEDx48gKppyA4PIzsygmw2i3TEnCEhpGv6A13TAgGa47pln7EV+NysxfM8lEyTNXtyXaiKwtJI/HU0vBCHxpAsFuDwQgyU7fqDahds7RSB8pRMOp1Gb0/PluWuOBc7YRCBEHlewzCCKJXv+6yShr/W57a1wWcUP6cUiiRBUhTIkgRZliHLciwlrq9evYJt2zh54sS239UKTbcKx3UDP/9UKhVJVOe6LlaWl7G0tISFhQVsbGwAlCLT24uxsTFkRSStzniilLJnG/G7aDbaUIiKgcjRCkEMQtbIUTUZUoPIhOs4sLm/gbBMppSit78fyVQKPT09+1GDOtgnBx0AIQQ9PT1YXV2F5XnQeR8B4cHvOg7yhQLStRTDoV1dswQhsFCOGDmol14Qk3WckAhhmgJdh+U4zAOhRhdHQgh6Mhn0ZDI4duwYfEqxtraGxcVFLC0u4ueffwb1fSSTSWRHRjDC0xCNQrdRIybtgHC//BIvZW2GHISjAZ7vw/e8wDoWYDtD1/NYyBhbC79ECCS+65YjhPBb+lz8mI2uX3zeamIvn6c4KKXlkQ7fZ61zeSme67rwsEV8CN9VKpIEWVEgceIQ9fP5vo+nT5/i4MGDHd8xUkpRKhZBeUi75mLu+2xMLy1hcWEByysroL4PPZHASDaLqePHkR0aairKQSkFfB9ShxT49aoXmiIGnCAWisWg10o6lYpEDIQDYj3ki0VQSqFpGjQeOSA8naCqaldKjfcy9slBh2AYBjRNY5a6rosEF6alUilsbm7Csm3IxSIStSapFisYZFkGCGnK0EUi2zs5Coe7TvBqiRCk0mkUl5fheR4Wl5YwMjLSUNkuEYLBgQEMDgzg9KlTcF0Xy8vLTK+wtISXL1+CEILenh6MjIxgOJvFQH9/mahJ7I7jNkeqNk2JSJHnutsiJGH4lMJ1Xbi8fW+YCATH5jlcWZahqypUbktt8PrvsEZgpxF0Y6xBTCROZgBA3JFA84At8uDxSInnefB4rtv3fTi+D9t1g88rKwoURYHCIwy1npfXr1/DNE2cPHmy6u/jGusUzJaZAtvNe3wfuVwOS8vLWFxYwNLyMhzHgaIoGB4cxEcffYTh4WGk0+mWSB0V0b4OVf/wk1SdM8T5o8Lz/daJQZRj5/MAIUjxOZYCGB8bC1K/+1GD+tgnBx1EJpPBysoKLEqD6IHoNbC5ucnyhzyiUBWkeYtlsWMUk2u1HXk1lD3sHSQGAhoXCMmEwLJtrDTo4lgNiqJgdHQUo1yvYJomlpaWsLi0hNnZWTx5+hSEECS4GLKnt5f93dODDA+NdxIiemCaZln0ICAD/I8XInLiimRZDkLqkixDkeWyidNx3Y7m5tuBJ7oxNpMGCOWoBXkIi1BFdEGQBRFlEOkW2/NQ4ouWIAqKogTjn1KKZ0+fYpyL0DoJS5SyOg5818X8/Dw2NzawvraGzVwuMHkaGBjA8akpDHPdQLvfZbCRqLBP7xTCEYRmq4EoX7wFMUhFTCWIqF/dz0YICoUCM5sKza99g4NIpFLQNG0/ahAB++Sgg9B1nRkjWRYsACIwqOk6EskkiqUSCvk8ZEK2NQQJ0KRAUSiUbR6ajkoOAE4QuJCt09A0DY7rYmhoCEvLyygUClBUFf1tND8xDAMHDx7EwYMHQSkzgVlfX8fGxgY2NjYw8/o1nvD6aUmW0ZPJoJeThl7+J+7KAp2TA8eykAOCcHml+E8sZoqqbiMCkRASH+40aChyEBWNUj1CqFg5nj3PC0gWXBc+eHte14VvmpA5WViYn0e+UMCly5eb/0ANIKo5NjY2sLa2hrXVVeTy+cAmWZIkNsb6+jB56BB6e3rQ198fe8MlSZJAeZqGonMVGAJlmpMmiUG+UIATIgbNtH9uRAx8z0O+WASECBSAZhgYGBwEAPTE2EjqfcY+OegwMpkMLMuC6brMb59PnEYiAZcb5eTyeWQkKTaXLkVRYPMQtd7kYkd5yL3TS4yqqnBsG8lkMihxXN/YCMLl7YIQgnQqhXQqhQMTE8HPbcfBxvo6Njc3WefItTXMvH4dNGlKJBLo7e1FH+/S1tPTg0w63ZJpjeu6cGwblm0znwvbDiYrscMVDZvaNcXZDaRAwONmWc1EDlqlo4Iw6LrONAxcnyEiMkIc+fTZMwwMDkJRVVi2zTxIWrhnjuMwsrm+jo3NzWAsedx4TDcMpFIpTIyPY2BwEL29vS2nCJoBwRYpC1pld+Gc4nxRzyWIgcfnmMjEoNE5+CaKUsqEwK4LQkhQITI2MQFCSJDu3Udj7JODDkO4JJZKJZR8Hyk+iIVfOPV9OK6LfC6HTCZTXbgkHoqINcaKooAQUhaujoxw6Vvz744MTdMC4V1vby8rccznsbS8zEo/O1Rmpqlq0H4aYIuS7/vI5/NY39jAJp/0X79+jWKpFDgsZnp70R9KTfT29lbtEOdzwmfZNnzPA+UkgPB6a0VRkIxoB9sQFWNht/RZ8EPpgSigiMfEiYiUgqIAIbIwPT2NQqmEM2fOBI2PSsUiFE2DxvUblQsPpRT5QoEt/py4bmxsoMQ9C4gkobenBz19fTh48CB6e3sDkSQhpCuEIPy5w/4QQUlpp7sM8rSnXEN/UAnq+8iLqgRCkIxYwcFOVZ8YhMdPnqcUjEQCkiQhOzICmUdpOp1Sep+wTw66gEwmA5OXnxnJJGTOaokkIZVKIZfPw/M85PN5ZHp62I6r2sMQrjuvA0VRQEJK8MisHluTdBxujfUg2Ltj21ASCfT398PzPBRLJSwuLiKbzXaMIIRBwBaxTCbDJo5QjwfLsoIIwyaPMrx+/TowEEolk0F0QeflqqqiQNP1YKJWVRVqMgkjkYBtmoFb2/sMETmISg46VT0ixv2zZ88wyduM244Dx7bhgqUfNotFWLYN13VRyOeRy+WwubmJjY2NrWhAIoG+nh4cPHAAvX196O3pYYt/6PNZ3AGVApFLa+PCNuMoft2dvILAQyHiPEHD4kOwFveRz1X/QsqIgW1ZrHyREKQSCeiJBHoHBuB5HhKJxH4PhSawTw66AEVRkEqlkM/nkSuV0KPrkLgoifLwdy6fh8d3sOl0urpWIGIFAyEEsqIEudeoD8Q29t9BgiCuyXEcJDjDF/qDUqmEhaUlZAcHkYohxdAItRYnXdfLogwAm+RyPL+8urqKtfV1TE9Psxxn6Hi6rsMwDKSSSSQSCRiGweqteWOu3t7eIMLTLOp+Iy36Y8QFypXyQHORg05ch2maePjwIRzHQSqVwsOHD1nUoFRi9tamyUpF+eLq+D7SqRR6e3owPj6Ovr4+9PT1NUzNua7LiAGlwVjuBiojBgLCg6JTBEVUxwTHbzBPCGLgcWKQ4nbFUaIN9T5BNcfGHHfw1LlN9cj4OOvsKEn7WoMmsU8OuoRMJgPHcWBZFnKWhYyqQgI3IpJlpNNp5HM5uK6LYrGIZCoFmZDtE33ECgZZUUCaIAcUqF7eJyaCmEmCiBzYjhP8TJIkDA8NYWVlBYViEYsrKxgGYtEgNELkkDxhPeEzmQyS6TQOcPGjx90AXc+DxRcgsRAtr6ygVCqxxjuhQ6myjAQnDwnDgJFIsH+L/yeTMDStuUhDBMvaTkJEDdilRCQHTV6vyxvziPsr/m2WSiiUSijxrpji2JRSPH78GIlEAslkEoZhoLe3F8lEAgbfTSqKwrwXQik8RdMa6iZ83w/skVVN62o+u5o2SES1OtHdUlgiV/u2ankfVBKDZCoFSZLgRmjiJoF5XNT6FH4FEfa4IykApBIJ9A0MBKLP/v7+psTZ+9gnB10DIQT9/f1YXl5mIUzbRkpRArW6LMssulAowLZtSDxnVjOCIHZoNSYAhTvneREeQnYJtSdo4aIY51QjLIUd3pZVQFglA0ChWMTS8jIopch0uDlKQ0Mo3ivDtKzAAwKSBN0wgp1lo52Q6JpXLBbheh4odzsslUrI5fNYXFpCyTQDF0HhUmgkk0gYRrCYEV7ml0wkWDoj5CIoyhvlkLtgt3ayQKjhUo2JmALbyhHdsJ9B6I8gymLhL3Ii4LrulvsjpczQJpFAMpFAb08PRkdGYBgGZmdnsbGxgW+//TZSBMrhIl6Lu096/DmVVRW6plUl2aVSKbiWbpXHkXo+HaExGCc58CmFXIMYsNNW8TyoQgxkWY5EXqUGuqdqR8hzXwNVVZFIp9Hb3w8A6O3t/WC7o7aDfXLQRUiShIGBAUYQfB+W60JXlGA3qagqEolE0NFPkmXmByDL2x+SRroDbobkcMV2vVdTIOgzXxUiWhHjjlTVNIDSoGd7GIIgEElCPp/H8uoqKKXMcrqDqEYQxG7E4jas8H3WQCeVghbqmRCl/4SsKOgfGICmaaC+j57e3m07U0opq27gi2GwO+Z/b2xsoFAqlYtNCQkiO6LaRHyXhFKQkGdCtT8K91II2xRX/k600J2dncX8wkJg5SwWdhoycrJ5nwfKPQhcTgRc14XHDZ4C6+7QTjcwe8LW5C9SMolEAsPDw8H/BVEyDION9Qqsra/jzp07+OSTTyIRA7HAK6oKRVXh8d4otm0zLUyhwJ5HXQ++d8uyWMkkpcikUi1VP7SCes2pyjRDMaIeMRDnC8L8/DoKpVJQlRAQAzROI9V1eK1B4KnvB8ZThmFgaHgYMm/e1I3U5PuIfXLQZSiKgr6+PqyursL0fYCXVQnoug7q+8FiIBEClU+cUmWaoU4EQZZlSITA48Yx1SbQAFHz1OI1MZAE4Z0fTiuEIUkShkQEIZ/HytoaqO+jtw0fhCgQd8HheWTLtgNHRUVRYCQSUKo0UooqzFL54uM4Dkql0raUCSGsaZOh6+jr66t6jHw+D9O2YfCoQbATDy3Yrvg/t2CudBv0XLfMVMixrOC9fug1wXsdBz6lePb8OXMkrDBokmWZpbIkidkFKwo0TQsIhlxBQAQJEZ4O1X4u8+hXs6AA7t27h0wmg8OHD7dwBAQLi6HrQfWJ0DFYpglZUeBYVlAu142QtYgY1LsnQVohrmiR0DlFeKnQm1CwTpSCCKZCxCB4XQ1INRb/gBTUeG+BWyVLkoT+gQGk0mlomtbx+eJ9xj452AEYhoGenh5sbm7CdF1IrhuU2gDMA8Hzfdi2jUKxiAyfND1KIaFiV1BnUVcUheXCXbcuOfBCu94bN27gr/7qr/Bv/+2/DRanv/of/0e8fPUK/+pf/SskRGe5GAiCxhfJehgaHIRECHK5HFY3NkAB9HXwgfd5KNSyrKB6Q1UUGKlUXdOaZvLmiUSC7bB5GLtpMxzCnN9URakqlourNDCMldVV/PrXv8Y3167VJC0AKyNzHQfJZLJh/j2IGsR6pcDc3ByWl5fxxdWrbe+gJVmGkUgwMzPeUdX3fWysr4OCtSLvhs4g7GNQD3FGDpr9bgghgCShkM8HFuCVxAConn6jIm1RY9w2Gs85HjVIGAbGJiYgy3IsrpMfMt7vmqpdjHQ6zRZaRUG+VGJh/RCSySRURQnc13wRjqVVOiXW2NGLh7Ke30HlI3f1yhWMjY7ib/7mbwAA/+k//Sfcv38f//yf/3N2vaFztrv8aJoGmwvH6mFgYICp+yUpMC6KG5RSlEolrG9swHEcUEKgahp6eNlawwW8iUlIFukiQlDk4fqWrjmGa4mKqCHzptwReZOwOOFTivv372N4eBgj3FY7Dggr7Ew6HZSjik+Y54thJ9FMMyOC9glXK4JG3/dRLBTgcAfQZDIZKaJCfb86MeBzTKNPXiqV4POU1aHDh6EoCgYGBvYFiG1inxzsIPr6+pgwT1WRLxTKHg7RpEmW5cCkJ9g5cHOXsoepSmWDMHep14RpW66cEPxv//E/xnfffYe/+Zu/wd/+7d/i//o//A/o5+Ke8pe2RxCEhXIU9PH6ckWSsLG5idUYCYLtONjc3GRNkrila28mg0yVXU9cEF32hAd/S6i1YHBBY7chqjaAaO6IndjTTU9PI5fP4/y5c/EfnFIUSyUmHk4m0TcwAEmW4VOKQi6HYqkUu/V4s1qfWMoYWzDT8lyXkSTXhUwIkslkVUJdedyaLZ6buAZBzlKZDAaGhoJ5dR/tYZ8c7CBE8xXFMOCDdXIr+z3vVCZJEusyFiIQovSubHGv1B1wMyTR5a4StcLPFy5cwPj4OP7zf/7P+Iu/+AtMhOyHq32GVqFpWsO0Qhh9fX2BiG9zcxPLq6stnxtg6ZRcPo98Pg+HL6ipVAqZTCYgBRKi7dqaLfWUJCnoqBgo3qOiwT0nEctd40aQE46464w79eG4Lh4/eoRDk5PorZP6aBXhSolUKsWqIzIZJlCUZdaKPZdjJjwxfLZWHC+D+aFFzUGgnWniubZtm21ufD8oy64VaQt/GkEMKi6gqbSYbVnse3Ec/P/be5MYSbLzzvNv++JLrJmxZGbkVpmsjbUlRbJIUKIA3QRRB10F6NJ97wF06zlOXwj1QRCgszANTAOtQQ9AQAKEaQnsniJZ3CqTtbCqyKqsyqzKPVZfbDd7c7D3Xpi5m7ubL5GVlfX9gEBkRoTb4ou9v33L/7t46dJxRJaYGxIHXzAiN6bZNpIokvasApV/2BTeeeAN/B5AOYpQiCAoigLNMMAmRA8Gefvtt3Hv7l1k3Np4IjNGEIyaaYUiy0tL+fPFc5u7e3tT75fxO8Cjo6N8ch5jsEwTS+32cA6f5/cnMoNIsi0r7xvnHQqL5IvItYqogbAQHofoTFgkH3zwAZIswwsvvLDQ7QJAEIYI41g6Y4rFT1FVuK6LZqMhO4T8IMhrL2axL+fUrTEYZK65CuJzXDeFxAs0fT7kSEyc1cbV5hTOSS3WLvHr1rRiqMujFUsrK9je3iajowVC4uAJwDRNLK+tQTFNBGE4tGBqmiYtWaMKAVGKIhQ/bDj2O6ibE/3000/xd3/3d/h3//7f44UXX8T//Y//WOtxs1yMTMOY6QLabrexuroKVVXR7/fx8NGj2uHcJEnyQtAgQMr9JdqtFlx+Fz+KE1lqFQUO7433pwhJy2P5As2OqmAFcTCRBYuXvufho48+wtUrVxbuNxBHkTTXcWy7cqaGrutoNptwbVt+Hr1eb+qokOjtnznywKM30z67s9YXhPx5sWxbGhyNP7yK4+PP1yxmWIdHR0gZw5WrV8cWyhLTQ+LgCcF1XTRXVqAC8Pr9IYEgPBCA/C6mP1CjIEizLL9Ii77tMWZIg4/f3d3F3/zN3+AHP/gBXn/9dfzFX/wFfvmrX+HTTz+tdxJTXmBMw5BOdtPSarWwtroKTVXhBwEe7e5OXFzDMESn20XMn4um66I1yqp6gJMK1QsfCyiKXIDqMrbvfL7DmolppjEu2nHz3XffhWVZuHrlykyPH3U0SZKgz4tGLdMcb6ajKDAtC0utFizDADQNcZKg3+/X8sFgjI1u5avJtOOaRaRgmj2mSXIcGVEUONx1srjPSi+CwaiGktvHz3K+LMvw6NEjIMuwsrKCZ65ceaxmX18F6Nl8glhaWYHZaACKgj53hitiWRZc1wWQ5/n6xSJFjigSTLlCN0TkgJshFSl+KPv9Pn74wx/i2muv4c/+7M8AAJcvX8ZLL72Ef/xv/63+SUwhENxGA2EYTlV3UKTZbGJ1bQ0az9s/GhFBYIyh3+/ng19Y7qjXarWmbkFbRBX48EbzPnkFuXipFeGp6TfxuFMLMqRd4yK9SGmwt7+PO3fu4Pnnnhsb0h5H1TOVpmk+4Q95ca9TMyKhqCoc10WTf1YzxianGRYgDAB+c4B60ZtpRIQgjuO8viBNZU1U1edolEAU+5NpkxnON0tTHB4e5tc/VcWr165N3w5MTISe0ScIRVGwtrWFvc8+QxiG8LmRiOu68kNlWRZULh5iPua4WTH6VAFk5b2qacjEnAX+IWJAyUmv0Wjghz/84dAx/fVf//WsJzPxgy/yg91uF6urqzPtpsmd6XZ3d+EHAR4+eoTTp07J1jsxzEqY/7jcVW8mlIIT4eCvZtsiAEhXviSOEQTBxFkSdff1uEc4Z1NEDhaVEmEA3n7nHbTbbeycPz/Xdkr/5wXACiC7E6aNjOmGgXazmdtl8+3Ztj0UfVhExADIRUjGI4cTw/uYXhgIAyggT6M4rjt6PwOffzHFUaYQxN9MSZqm6Pd6ODg8hKJpOH/hAs4WJqkSi4MiB08Ymq5jaX0dDr+bjEWEoPBBM/jgH5W744mRz4OIxUFVVTBFQToYPTipO8tieHHMn7WaTSgAOt3uXLtzXRfrp05BV1WEQYAHDx4gYwxRHOPo6CifY8D3N3c+uqJlFMDxwJ4ZESmjOI5njqQMIi/CjwnZxjgpTbPA992dO3dwsL+Pr3/96/NFSooLM480ZXzRnkUYCERBsWkYUPj70+MRrHxXixEGQGGuhaqOFQfTehhkXNgIYWBa1sT6guLWpUhlbKYiS0GcJPD6fXR6vdyAqtHAa9/4xszbI8ZD4uAJxHBdOK4Lt9kEeJeCMEISaJqW58v5NLRutzs0xEigGwbAWDm//zjuKPlFaNSedF1Ho9lEt9OZe1eu4+D06dPQVBVRHOP27ds4OjrKL/CqilartbDeZwUVpkBzPp8a9+1XeLX7pP0D9TzqH1diISvUuUzsVJhjgSiSZhnefecdbGxs4PTp0wvZJhhDn9/pq8gjanNbEfO+f9e2AT6RUHgCLDK6k6UpwItsRx+KMpUXQ5qm6HPXSwD5ELAJxbvAsAtiJszbZhRZURjC7/fzNGQYwrQsXL16leYmnCAkDp5AVE2DwW1Zhc9BEscyPF78u1arJZ0U+4VRtUVMPno2SVPEUYSMsRN3dCsy7kKytLSEzgLEAZDbUp8+fRpJHKPf7+PR3h5URZEiatEU7/gWkd93bBtQ8nkY4YKiB4+r7qBYjDhun4tMc9y8eROe7+PrX//6wrbp+7405nIr0nXzYHKHRSEsvTnbHQcZV28g0gjTpBNifs3JRH1Bs1lp1z0KsZ95X/MgCOAHQT6zwfdh2jbarRaefe65ubZLjIfEwROKzvN5umGgySvq0yxDr9stXVAUVZVDRhhj8DyvstVRDAsSUxAVHnF4bGFn0cc88ONWq7UwcQDki5RIuQi75XksiidRuhDPeRFUVDWv+saE1kZx0Z1rb4tl0qhmwaIGAgVBgA/efx8XL15EawHTOhnyLqAojgHGSl4Gi0Tjgl7X9XwOQb9fe6z6JDLud1JlLKRguvdLEATw+v08EsHbNOsUexZrmVghdVKnW2NoW4zB9zxEYQgFefRA13Woqornnn8+j4gSJwaJgycUVVVhLy/nRkaFC0rG8lkLxVZHYbUsqqmrWh1FEZR0b+MWq4xxK+bHdF4Kb18StNtt6Tw3Lx7v8NA1DefOnZPmUY92d3FweDj39qsopRgWcJduW1Z+cWdMjkmu2ieAWmLkcaUWivnucSwicsAAXL9xI18kFnT3GHEvA4a8/uPE7Hd52qXpunIiZZ8XHs+LiByURiOLRbrme3OwvsCwLDTq+BegLAzmFcosy+B5HsI4zotCdV2On9+5cAHnL16ca/vEZEgcPMFohgFL3BXx8LhhGHkltefJD7DAdhw0eGdDFEWleQy6rss7oYiPIQb44qaqYNxi+XGIhOJlSnQszBs98Hw/Dz0yltdruC42NjZyxzZFQefoCA8ePlzYXdogCwvfKwpcnkedp82zvMmTlwcyrTApDL8AcXDnzh3cu3sXL7/yynjfgZokSQLf86AAsCd5GcwKF+NFk7KG60JXVdm6PMvdtaCqU0G2DdZ8/YfqCxxnojkYuHmRaEus+ttp501kaYqe5yFJkvwmyXXR6/UARcHS0hJeefXVqbZHzAaJgyccw3FgilZGRUGz0ZAXL8/34fE55gLTsmR7X5Ik6Ha78sJtWRagKJVWvWL7UiScdMEi398iOhZ830fg+2BZBoeP1813oWBtbS23W9Y0hEGA+/fvz2y8NI5F3qHrug6TRxC8Koe9JzCtUGvg0gJEShhFuHHjBra2t8fO/KiLGBgERclHc5+ELz9jQMVwIUVV0Wg0pEDo1TRLqqIYuZllWNhgfUGj2Rwvkvi5jLVrFh0KMxRApmma11S5rmzp1nUd3/7ud8nT4DFB4uBLgNlsQheFQPzO0nVd2YHgDaQQdMNAs9WCxgc2iToFk9cdpElSOcZZQbmI6HGIBN0w5upYCIIAvu8jFcKgolWx1Wrh1KlT0HUdaZri/oMH6FfMqJiXRbooOrYt5y6M7F54QuyT5Z0jxqcVFvFeevvtt8GyDK++8src28qyDD3+PtA0TRqMLZwxYXYhEDQhEGaMIIh6A03TpvYQCCvqC8YNTmLIRYFowxzJlJ0YcRyj3+shYwymrqPpunlnR78PqCpeee21hdSXEPUgcfAlQFEUWEtLUDVNftgsy8pz6shHDhcjBAAq6xTiOIZpmqXCxFH7EzDegiQn7p0AS0tL6Ha7Uy8eYRTlPeNjhIHAtm1sbGzA4u2CJ1GHUBRXc29LVfP+euQ1JPPWZJxkakG875QxnQoM84uD+/fv47PPPsPXX3pp7GtdB5FbB2NQhTBY9HNUc3vCaVDjf9/3vKmjQrJTYYqogTAUEk6shmmOrC8Qr59MH4wwAys9hrHcqXUCYoCTx8/bMAxp/HZwcAAoCs5fuIDzFy7UPjdifkgcfElQVRU2FwgCwzTRbLWgKkpuhtTtwi+kGUT7kWkYstVRFAzVGStbMi85QZHQbrfR6XTySvaa207TVJrJWLZdy9xI13VsbGzkrY2Kgk6ngwcPHiy8DkEBFvIc6YYBwzRzR8xieuExtSfWJauZUpjnqOMkwfXr13H69Gmcn8MJEYDs6hHH3XDd8XfAszBlUZ6IIIj22GnmbCiqKj1Qatkm8+3LzidFyesLCk6shT8uRYbk7xfUdSIiA2EYgiG/6WnwTq1up4MkTbG8vEx1Bl8AJA6+RGiGAWtgJKmu62i327KVMQjD/EPF7zQVnoawef4wThIpDMYVu8lipoGLQEkkLIhSx8IIB8JBPN9HmqbQNK225z2Qn9fq6ipW+dCmIAwXV4dQKABTVXUhw4Vcx5EXf7FgzDSV8QRTEHWKEed9Lt59913ESYJX51wkRMQg5YZBzUbjOBS/CAYLD6dA1TS4jpOL9xrRIgbIWoaUfyYn1RskvMaiaIPcHKgvEBMSxWcdmC3yNC45ItJlfV7noKoqmq4rCyDDMES314Nl23iFZid8IZA4+JJhWBbMRqO0OIu7DlGImHLHRFGsqCiKrOAX+BWDnaqQl4SBi4OoSViEUBB5xF63e7yfMc6KYRjKtMis4eBms4nTp0/D4HUI9x48yMPMC2SaSMi4bYgLph8EuXnVDOc7rWXuNNRpY5znWXi0u4tPPvkEL7zwwlx1AUVhwBhDo9C7vwixqwBT59kH0Q0DlmFA0TR4vLWycl/iteQL+EiPA06WZfB8Xy7G4MOhGsWppIzJmoKq/UzLKCdMIVBEO7ZpmnmEk9dVZVmGg4MD6IaBrz3/PNbW1qbeNzE/JA6+hFjNJvSKu2XDNNHiUQSAjyjudGRrksU7GUxdR5pl6Bwd1bpjFoV2oyqSGWN5znNGodBqtfKOhYGixKr9JTydwBiDbdszVWYLLMvC5uZm7i2AfBE6ODiYuWK8auldxBhZwzTz9IKqwvM8eaF+nEOVxjFJHDBgZpGUJAl+/etfY21tDZcuXZrxCI+FgTjWVqHobiHCYIE2yLZt52mOimJUhlwwFp0OM54qHDVTQXQixPyzbpgmWsI4DYV6AiCPfFSf4FTnUDmyOcvg+758HVRFQbPZhOu6pZRUt9NByhjOnD+Pi+Rn8IVB4uBLit1uQ60Itam8kK3ZbEIVcxd6vdwUKctgmCaWlpdhmSZSrtCn6rEecZFQcByOlF0ONS+Wuq6j0WhUex2IFku+LZ8fq6Zp8w9RQh4KP72xgXa7DU1V0el28ejRo4XWIagzhpmLuI4D8EFbQtDNssVFywkhDIEaHgcz8Nv334cfBHjttddm3oYYvSwW0WajMfNo5yqkMFhgMaqIiEVhKO2cpTAYeC+Jz9tgzYdoC/T4Z1/RNDSaTTiOk3ubFAoMq2CjhEINsoHPj5gPIyJ+pq5L6/fiPkQ64fTmJi5dukTphC8QEgdfUkSBYuUFifdst1utkjNihw9n0jQNq2trsG0bcRwjDAJ0eTfDJMTeJg7XEeHOmu2Qbd6xMHK/qoogCBDHcT6RbYFtZ4qiYGVlpVSHcG/Bfgjz2gYrqgrXtmWniahOn2obWLxbYnHg0iKiJEX29vfx0Ucf4YXnn0ez2ZxpG2KBZCchDE4wgqPrOizThKpp8H0/FwaFfRZJk0R2XYjjCcMwH+4kooa2LaMlxQjfuM+xOia1N5aCUGJZBt/z4Ikpl/zmxeU3L4PncXBwgOX1dZw5e3bmMe7EYiBx8CVG03U4y8sjBYK4A2kWpjf2+n30+30Yug6XT1hj4K5kPMIwKYowrfOatGges6CJjoUxG0EYhsgYgzNnOmEUxTqEjNch9KaoQxj3fMiFeY6FxLQsOSMjCIKZphsuuu5gUqfCrN0taZbhrV//GivLy3jmmWdmOjYRMRB1N6L4cCHULJydB8e2ZXtjGIYj9yecTVVVRZok6Pf7CHxf+hY0Wi05MEk8F3WY9Z2apimyLJPpjIgLFFFbYJjmkEjNsgy7e3twms2FGVwR80Hi4EuObpqwl5fHXoANHsKzC1GEbrcLTdOgaRos05QhevG7cT4IReoa/4iJcCzLZH1CkXa7DY9bplYhhIGmaXP3uI9jsA5hd3cX+3PUIRQRRYHzLCmi3SxN00qny4mI3PKCmNSpMGvE5IMPPkCv18Nr167NJGik8yFjsqV31DHOdOe/oLHTI+GLuM0ndUZxPPI4U14YLCa3DrYnCoOlOvtcBBmfiyBajVVeMN3gx1IpDHZ3Ydo2ts6cwYULF2TdFPHFQeLgKcAwTVjLy6MvcnxRclwXrVYLmqYhYwxxkiD0fURRBMuyjn/Hi7fqRBGK26+LqE/ICvMcWq0WGHjHQgVRFCFLU1j8rkNUhp8Eg3UI3W4XDx89QryAOoSxxZ01UFUVbqMhowfplIuUoigL7emfWIw4w4JzeHiIDz/8EM8+95ycvTENaZKg2+vJ42rWGBxUF/m6nWDUQFqZIzcEEt4HcYVwzhhDHEXo93oIecpNtCcKw7Na+8x3PPexR2GIbr8vj1UUQVsjBlllWYbd/X1ohoGts2dx8eLFk3OqJKaCxMFTgmnbsJeWxgoEMAadRxEc24auaYCioN/vo9vrSVdFh7fOiShC3TvUWS8wLMukHfT+/v7QHUzC7Z4Z8tC63N8JVusP1iFEUYS7d+6MnQFRu2J9ToFgmSY0TYPCX7tZGGxZYxVfGY4H+oiWVfk4voClaQqmKNIrgIk8tfg+5WuUMYZfX7+OdruNq1evTn1eSZKg1+/nIkjTak0UrMVj7BAp7UNRjl1NB2pgMm6NLpwFNZ5GnOWc5z0rkc7o8foOVdPQbDZlCrBq+6JlUVVVbJ87h4sXL85cW0IsHhIHTxGm48But0ff7YtaAfACpVYLjuuCMYbO0RGOjo4qowhev58PZZl0lyoWvcK+6mIYBtbX13Hv/v1SIaMorkoZg21ZQyHJkyiyKyLqEEzDgAJgf38f9x88GFm8Oc2xKDNWg4tws4K8CjwYUzhZMrQBkBbrP0TRaOFvil9yYS+8b0rbzTIkwk5XLEaDi6ii5G56NUPbv/vd73B0eIhr165NvcAJb36GPPojnPYmMm7BnyJHPzMT9mGZJsBft4SPVw95gbHHixVt287z+VOMml7EWUlRwAuaGWMwbVvWd4w7r8PDQ2SKgjNcGMwSJSJODuoTecowXRdZliHsdkcb3/BFXFNVrK6sSCvigI88DsIQFi8eEnPu4zhGp9MpTT0ciciri4rlmndbm1tbeO+993LHND5HgmVZHj5PUxi8L7vqclNauGrtrT6iDqHT6aDT6SAMQ9y5dw8rS0totVqlBUjUVtRCUWp51Fc9TtO0fE4E8qmUhmFAE+1pyJ+PDAPP/cBiPy9poVNhrHXywJ3wqN93ul28/8EHuHLlCpaXl6c6ljiOZRRF5xGDuov6OMm76DbFih2MN07iwss0DIRRBN/3AUDW5jCW24dPnTphDGyOc0q5KE0KIlnVNJnympS6Ojg8RJKmOHP+PM5fuIClpaWZj4U4GShy8BRiN5uwWq38jnDcH/L2s+XlZRmKZCx3W/N9X7YWigFOwpO+1+tNleuue5He2tpClmV4tLsrfxZyq2fdMOTxpdlkZ8ZFX8oVJZ8lv7m5CceyoAE4PDrCg4cPSy2PUwsTLuCmrgNQlNwYibfl9fp9JFmGlEcC0knPzwIWOxFJ0usWvI0+GDAAv/7Vr+A6Dp579tmpHh5FkRQGhmFMJQwADEcOiq14Ux1JfURdAQa7OQr/lmkc3nUQRREODw8RC68A08zbHRVlsh/AYJRqxterGCkQwsDgNxINXiw76b18xM3Xts+dw87ODlZWVmY6FuJkIXHwlGI3m7AaDWmrOg5d12FZFgzDgK7rcBsNqDyl4Ps+er0eDMOQtQhxHKPb6chIQ10mXbBb/ALz4P59+bOYiwOrUL2sFC7cg14KQ5GFBd/xGYaBjc1NrPBahDiOcf/+fRwcHiLLspkX3WlazAoPgqXrSHmxWsDvKuvub97qdDH2u8qMa1refvttHHU6+IM/+IO81a1m/UYYhrlrpKKUpvnNhVi0T4JipKBCFEi3Qv7/LMvg87HkogtBUZQ8YsVrhnRdnxw1mNWzgJOMEAWtVksWEDJgojDodLvwfR9ndnZwbmcH6+vrcxwVcZKQOHiKcdptmK5b2Too4dEDh+dnRWtau9UaEglhGMIwTRlFEMKhjpug9EbAaJGgKAo2Nzdx7/59eYEUUYKxd0b8wjdYqwCcXNFiq9XC1tYWXO421+l0cO/BAznQZlpqdTHwBSVLU7A0RcYYFF3PRzvzdFBSw8iquM95GOmMOKXw+Pyzz3Dz44/x8ssvY3V1Vb52Km97G3WUYRgi8H0oAMw5hIFYqGcdmFRzJ8fbF1G3Yo2HOA6O+Mx1u105sdAwDDh8AqmmqkiSJI+qjflszCuNhSjoF0SBWRAFqqYhTVMoqjpWGDDG0OMFi9s7Ozhz9ixOnz4959ERJwmJg6ccd3kZluPkxiQjLnwKv/tw+EQ4MfbZMs0hkRCFYe6LzheEJEnQ7XbrRxGESBiRa93a2pJ3KFmW5ZXPSn33PemngGNr35MYMw3kEZdTp05hbW0NuqYhTRLcf/gQ+/v7c81nqFzgsgxpmkpnxGIBoGmaebGmoqDneUPWtaN3tiBxMPjaiJB5DbrdLt66fh1nz53DxQsXqo9RGfaHCPnddIZ8GNk8wkDhaZGTqi1QADk9sSgEKiv40xSeEAVRVOpCaLfbMAxDvqeTJAHLsrHiYNZ3fZIk6Pd6laLA4aJAnJsw9xr3+fd8H51OB9vnzmH7zBlsbW3NeGTE44IKEr8CONwDIfJ9YMRwFhV5B0MURYjjGEEQyAuuZZowDQMR/3lx8ckYg8KjCGEUwbas2v3VCiAnF4rLyvr6OnRNw4MHD3B+ZwfAfJ79xRSEuICpilIqQpunrRAAGo0GbNvG/sEB+r0eev0+PN/H6upqPhNh2mPmxyOsiVNu/VtcuAan5dmOgyRNZXFps9mcuNDN05YnalOA4denbkogiWO8+eabcF03H8U86TXgd95hEMiJorZtTzWye/AcxhYDzrbRUlGq6AiZ9P7K0hRBGEo3QSB/Xm2e7gOOixDTNJVRA0VVj8XBwL5nIUkShEFQMiMzTROWZUlBIFAU5fi9OU4YeB4Oj46wfe4ctra2yP3wSwJFDr4CKIoCZ3kZhm2X76bLfyTvUMQ89WK6QIiEdquVFy/yC4XKe92jOEaaJPA8Dx0RCq170S10Vei6jtMbG7h//37etsUmz6ifhuLFc9CMabClbxo0TcOp9XWsnzoFXdPAsgyPHj7E7u6uzM3XpTiXIhEX3zF/K85LFIQl/O6zFrMWpvH3hjrYrsY7TCbCGN66fh2B7+Pb3/pWrQE7jDH0PS/33eDtnLMKg+Id71yIWoFCVEAUAteZX5BxMdcp2Azruo5mo4HWQGtisWA4ThIgy6AbRqkLZSGRAv5+rYoUAPmioeBYcAPVEQpFURD4Pg6PjrB15gw2Nzdx9uzZGY+QeNxQ5OArgqqqcFdW0N/bQxrHSLMsz+kWFmYFx8WJQRDA8zy0Wi15dyXuaE3ThGEYiOMYfhBAR2F+AmPQGIOXpgiCABaPJEzTZrW5uYkbN24gKoRVF424XJfSG4OhXlHhXQhpT7oDdF0XhmHg8PAwn4jnefCDAKsrK2g0GhOPK2MMjPeyi/1VFSvKiEhhcROtZH0+/U7XtJJpVCUzLo7SNrkqpVCDjz7+GHc+/xzf+ta30Gy1au2v73lyPkej0YBpGEO5+rpII6cpHys+B4PvE/E7sYBP2m6apgiDAFFBOOq6DtuyRgolVVWhqSpSxqT4Ng1j9lQIT00EYVgSsKMiBUAuDEQLZJqmY2sagiDA/sEBTm9tYWNzEzs7OwsfzkWcHCQOvkKoqorG2poUCOJuR4gE8DYkx3EQRlEeYowiaVlcpEokiIUsSRLEXHyIEcN1RIJY8LY2N3GdMTx8+DDvCjjBsa3yvCoWW6CQjpA/OBYMKCzaRYGlqSrWVlfRbDSwt7+POEmwu7eHvudhZWUFRsX5SKOigbtuIcgGl5pSuqSAoeuwbTsXd0EATdcnRl6GFrsazDOmeW9vD++++y6euXIF2zVCzHEco+95eZsc9+nXijlv/r6bSSQMPqZ4F15Y9IvibHAvIrwvIlHjqBIFBhfkdaInqqYhThIkSQJV03KX0ylJkwRxHOfzGgrvt7GioFDLAxyLJICnFlEQR1mGKIqwt7eH9c1NbGxt4cKFCyQMvmSQOPiKMSgQgPyCpWqarAFQGYPrOOh7HgLfz90Bx3QYmKYJ0zQRRRGiKCqJBM/3ZcufiEpYljX2QtFoNNBut7G7u4vllZWF9ORPYpJIKFIKpxbqGESEQeSYTcvC1tYWjg4P0el2Efg+7gUBVpaX84iM3CCr1/EBlFIio47Ttm0kfAHp9/toNZvjByDNcAed8cWtJA5qFPQFQYBf/PznWF1dxYsvvjh+J4zBDwKEYZgXzXLXw6pzEeI2S9Oh98tge6ssPCykAoQAVI7/qLztKvjrXSdSkCQJwjAszUcwdR3WlBNGNT55MUvTfDhRzc8GyzJEcYyYp//kKXCDJZOPhx5EdFgMnl1RxCqF76IrodPpYG1jA5tbW7h06RIJgy8hJA6+ggiBEHQ6iPgdmQjXqrxg0bIsWZzo+35pGIqs8B64IAqRkPE7hzCKoOs6Ul5shSBAEIYwDQM2b8kaddHY3trCzZs3oVy9eqL2yINMIxKO/1QZWmCKvgvtpSXYjpNHEaIIe/v76Pf7WFtdzZ+fGjn60gW4cHyjFqWG66LLOz483x+b0pj6jpuxvBYCA+Kg4j0xuJ9f/uIXYAC++c1vjhV9jDH0+30ZurZsW3ZkjEIBH1ucZbKeQBFhfv5/kYYQ28kKC3vt91nhuWcYn2oSYftkTlFQXKQTLiTrGB8lSSI/x0V0w4DF25KrBEYpYjXw++L7VXh7CJF8uL+PKI6xxT0MKGLw5YXEwVcUVVXhLi9DN034nY4ML8oogqLAdV1pF2xZ1nEot3ChqrosqqoqF3+RmhDphzCKcuMa34dlmnBdF7bjDOWuNzc38eGHH6LX66FdlZOucZc6D4MioeruafSDlaEFxzRNbG5soNvp4Ig/p5/dvYuG40gHyuOHK3I70qdhsOaAfx91TIqqouG66PR6uUFSEMix3JV/P2ZbgxRtk0sX/gki47333sPu7i6+973vjT2WhBe2iveX22jAECZY4nUo3rUXogDCoU+07Yr3dfHIBgsoa1EQxLU6MapEgWGUPkcTURSoKNg7cxGfpCk0TatMTwGj0waarsMwjDwSOGLBlikzVL8filGzYiFxHEXYOziAbdvYOXcOm1tb2NjYIGHwJYbEwVcc03WhmSa8gwOkcXwcRWAMKvfvLxUnFqizNOu6npsmOU5+weKzGgIuEHzfh2macFwXzWZTioS1tTXouo793V1sbW4Ob/gxpBqAskgQCyib0EEwaqFVFAXtpSU4rotHu7tIuBV1r99Hk6dSND5TAkBJkDBxN6wcz60QlfFioWYDgkTVNLi2nc/N8H35WlQxTeygZJssNzBerN27exe/+93v8OKLL2L91KmRfxeGIbwgyCviRX2Bqsq8dnF/xToQoFD3IQRCjVRNHW8D6Z0xQRQwxqQALqaJTMOAPSKXX7GzvOhPbBPlz5kYbqSraml7k9IGhmGMr92pKHqtIuPFsqJLSVEU9Ho9dDodrJ46hVMbGzh79iwNUXoKIHFAQNP1PM3Q7SLiHvVCJFi2jZDfAYnixCpkqHbExbZYm+C6LqIokpX8IU9BdDsduK6LVqsFwzCwtr6O3f39keZNctuALJY6acmgAHn4HGMWiwk5fE3TcPrUKfi+j6NOB1EYSve4ZquFdrNZ6R0gkKmLQgtmcX/FPRumCSNJEMUxur2enJYnzwW8+jzLAF5UN/gayn3x/cR8fLYihAy/g2QDx6CwfLhPr9vFL3/5S2xtbeGZZ56pfN4YY/B8HwmvWdEMQ7pPFo+1Lgp/nmWUo4oxwqAo8MbWn/B6ERG6L9aEGKYJe0Quv3KfY+7YBRH3NzB4C+OsaYPSfgH5nh6HfK35OWdpisOjIyRJgu2dHayfOoXz58/XKqwknnzoVSQA8DTD0hJ0y4J/eJg7E6oqwAWC7/tjixOLoXAAYy+8g2kHz/PQ7/flAtbt9WDbNlaWl3H37l2EQZC3bI1gqEgPJysShuoSBiIJ4/YtFnQAcBwHjuPA6/dx1OkgjiL0ul30ez20ms08kjJiYZHPt8j5jolmuNwhM00S+NwgqfgaKsXXqhC1kMeM8p1slqYAYzLKI+pVlMHngN+9//znP4dlWbh27Vrle0cYN2X8HMR7oxZsvPGPKOCrem6qug5EK+KkSEpREBTFq6rkA7Gsmu270xbbJnEsRzd3ut2p0waDqGOeu0GkCGVMphEc18UWdz3c2NiY6lyIJxsSB0QJ07ahr6+jz9MMqqrmzok8etDtdtFqtSZf1Gpe9HRdR7vdRqvVQhAE6Ha78LkDnsYHy9y+dQtXrlyRkxknUapMx8kJBbndSZEEcSyMVRYfuo1G7k/Q7+Po6AhxkuCo00GXdxq0K7oNxPMgLtilwruhA1XQaDTQ6XSQ8kE+szg3CkRBnBAu6oh9M8Zw/fp19Hs9fP+P/7hk6COIkwQej1ZByY2cqv6uatt17rSBfNEc1w0iREExNVNFlqaIkgRxFJVeRwX53ANT3KlPoNh+WQcRnQiCAH3PQxRFedcGpkgbFPfP7/5ZXWGgKEh5xAIAur0eOt0u1k6dwqnTp7Gzs5M7chJPFSQOiCFUnmYIu12E/T50RUGj2US320UURXl4utmcesxwlZmPQFEUeScdxzG63S7SJIHrOPj87l20l5ZkqNR2HJh8guQkxIV4nomJdfYhzgHIC8iqznXSvIVGowHHdXPHPD7vvtPp5EWZPJIgRIJcGAtDfMTPqxYdVVXRbDTQ6/UQhSF0XR+OxtRYrLIsk7aqaiFyUCUGb926hdu3b+Mb165haWlp6PdhGML3/bxGgLcp1i3WG4pUTfp73n9fZFRKpEiWZUjiGBFvDS1i6jp004Sh6xPfW9JPBDiONIx5jLBITuJYOoX6vp8XI/IUnTRMqvscFNJdrM5jRBcCF1ZZluFgfx9pluHMzg5Onz6Nc+fOURrhKYVeVaISVVXhLC1B42kGE8gFQqeTC4RCBKHukit60SctQoZh5HMJXBfdbhfvf/ABPN+Hw8OZfc/LFzddh2nb+UXaMEYuLGKhnqYifx5UJbeiLhYv1hmdLR7bbDTgum6ebjg6QpqmOOx00O310Gq10Gw0SnfNLMukCY8Ms1cs2DpvIRUFpmqjMXxhn/D6ZDzdpGpaLg5GpI/29vZw48YNXLx4ETvnz5d+xxiD53kyT15n1PI4YVkHMU+j2JpXqrwf2FecJEiiqGRWBEC+7+pGsUTxXvH1qjqPjHchJHGMJEkqhWQcx9A1De3l5Xp36sXnbBoxpRQcQ/lXGIbY29uD22xie2sL29vbNFXxKYfEATEWkWbwDg9hMwbG7z7jKILvebC4V0FtkVC8a5qwWFqWhXPnzuHWrVs42NvDuZdfhu958i4ujiJ4QVC6YBuGkX+vuJsrtbMhv8M/sZSDOD9etzGpqHIQVVHQbDbhNhrodbt5JCXLcHh0hG63m1eDiy4FDJyHSDNUCATbtpFkGZIoygsgm82yy96YFlUgv6PVC66LVemMw8ND/PQnP8Ha2hpeeumloccLG2TwaJE9xuJZCrsFRH3UgQLFweMuFvcVf6OpKgzThKnrtYsLRVfJuKNOk0S6HQ5GJYDjTh9d1xFFETRNg2Wa+fTUcYjaiWmfMy4KFHbseMiyDN1uF0edDk5tbGCdRwsojfD0Q+KAmIiq62iuryPodMC6XYgBOH4QQFFVWKaJDJhNJIj/YvhCqigKLMvC+QsX8P777+Oll17C2vo60iRBGMcIgwBxFB0PGioIBVXToPMCLUPXh/KxYn/FToe67Vx1ENtVAKQYvlusm+ZQFQXtdhvNVutYJKQpDg4OEMYxXMdB03WhDnaRjCoIVRQ0XRc9Xune5wKh6DOhjBFuInev806FwTv6breLN954A81WC69/+9ulaE4Yhvl7hh9HqypyMXS4870exemVsoOC/1+cTxTHiKNouLCQ1xFMneoYQZZliHlkICnk8OU++YRF3TCgi8gMf5wQLSZPY4xi0Oa45oGXRAFw3Ja5v7+PDMC58+dlmyKlEb4a0KtM1MZut6GbJnBwgIwx+LzLQFVVGLqeV+JjSpHAEQvG4KKkGwa2t7fx8ccf4/cffYSXX34Zmq7D1XW4jpPf7cWxLJhMuOEPQ37nVRQGBhcLxZAwG9w/Z+5CRn4uVfMSABynAYDhHv4KiiKhy1MMWRDkRYZpitXVVTQKLpbinFhVFIEX/vW4A6HwWSh2H4xa6KTHQUVkxvM8vPHGG7BtG9/5znfyiYHg3Qi+jzRJ8jZFXUfDdSvD8vOmD/LTU+S2iu8nBXn0QFgZ+0EwsrBQG5w2OeI4qwZgFf9GvCdjbnk8eJy6puViYMwMjJgLA4VHMAaFbjFlVkybTKRCFAg8z8P+/j7cZhOn+ZjlU2P8KYinDxIHxFToto32qVNQVRUZH6rU4613uq4f98RjBpFQcYE1+EVz5+xZfPzJJ3ju2WdLkwZF2FUKBX4hFe1eYRyD8QFEqa7n3va+n9+l8Ts0jX8VGTzmWRetUXdwxS2Jws5ROXAM/O3S0hJazSYePHyITqeDJI6xv7eHTqeDpXa7ZHUtogCKouRhZlG4yAsUhcWyiCAU8+OV51Mo4isShiHeeOMNKIqC7373u7AsC4wxBGGYz0bgx2LbNqwKG+R50wfFCIE8x8K2E1Hgx/06soJoE6LRGDNDZGg/Ff/OsixvGS3saxCNC1YhBuoUMgZhiCiOZdqstH+RIhm7laGTGCkK/CDAweEh4ijCqY0NnNrYwM7OTuk9RXw1IHFATI2m62idOgWoKg52dxFFUb64tFry7nMukVDcF08PnDl3Dp/cuiUFQhVFB8CYu8UJRznR5x8lCVTkEYksyxAN7Evjk+40TZM20gBKx5/VEArSb77G3AT5mPyBAHA8A2DE36qahna7Dcs05YKXxDH29vZweHSEhnCcLIieUkhdUXKB0GyiJwRCr4eG6EIZk1oQz4+4W46iCG+88QaSOMYfff/7eccJ91QQi5BuGHAcZ0iEzSoKSpGegQhB8Y5diILSYxUFuqpCN83JhYVK2bFQUBQCGReiVdEDhacnxHtzWjvhKIryYsUkgW3buYAZOL7ajBEFYRii0+nkxb6ahu1z53Dm7FlsbW1RGuErCr3qxEyoqoqlU6eg6Tp2791DFMd5BKHVKrU4zisSxMz62DRx9uxZfPTRR7j6zDMTe7qNwl1WzHPKEa+Oz7gRURTHeY6WMblopWlaEgyqqkJTVTn6WCvkguUxojoFMU0v+9BjUb4zHbXwKKqKVruNddPEEW97TLlBTqfTgWXbaDYacGz7OJVR8GUQI5B7/T5SEUFoNMa+RpqmyTRFmqb46c9+Bt/z8L0//EM4fJpnzJ0OoeQzOswKZ83BVE6d56QkWvh3sXgKIVDlaSBEpq5psoiyskhUFPPJ//KogxACWZb3/I84RlVVpcAc10FTl1BEDbiwmKZ1UVJDFARhCJZlMAwDz774Ii5cuFD5mhFfHUgcEHPRXFmBqmm4f/t2nr/mAmGouBBlJ73iHfkkLMtCGIayc+HTW7fwzOXLtS2ThVBwAURxnLeoDdjNios+P6jScWVZVhq1qyhKLhSEYOALwhBKffe5Ogzmt5VCjYCqaVhZWcHS8jI8bsUcRRHCIEAYBFA0DQ3HQaPRkBd9kafWNC1PMfBix77nHY9GrlhAhTlVmqb42Ztv4ujwEN/73vfgOA663a7ctmmapcmbg6mZOs9NVYRAhu15NKjKWEoXYoB/lV5PoPT6i9+lWZbPKIgiJDwyMGo+g4K8UFe89uJrkQWtCT+3IAzhOk5lOmbc49k4URBF6BwdIQyC/H2aZVhZXcU3vv1t6kQgAJA4IBaA225j8/x53Lt1K19ceHh61GVMVLkzQBrDjLvoiVx1mqbY3trC737/e1y6fFma8cjcco1jNbm9rBAKYqFBkgz5y4sIQ9WxVeWUtYHFQjjQLWK5qMp1C0FSXBpFC2Sz2UQUx+j3evA8TxYe9vp9GIaBRqORzy7QtHwOQSHFkCQJPN+H6zjlu3W+X4OnZH71q19h99EjvP766zAtCx4f/62qKlzXLU+aFNuZ4lzFeyTjBX0pf84H7/gV5M+9FANjFmkG5K2laYpUCIAkQcKFwajjKQoAET1alBBgQP5eHkjl+EGQG1bxaIc1puWzcLCyMwM1RAEUBY5tY/vcOTz7wguUQiAk9E4gFoLbamHr4kXcvXlTWuI2Go2xjylGE4DjCv6qi65pmgjDEDvnz+PuvXu48/nnOHf2bOniV/x3XaGAQoFXsZhslN3uYHi/mDpIkwTFRwljG7GQiDtLRVWh8kV0niI8WTwoCg6PD1Ken8mjCYHvo9fvIwgCxHGMw8NDHB4ewuHRBMe2oet67qLY7yOOY/iKIm2WE75wimN96/p13L1zB69duwbLtuX8AllwOPi8TToZUXHPmDQCEtGBqudcG4wMFPfFUwFChGZ8ymhWEAVhFKGKwTSSOioqtADka3d84PJ3ogvH9304jQbsMVEDWd8ypkYljKJ8/Dp3pBSOpK1WC1tnzmDn0qWFiR3i6YDEAbEw3EYD25cu4d6nnyKKIiiqCkfcfdZgXNpBRA+WWi2sr6/jdx9+iLMD4qCICJmL8KosxhuDuCsUYfdiIaMQDYP3lmLhkkV8x79AhuPoA4ChyXnyWPmCBOSL0+DXKAEh7ZoHqvMH79BVxuC6LlzXRZam6PZ66Pf7SHjRoO95eXuo66LBv/r9PiLeZeA4DjIeJdE0De+9+y5u37qFF154AUvLy7Lg0OYCow7iecl4Hl98pXwk8OB5ioiAEFjCGTJJEll0KrYxqetDpCFE0anOxYB4vk+KoVdwzDGKLg+Dd9RUzZsoigLx/0GEKAh8X4pJ23GwvLQEXdexdeYMts6dm+e0iKcUEgfEQnEbDWycP48Ht28jDEOkaQrXdY9NdsZUwQtKaQd+QVOU3E8+NAxcOH8ev/r1r/Hw4UNsjLFwFZdKpSIMXSvfLRalwoI32K6W8jvUwbHJipJP5xMeBsX9yfPjCxnLMnlnPu5YFCW3ZVYKi5jYf5qmsgBwMPpSHNqk6TqWlpextLSEIAzh9Xq5B0Gaotvp5EWMlgXTssB4q6qiqnk+njHcuXMHt2/fxtWrV7G5uQkAecHhQMhbpFyyNM3bBge+FxGLW9FUSRXRFVEoyoVAMcUxtptDRGgKAkvj31M2bN60aEqpAgxHnMaR8Oc9CILcQdK2j4+12L44ZpshtzgPRKqnIAoMXtS4c+kS1si7gBiBwmYtpyaIMXieh4eff47I96EgFw1i0M+seXiFj9/tdrv4+S9+AcMw8Iff+97MxygjCzNvIadYLS8iDYJowHlPoABQNO04lz/Qisf/MfbYGGPodDqAogwVgZa6QgZEQ+l3vOjQ8314/T6SOJa2u2maQtU0OJaFnufh+vXrAICdnZ184A6/oxXiKEvTfOEtpALEXbz8XjjP4sItvxfz+SNEwLiFv/izUUQjZhfMS+n5nuOy2vd9HB4cIM0yNJtNNPgsjTpmWWEYotvrIfA8+ZpLUWAYeRpD03DpyhW0l5dnPkbi6YciB8SJ4Louzl2+jAd37qB/eIherwfbtivTDHXFghgwZJgmLly4gN/cuIG9gwOsrazMdIzKwHfpLle4CNcREKqqwjRNFBu/RCpC1/XciInPV2A8bM4AOe1uFsSxitByJiIHRZFRegAr2UQP3RMwBtuyEGsawiCAHwR5u2eSYDeOsbu3BwZgqdXKx2v7PjRdhx8E8vHFdIaCwuLPBzUVIwIymsGOZzmIyIdaWOSLtRmyZmOOO34xNnvWLQw9riqCMYcwSLMMPT791LJtOXdiVJQkyzI53dIPQyBJwPhz6zgOllotGdVhjMFpNHDx8mXYZGpETIAiB8SJwhjD4f4+9h88QBrH0DQNjYJV70zbBNA5OsIbP/0pGGP4kz/5ExgnVDRW3Oco4TBugBNjDNFA8VsGXl/BBUOxYK74vVisOYp+r4eUMbiOA13XhxZ/OV1v4JjE98EoRTEN4gcBet0u7t+7h/3DQwDAxuYmltptGIYBy7Jg2TYc08wXcl2Hxu/+xZ28EAzFxb/479LvHgMZY0NtrINUCYB5Fvw6iK0fHBzIaNDq2hqsCq+BLMvgBwEC35f+BMX3pj0gCsAYoKrYPHMGm9vbVHhI1ILEAfFYCMMQD+7cQdjtggF5v31FkVUxijAuopBmGe7evYs333wTZ8+dw7XXXpPtckph0XkcFD9AshCSX6wz5KmFWRYXcacvxj1L0cDvfpFlsvXQsu28kHLg3EVkQRpTiZRCsdOBmwKJQr+MOy7GcYybN2/iwYMHaLfbaLVauUufacI0zeNtqypMy4LDOxWKLXfFy8u4mQ2Pi5infr4IATDIYFSq3+9jb38fSZpieWmp5DeQpmkeHeCCQAgq4XHh2DZcx4HtOOXnmDE4zSYuXLpE0QJiKkgcEI+NNE2xt7uL7t4eUj5hznXd0XfdGL/AJ0mC377/Pn77/vv4g298A+cKVdeimFHeqS7uNKaCAbIWoZSeKOTUi99Fd8VgLUKl2yJj8IIAURDAtm3YjlN+jNjfQMh/cBtCKMRJgsD3866MNMW7776L/cNDfO3qVWxvb0NRVURBcHw+ijJUUyHcJoVIcLjlb4nC+RUjLzJ0Xjz/Ucdf+JnCWLkjpeJvxREGUTRUoPq4EKJxcCAUkHtuPHz0CHEcw200sLy0lHeT+H7udxBF0tcDjEHTdTiOA8d1YXGhNoiqqtjg0QKCmBYSB8RjhTGGXq+H/YcPEfb7uWlPo1Grl1z6IRR+FoYhfvqzn2H/4ADf//730Rrj7lZqkZzjHKYl5X4HJ/FRC8MQvufBtKzaw3EGbZmTOEbAF30oCsIgwDtvvw0/DPGtb34TtmWBAWi2WkiTBL1eL3dWVFU4ros0SfIwNx+hLTpNhDDRdR2mZcHmX1VteY+DlI9MfpxI0VKorag6rocPHyKMIjDk9TphGCLh7cBCNOlcELiOM9QdUt4pg9tu4/ylS7Bt+2ROjHjqIXFAfCGEYYj93V14h4dIkwQuvwOqS/FCu394iP/vf/0vuI0Gvvud70zstRdjjAdnQJwUIg2QzFGAOIqYuyDquo5mqzXy7watkBljiLkoKBboBb6Pt956C7qu4/XvfAc2dz5U+eRLII+E9Hs9GTFo8omcAOSkzjAM82mCUVQaF80Y90QQYsG2T8xkqAhjDPEYO+SF7QfHEZtxrZaCJE1x/+HD3LUwio6FMk/zmKaZC4JGA3qN50lVVWydPYvTW1sLOBviqwyJA+ILI8syHB4eontwgLDfh6nrcFy3tGjXgTGGT2/dwi9++UtcuXIFz37ta1PbwMr2vmKb3wJJswxpwRBpYdvl/gSKqmJpaQkAjkPzFZX0okBSeFCIrgLTsrC7u4u3fv1rLC8v49uvvw7LNOUQJcuyYBTEW5Zl6Hue9DMYupvlgoAxhiAIpKlPFIZDxZH6gPWxGGdsLGBwkXyeRPRmwc+/jGbx/H8VGRdicdH5kZs39T1P2lu3Wi1YfB6FzSME05x/s93GuQsX8vQSQcwJtTISXxiqqmJ1dRWmaeLw4ABBt4tupwPbtmFWWPCOQlEUnD9/Ho8ePcJHH3+MpXYbG5ubecEjz18zRYFSuIMdRIR9izl+ZYGRBQV5DYQYhrMIRH8/FKW0SEmKnRXcMjgMQzkvQtQGmIaB33/0Ed57912c29nBtddek+ZDYvEvCgMGyHHPXr+PKI7heR4yxo7D2IXR3Y7jwOELVpqmuVAIAoRhiDiOkXLfgTAMC4fO+GbyNsiicBBfRs0RyML0aF5hMFgsW2zDFHMa4jhGLAQAFwPSoIojIgue5yHwfSiMYX1tDetra8MFhTVQVRXbOzs4tbEx1/kRRBGKHBBPBFEU4eDgAH6/j9jzoGQZTF7UVjeSkCQJ/vVf/xVJluG1115Dw3WHcq5DeV9xx1cjYlA1SbLuZVy0LAJ8TsGkjx1PfchjHlNNf3R0hCzL0G63h+40i6JAnKeqqrkAM02wLMNb16/j9u3beO655/Dss8/K8xNRCSgKlpaWRs6bEG11AGSRaaUIqzj+NE3zKYgFi2o5bZHvb/BRJSMlMR6ZT8kUIkJEHRjy94WYt1AX8VqVCgj5ORXv/otf0q8Bw50IYtyyEDRRFCHg0Ztms4nllZWpBaiiKFhbX8fmmTMw6gxlIogpIHFAPDEwxuB5Hnq9HoJeD5HngWWZHP1bxxvh6OgI//pv/4bNrS1c5gVZJfvZMfsutveVbGqH/1j+syQYxhU7ivZDvj2x8InH1clPj6Lb6SDhi4xwLEziGFEc5zMu+HFpXBQYvLo9CkO8+eab2D84wGuvvYadnZ3j80Lu7+97HgzTRKPRGJpCWRRaURSh73kA8nHJjUajZNs8+FzUgTEmw+9FwRDzaZpyjoI4Dv48Fjs0RI2BSOlUvQ/kbAJAukOOc6cUi744u+LzoOs6dMOAoevQdF2OCxfvXTEd0/d9hEEA07axsrw8VfpAUVWsrq9jY3u73qRGgpgBEgfEE4fIU/f7ffSPjhD5PrI0hanrsGw794Yfw0cff4wbN27g61//OpZXVvIRxa772HrsZc4fBfGA4YhBuqD8txiSpOs6VE0rCQLR9iYiBYJet4uf/PSnSOIY3379daytrQ1tt9frIYljOTthUByUWjMBxEmCPu9kUBUlN7safK0Kfgrz+goUIw3FEH5RUMgoiBAHKLSKAtLfoXhOk5ATIbkI0LkImFTnEvNOjySO4QcBTMtCq9EY33lQQFVVrJAoIB4TJA6IJ5ooitDr9dA5OEDs+2BJIhc7wzAqL+YMwM9++lM82tvDH3zjGzAMA6qqotFsTl3suEhEBTsA2ZNfNXeh9vayDFEco9vtwvM8mKYJi6dRdFXN71pNc2jRevToEX7+85/Dsix85zvfGR6trShAluHw6AhgDC2erhgUB+Kcis9oxo2ZhPBpNhrQR7UuDnoZLADxnLJC8adILWDgWB+nIZOIrAjhKyIKk8aaM8agahrW1texceZMSeARxElC4oD4UpAkCfr9Pg53d/NIQpJAU1U5QXBw0Q/DEP/v//gfcBwHL774Yl64pihwHecL67MHY0gHPm7iDjorWOAWXQwxcGcraghE5buC3EAnDAJYto12uw3TNCvD1HEc47e//S0+/ugjnDp9Gt/61rdGLjZJkqDX7Za6IKrEQfVpMvT4SGgAMPg46JFphuENVP+44v/CPEnst6rwsFjv8bhhjOUphDDMO0XiWIq1drs9WqDw2pCV1VVsnDlDkQLisUPigPhSkWUZ+v0+DnZ3EfZ6yLIMmqLAtKx8USwsQHv7+/jJT34CwzDw8ssvwzRNpGl67Mz4BUQRinlygRAIg1XtxcdEUYQoiuTfiL/TdT0XCFEEwzDQXloaCpUDwN27d3Hj+nXESYIXnn8ely9frlysRaog4EWGpmnC5Xe3dcWBOKeAGyMJhL1yqVixePkpiCOGPLqS8b+ZNGJZzqKouJxlfIbF4yaOY3g8JZbwGgld15FmGVqt1kjfAlXTclGwvS0jQQTxuCFxQHwpEXdk+48ewe9288IzxqDxqnDRK+8HAX7yk58gDEN84xvfgG3bMuTtum7lfIcTPW5g5F0sA2Q4XAgCUXwnFkbGGEyRLuCFbmmWoXN0BIV3FcgKe0VB4Hm48Zvf4O7du9ja3sbLL7+MBndSHFpsC7n4bqeDNE3zUdumKWc7DBZqDtYdDJImCTxuycz44ui4bj5hccbnbxAxLnpUimlUl8VJwVg+AjvirZkicqFy0dNsNocjO7wtdWl1FZskCognABIHxJce3/dxtLeHXqeDlE9AFEVvKvJitN/cuIGDw0O8+uqrWF1dRcznBxg8ivC4ahGKi6wYtSwNkni/f8LvNBUcL+CifmBUb//R4SEyxvI7Ul1HlmX45JNP8O5778HQNLz8yiv5fIQJd9+KoiDNMnSPjmQLoxzINOMiyxhDGEUIfF/e2Vuii2SG7QlBkvICz3GXsMedUhCeD2KipqqqSLl1tqqqsotDvN8U5B4SK+vrWF1fJ1FAPDGQOCCeGrIsQ+j76B0dod/tIgqC4xHEWYb3f/tb3Hv4EM9cvowzZ87kOXt+oW7wO+STgPGUQSaMcnhffMZnGVQ8AFCUvAWuptlPr9fLh/a4LoIgwPW33sL+wQEuXryIF198cWydxWDIXqQURAsjgLnEgSDNMvieJ+cbqJomR01Pi5hQOZiiKM4weJxRg4y34cZxLIdPqaoqjZ1E8aGcZKlpaC8vY3VtDe3l5cdyjAQxDSQOiKeWOAzROzyE1+sh5kY7v//973Hzk0+wvb2NK888IwcOKYqSW9c6TjkXXNHmNmSGNOBvUBQCaZZVOvOJ1IZwKlRVFZqmQVNV6QpYrOTP0nRsQZ/v+/A8D7dv38ann36KVquFV199tbJFcZDB1ECn00GWpnAaDTnvopY4EE6UE/Y3eHc9aTpnkWxCpKB8OAVvCVG3UOuR9RF21J7vS28Eh6euoihClmUlrw2n0cDK2hpW1tZmEkUE8bggcUA89WRZhtDzcmOlIMDNmzdx48YNrK2t4fnnn0eSJPlEPF64ZhjGUHGjpOhUOMq1sOLnRQEgrIk1VZU2wxMRhXojog137t7Fb65fRxhFeP6553Dl6tVa1sKDDLoiyloHzB85KJLxlr4wCKQvgu04I4dvTSMKBGmaVrorFjtBgELtBJ+gWPkaCxtuHIupiA+XSrMMyLJ8oJRtIwwCRDwy5LouXC4IVtfX4dScnEkQXzQkDoivFEkcI+j1cOvmTfz0Jz+B7Th4/dvfhq7r6Pf7eVgY+cJiGAYs04Sm68cLU6Ftrvi9iMrv/oUQEPMPijDGZq+g53a+Gb9rffvtt/HZ7dtYXlnB1559Ftubm/XbBgeoSikAixcHgoQXLKbcftjgHha6rs8kCAQnlVKQg6v4NEsgFzbCnEu0cGaMYXNrC5vb22gvL88k1Ajii4TEAfGV5c5nn+Gff/QjqIzhlVdewdbWFrIsQxAE+ahhoGTfrE5hcTsJ2Xo3SyEkY9jb28PNmzfx+eefQ9d16QaZpSlazeZo46EJiJSCO1CDMY04kFbUNffJGEMYhnnbH2NAlkHVtHy2hmHMJHQW3b7IGEMcRXLENeOdMZZtwzSMfH5FEEAzDLRXVnDx4kU4EwyOCOJJhsQB8ZWm1+vh//nv/x33PvsM66ur+NqVKzh//jwAnLhImDZ6EIchbn32GT65eRO9bhduo4GLFy/i/PnzsEwTPT5e2bHtmareR6UUgNkiB3W8CQQZgCxJ8lQD7zgRYXzTsmBVOD2O3C4WFzUYJwoMXUcYhgijCI7rYnl1Fe2VFayuri5s1DRBfFGQOCAIAHfu3MFP33gDNz/6CG3XxZUrV/DM5cvQdb1SJJjcZ2BesjQdHz1gDLt7e/jkk09w5/PPwRjD9vY2Ll68iFOnTg11Gfi+D8Mw0Gw0SqkMMZFRUOVPIIYBGZYlvRDk4zFfWkHk+lnxa9QgJG4LXQzdsyyDpuu1ogmLiBpkfMBTGIZIsgwKb0W0bBuapslx0812G0tra2g2m2g0GnBmGLlMEE8iJA4IosD+/j7efPNNvPOb38DWdVy+cAFXrlyRLYKiJoFlGVRVlUJhnmhC1QCmUVGCnfPnYY+w0k2SBN1uF2rB8ngQ0eqnYlgcjEopADNGDsT+CoV805IkCSJ+d85PYGw0QcxRmGWBltMsowhRHENBXgipKQosx4GmqvCDAIwxtJaWsLS2hkajgWazSfbGxFMHiQOCqMDzPPzyl7/Er37xCyhpivM7O7j6zDNYWlpCFMeIo+h44WMMOr+rNQ2j2rtgDDK9kGXY3d+vFSUYtZ2joyMwxtDmw5IGfz/K0TCKIvT7faiqWun5nwFg3MxH1hOw47HG4xb/4sCpWRkbTTBN6LwbJBvVQTKGNE0RhWHeeigKTbMsn7RomlC4iZOiKFhaXcXS6ipc15UjsgniaYTEAUGMIY5j/OY3v8HPfvYzBN0utk+fxtLSEtxGA5ZlweCLk2EYMsduiLTDiBx5FEXodbvo9Xrodrvodjo44v9nWVYrSjAKYYZk2zYcx5E/HycMgDxqkKbp0OMEoiZgVhYhEARD0QQAYCwfiY28W0SIBVVVoek6VEUppSKk2AhDJHGMjBtVAZAW3EAeOTAtC8tra2i222g2m9X2xwTxlEHigCBqkGUZPvzwQ7zz9tvYPzhA9+gIaRzniw93M2w4DhzHge04sHneXtN1BNygqNft4qjXQ8LrFwDAcRy0mk00mk24rovl5WWsra3NnLcuRgBEakGIllEf9TiO0ev1oCgK2u12Zdvd3OJA+DQssoOAL/BxkiCNYyQisjGwXzHYSlFVMG5OFfLIj5hbYds2nEYDtm3nHhfc60LnQq/RaOQ229SSSHxFIHFAEDPAuInPwcEBDg8P5ff93V0cHB6i3+sBaQpNVWEaBtrNJpqtFtqtFtpLS2jzu9CiG+M8ff3F4zrqdMCyDK1mE1qNCv9ut4skSUZGDYD5xYE4thO52PBFXgy1SrMsn4SYJIjjWKaBElFMalnQuGGR22zKeRRiWJemaaV/U5SA+CpC4oAgToA0TbG3t4coiqBpGqIwRBwECH0fSRTJYkZD16EbhowUZNzgaB76/T6iKJK2xOOoEzUAqsXBj//n/8TVq1exvbVV+9gYt1ie9xxLx8a7ExTGkPKiwjhJEEcRNF2H7bpwGg0YlgVN02AYBlzXhc07DzRNow4DghiAzL0J4gTQNA2nT58GwHvl4zjv4Q/DvA0uDBF6Hvq+D9bvQ9c0OWRJ4XMV5HLFF9K6C5hlWXLc8ySvATEYyDTNIWHwwQcf4J/+6Z/wyaefYnd3F//bf/gPuHbtmvz9xx9/jM8/+wx/+Zd/WfNZOT6HqQVC8W/5lEggrz9I4jj/zqdaWo6DZrsNp9WCbduwLEt+URSAIOpB4oAgThhFUWTLI5BHFYRICMMwr5QPAkS+j5APJFI1DaqiQBPhbW7BLOYCKOUdHP+bd04oioIsyxDH8chpk3EcI45jmXMHgP/jP/0n/O//8T8CyIXDzs4O/uiP/gh/85//89Djr732Gv7P//JfphIHQN7VIM6j5Ecw0PJYlA6ioyNNU6QiZZAkYHzstu26WGo0YPOIgBADRiEqQxBEfUgcEMRjRtO0fCAPD/kXowpxHOd3wlGURxeCAF6/D0VRZOW9pml5pEFV8wp8MXGwUPBnGAaCIIDnedA1Lf+7whChNEnQ9zwwAJZpHt/RF+oCXn75Zbz88suV58AAvPDCCzg6PMTtzz7DzrlzcnCReLw4HtElUdUtIYoFB38mRID84lEBRVXhNpuwG428dZQXEJqmKQUBFQ0SxPyQOCCILxjDMGAYBlqtlqygF3f1URThaH8fvf19ZIwh48V1QhCofLiToijQFAWKiDjwn6Vpik6vh1azeVzXkKbodrvIsgy6rsOyrJJngxgxLZwMFQBJmiIVfgxcYGi6jhdefBFvvfUWzp49W/I9AArDqcT/j3+Rn0uWDX2laZp3FLDh8cqGbWPzzBm43F9AfJEYIIjFQ+KAIJ4gFEWRlfOic2B9fT3v7RfRhDhGHIZIkiT3+88ypHGcL668GC9LUyRpCt/zAABhEOTpBUVBzM1+dE2DZduynQ+KItMRInUhBIWqqseLdWF88bVr1/Bv//Zv+NM//dNcDPCfC2GQ8XkEmThO/l1R1XxipWFANwwYvOZC03UoPEJiGgYM7iEhogKUIiCIxwOJA4L4EiAEAwrdB6KmQNxxV30FQYDD/f28ziFNwdI0D88rCgzLQsSLFsXCbhgGOt2ujB4oXDCsLC0hiiIcHh4epwEUBZcvX8b/9V//Kx49eoRGs5mLCC40VB7JMAwDqmgV5N0ZoktAjLYe/BJ1EwRBfDGQOCCILymqqo719BcpirW1NXS73Txkzxd213WhaVo5ncAYPn/0CJt8KmWWZTIF8P4nn0BzXWyePw9VLP6qivfeew+fPXiAy88/L62EhaCQdRKFRb/4f4IgnlxIHBDEU4pIUSwvL2N5ebnWY3q9Hra3t4dEw7179+C6Ls6cOSO3rSgK/v7v/x7f/OY35c8Jgng6oEoegiCGUBQFnufhnXfewbvvvoskSXDr1i288847uHPnjkwf/OhHP8Kf//mff9GHSxDEgiGHRIIgJN///vfx4x//GADw4x//GH/8x3889Dd/9Vd/hX/4h3/AJ598gmeffRa7u7totVqP+UgJgjhJSBwQBDETf/u3f4t//ud/xr/8y7980YdCEMSCobQCQRAz8aMf/Qg/+MEPvujDIAjiBKDIAUEQBEEQJShyQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGUIHFAEARBEEQJEgcEQRAEQZQgcUAQBEEQRAkSBwRBEARBlCBxQBAEQRBECRIHBEEQBEGU+P8B3liLedSl2tgAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "plot_bloch_vector([0,1,0], title='My Bloch Sphere')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adjusting the output from plot_bloch_vector()\n", - "\n", - "When using the `plot_bloch_vector` function it returns a `matplotlib.Figure` for the rendered visualization. Jupyter notebooks understand this return type and render it for us in this tutorial, but when running outside of Jupyter you do not have this feature automatically. However, the `matplotlib.Figure` class natively has methods to both display and save the visualization. You can call `.show()` on the returned object to open the image in a new window (assuming your configured matplotlib backend is interactive). Or alternatively you can call `.savefig('out.png')` to save the figure to `out.png` in the current working directory. The `savefig()` method takes a path so you can adjust the location and filename where you're saving the output." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:09:18.283600Z", - "start_time": "2021-07-31T05:09:17.464585Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:19.155318Z", - "iopub.status.busy": "2023-08-25T18:25:19.154947Z", - "iopub.status.idle": "2023-08-25T18:25:19.204641Z", - "shell.execute_reply": "2023-08-25T18:25:19.204006Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:19 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3.10.6 64-bit ('quantum')", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "e6bd4a3f608106bb98e03db025c716fec5b1c45149c5607eb898d72d2cb3836e" - } - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "914030e209ab418f8c633dd6e8b5e4f2": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "ba8e23e94da44f7d9d779cb1bbdb0f79": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_f214fd1835684365922b79d6a28add28", - "placeholder": "​", - "style": "IPY_MODEL_914030e209ab418f8c633dd6e8b5e4f2", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "f214fd1835684365922b79d6a28add28": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/tutorials/circuits/3_summary_of_quantum_operations.ipynb b/docs/tutorials/circuits/3_summary_of_quantum_operations.ipynb deleted file mode 100644 index 1fc4f3fe469f..000000000000 --- a/docs/tutorials/circuits/3_summary_of_quantum_operations.ipynb +++ /dev/null @@ -1,3679 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Summary of Quantum Operations " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " In this section we will go into the different operations that are available in Qiskit Terra. These are:\n", - "\n", - "- Single-qubit quantum gates\n", - "- Multi-qubit quantum gates\n", - "- Measurements\n", - "- Reset\n", - "- Conditionals\n", - "- State initialization\n", - "\n", - "We will also show you how to use the three different simulators:\n", - "\n", - "- unitary_simulator\n", - "- qasm_simulator\n", - "- statevector_simulator" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:10:06.399483Z", - "start_time": "2021-07-31T05:10:06.113598Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:20.828670Z", - "iopub.status.busy": "2023-08-25T18:25:20.826710Z", - "iopub.status.idle": "2023-08-25T18:25:21.367779Z", - "shell.execute_reply": "2023-08-25T18:25:21.366924Z" - } - }, - "outputs": [], - "source": [ - "# Useful additional packages \n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "from math import pi" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:10:07.863361Z", - "start_time": "2021-07-31T05:10:06.401320Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.373328Z", - "iopub.status.busy": "2023-08-25T18:25:21.372022Z", - "iopub.status.idle": "2023-08-25T18:25:21.764162Z", - "shell.execute_reply": "2023-08-25T18:25:21.763389Z" - } - }, - "outputs": [], - "source": [ - "from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, transpile\n", - "from qiskit.tools.visualization import circuit_drawer\n", - "from qiskit.quantum_info import state_fidelity\n", - "from qiskit import BasicAer\n", - "\n", - "backend = BasicAer.get_backend('unitary_simulator')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Single Qubit Quantum states \n", - "\n", - "A single qubit quantum state can be written as\n", - "\n", - "$$\\left|\\psi\\right\\rangle = \\alpha\\left|0\\right\\rangle + \\beta \\left|1\\right\\rangle$$\n", - "\n", - "\n", - "where $\\alpha$ and $\\beta$ are complex numbers. In a measurement the probability of the bit being in $\\left|0\\right\\rangle$ is $|\\alpha|^2$ and $\\left|1\\right\\rangle$ is $|\\beta|^2$. As a vector this is\n", - "\n", - "$$\n", - "\\left|\\psi\\right\\rangle = \n", - "\\begin{pmatrix}\n", - "\\alpha \\\\\n", - "\\beta\n", - "\\end{pmatrix}.\n", - "$$\n", - "\n", - "Note, due to the conservation of probability $|\\alpha|^2+ |\\beta|^2 = 1$ and since global phase is undetectable $\\left|\\psi\\right\\rangle := e^{i\\delta} \\left|\\psi\\right\\rangle$ we only require two real numbers to describe a single qubit quantum state.\n", - "\n", - "A convenient representation is\n", - "\n", - "$$\\left|\\psi\\right\\rangle = \\cos(\\theta/2)\\left|0\\right\\rangle + \\sin(\\theta/2)e^{i\\phi}\\left|1\\right\\rangle$$\n", - "\n", - "where $0\\leq \\phi < 2\\pi$, and $0\\leq \\theta \\leq \\pi$. From this, it is clear that there is a one-to-one correspondence between qubit states ($\\mathbb{C}^2$) and the points on the surface of a unit sphere ($\\mathbb{S}^2$). This is called the Bloch sphere representation of a qubit state.\n", - "\n", - "Quantum gates/operations are usually represented as matrices. A gate which acts on a qubit is represented by a $2\\times 2$ unitary matrix $U$. The action of the quantum gate is found by multiplying the matrix representing the gate with the vector which represents the quantum state.\n", - "\n", - "$$\\left|\\psi'\\right\\rangle = U\\left|\\psi\\right\\rangle$$\n", - "\n", - "A general unitary must be able to take the $\\left|0\\right\\rangle$ to the above state. That is \n", - "\n", - "$$\n", - "U = \\begin{pmatrix}\n", - "\\cos(\\theta/2) & a \\\\\n", - "e^{i\\phi}\\sin(\\theta/2) & b \n", - "\\end{pmatrix}\n", - "$$ \n", - "\n", - "where $a$ and $b$ are complex numbers constrained such that $U^\\dagger U = I$ for all $0\\leq\\theta\\leq\\pi$ and $0\\leq \\phi<2\\pi$. This gives 3 constraints and as such $a\\rightarrow -e^{i\\lambda}\\sin(\\theta/2)$ and $b\\rightarrow e^{i\\lambda+i\\phi}\\cos(\\theta/2)$ where $0\\leq \\lambda<2\\pi$ giving \n", - "\n", - "$$\n", - "U(\\theta, \\phi, \\lambda) =\n", - " \\begin{pmatrix}\n", - " \\cos\\left(\\frac{\\theta}{2}\\right) & -e^{i\\lambda}\\sin\\left(\\frac{\\theta}{2}\\right) \\\\\n", - " e^{i\\phi}\\sin\\left(\\frac{\\theta}{2}\\right) & e^{i(\\phi+\\lambda)}\\cos\\left(\\frac{\\theta}{2}\\right)\n", - " \\end{pmatrix}\n", - "$$\n", - "\n", - "This is the most general form of a single qubit unitary." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Single-Qubit Gates \n", - "\n", - "The single-qubit gates available are:\n", - "- U gate\n", - "- P gate\n", - "- Identity gate\n", - "- Pauli gates\n", - "- Clifford gates\n", - "- $C3$ gates\n", - "- Standard rotation gates \n", - "\n", - "We have provided a backend: `unitary_simulator` to allow you to calculate the unitary matrices. " - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:10:08.577924Z", - "start_time": "2021-07-31T05:10:08.575044Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.769856Z", - "iopub.status.busy": "2023-08-25T18:25:21.768472Z", - "iopub.status.idle": "2023-08-25T18:25:21.773548Z", - "shell.execute_reply": "2023-08-25T18:25:21.772959Z" - } - }, - "outputs": [], - "source": [ - "q = QuantumRegister(1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### U gate\n", - "\n", - "In Qiskit we give you access to the general unitary using the $u$ gate, which has the following matrix form\n", - "\n", - "$$\n", - "U(\\theta, \\phi, \\lambda) =\n", - " \\begin{pmatrix}\n", - " \\cos\\left(\\frac{\\theta}{2}\\right) & -e^{i\\lambda}\\sin\\left(\\frac{\\theta}{2}\\right) \\\\\n", - " e^{i\\phi}\\sin\\left(\\frac{\\theta}{2}\\right) & e^{i(\\phi+\\lambda)}\\cos\\left(\\frac{\\theta}{2}\\right)\n", - " \\end{pmatrix}\n", - "$$\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:10:09.406278Z", - "start_time": "2021-07-31T05:10:09.398661Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.778156Z", - "iopub.status.busy": "2023-08-25T18:25:21.776967Z", - "iopub.status.idle": "2023-08-25T18:25:21.788716Z", - "shell.execute_reply": "2023-08-25T18:25:21.788134Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌────────────────┐\n",
-       "q0: ┤ U(π/2,π/4,π/8) ├\n",
-       "    └────────────────┘
" - ], - "text/plain": [ - " ┌────────────────┐\n", - "q0: ┤ U(π/2,π/4,π/8) ├\n", - " └────────────────┘" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.u(pi/2,pi/4,pi/8,q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:10:10.420628Z", - "start_time": "2021-07-31T05:10:10.342207Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.793275Z", - "iopub.status.busy": "2023-08-25T18:25:21.792121Z", - "iopub.status.idle": "2023-08-25T18:25:21.817303Z", - "shell.execute_reply": "2023-08-25T18:25:21.816632Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 0.707+0.j , -0.653-0.271j],\n", - " [ 0.5 +0.5j , 0.271+0.653j]])" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "Note on U gate deprecation\n", - "\n", - "The QuantumCircuit methods $u1$, $u2$ and $u3$ are now deprecated. Instead, the following replacements should be used.\n", - "\n", - "- $u1(\\lambda) = p(\\lambda) = u(0, 0, \\lambda)$\n", - "\n", - "- $u2(\\phi, \\lambda) = u(\\frac{\\pi}{2}, \\phi, \\lambda) = p(\\frac{\\pi}{2} + \\phi) \\cdot sx \\cdot p(\\frac{\\pi}{2} - \\lambda)$\n", - "\n", - "- $u3(\\theta, \\phi, \\lambda) = u(\\theta, \\phi, \\lambda) = p(\\phi + \\pi) \\cdot sx \\cdot p(\\theta + \\pi) \\cdot sx \\cdot p(\\lambda)$\n", - "\n", - "```python\n", - "# qc.u1(lambda) is now:\n", - "qc.p(lambda)\n", - "\n", - "# qc.u2(phi, lambda) is now:\n", - "qc.u(pi/2, phi, lambda)\n", - "\n", - "# qc.u3(theta, phi, lambda) is now:\n", - "qc.u(theta, phi, lambda)\n", - "```\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### P gate\n", - "\n", - "The $p(\\lambda)= u(0, 0, \\lambda)$ gate has the matrix form\n", - "\n", - "$$\n", - "p(\\lambda) = \n", - "\\begin{pmatrix}\n", - "1 & 0 \\\\\n", - "0 & e^{i \\lambda}\n", - "\\end{pmatrix},\n", - "$$\n", - "\n", - "which is useful as it allows us to apply a quantum phase." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:10:51.997454Z", - "start_time": "2021-07-31T05:10:51.992620Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.822254Z", - "iopub.status.busy": "2023-08-25T18:25:21.821019Z", - "iopub.status.idle": "2023-08-25T18:25:21.831689Z", - "shell.execute_reply": "2023-08-25T18:25:21.830408Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌────────┐\n",
-       "q0: ┤ P(π/2) ├\n",
-       "    └────────┘
" - ], - "text/plain": [ - " ┌────────┐\n", - "q0: ┤ P(π/2) ├\n", - " └────────┘" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.p(pi/2,q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:10:58.149042Z", - "start_time": "2021-07-31T05:10:58.138158Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.835110Z", - "iopub.status.busy": "2023-08-25T18:25:21.834704Z", - "iopub.status.idle": "2023-08-25T18:25:21.856068Z", - "shell.execute_reply": "2023-08-25T18:25:21.855340Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+1.j]])" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Identity gate\n", - "\n", - "The identity gate is $Id = p(0)$." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:00.799001Z", - "start_time": "2021-07-31T05:11:00.794172Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.865950Z", - "iopub.status.busy": "2023-08-25T18:25:21.863716Z", - "iopub.status.idle": "2023-08-25T18:25:21.873472Z", - "shell.execute_reply": "2023-08-25T18:25:21.872871Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌───┐\n",
-       "q0: ┤ I ├\n",
-       "    └───┘
" - ], - "text/plain": [ - " ┌───┐\n", - "q0: ┤ I ├\n", - " └───┘" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.id(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:03.627749Z", - "start_time": "2021-07-31T05:11:03.619164Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.878047Z", - "iopub.status.busy": "2023-08-25T18:25:21.876879Z", - "iopub.status.idle": "2023-08-25T18:25:21.893065Z", - "shell.execute_reply": "2023-08-25T18:25:21.892407Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j]])" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Pauli gates\n", - "\n", - "#### $X$: bit-flip gate\n", - "\n", - "The bit-flip gate $X$ is defined as:\n", - "\n", - "$$\n", - "X = \n", - "\\begin{pmatrix}\n", - "0 & 1\\\\\n", - "1 & 0\n", - "\\end{pmatrix}= u(\\pi,0,\\pi)\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:05.287138Z", - "start_time": "2021-07-31T05:11:05.281859Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.897778Z", - "iopub.status.busy": "2023-08-25T18:25:21.896597Z", - "iopub.status.idle": "2023-08-25T18:25:21.909168Z", - "shell.execute_reply": "2023-08-25T18:25:21.908074Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌───┐\n",
-       "q0: ┤ X ├\n",
-       "    └───┘
" - ], - "text/plain": [ - " ┌───┐\n", - "q0: ┤ X ├\n", - " └───┘" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.x(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:07.285569Z", - "start_time": "2021-07-31T05:11:07.276242Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.915449Z", - "iopub.status.busy": "2023-08-25T18:25:21.911737Z", - "iopub.status.idle": "2023-08-25T18:25:21.926832Z", - "shell.execute_reply": "2023-08-25T18:25:21.926247Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[0.+0.j, 1.+0.j],\n", - " [1.+0.j, 0.+0.j]])" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### $Y$: bit- and phase-flip gate\n", - "\n", - "The $Y$ gate is defined as:\n", - "\n", - "$$\n", - "Y = \n", - "\\begin{pmatrix}\n", - "0 & -i\\\\\n", - "i & 0\n", - "\\end{pmatrix}=u(\\pi,\\pi/2,\\pi/2)\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:09.274252Z", - "start_time": "2021-07-31T05:11:09.270141Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.931392Z", - "iopub.status.busy": "2023-08-25T18:25:21.930246Z", - "iopub.status.idle": "2023-08-25T18:25:21.938456Z", - "shell.execute_reply": "2023-08-25T18:25:21.937866Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌───┐\n",
-       "q0: ┤ Y ├\n",
-       "    └───┘
" - ], - "text/plain": [ - " ┌───┐\n", - "q0: ┤ Y ├\n", - " └───┘" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.y(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:11.878524Z", - "start_time": "2021-07-31T05:11:11.868931Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.942944Z", - "iopub.status.busy": "2023-08-25T18:25:21.941809Z", - "iopub.status.idle": "2023-08-25T18:25:21.955152Z", - "shell.execute_reply": "2023-08-25T18:25:21.954560Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 0.+0.j, -0.-1.j],\n", - " [ 0.+1.j, 0.+0.j]])" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### $Z$: phase-flip gate\n", - "\n", - "The phase-flip gate $Z$ is defined as:\n", - "\n", - "$$\n", - "Z = \n", - "\\begin{pmatrix}\n", - "1 & 0\\\\\n", - "0 & -1\n", - "\\end{pmatrix}=p(\\pi)\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:13.903719Z", - "start_time": "2021-07-31T05:11:13.898408Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.959748Z", - "iopub.status.busy": "2023-08-25T18:25:21.958576Z", - "iopub.status.idle": "2023-08-25T18:25:21.966774Z", - "shell.execute_reply": "2023-08-25T18:25:21.966200Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌───┐\n",
-       "q0: ┤ Z ├\n",
-       "    └───┘
" - ], - "text/plain": [ - " ┌───┐\n", - "q0: ┤ Z ├\n", - " └───┘" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.z(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:17.430999Z", - "start_time": "2021-07-31T05:11:17.420906Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.971196Z", - "iopub.status.busy": "2023-08-25T18:25:21.970066Z", - "iopub.status.idle": "2023-08-25T18:25:21.983510Z", - "shell.execute_reply": "2023-08-25T18:25:21.982914Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 1.+0.j, 0.+0.j],\n", - " [ 0.+0.j, -1.+0.j]])" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Clifford gates\n", - "\n", - "#### Hadamard gate\n", - "\n", - "$$\n", - "H = \n", - "\\frac{1}{\\sqrt{2}}\n", - "\\begin{pmatrix}\n", - "1 & 1\\\\\n", - "1 & -1\n", - "\\end{pmatrix}= u(\\pi/2,0,\\pi)\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:24.374244Z", - "start_time": "2021-07-31T05:11:24.369684Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:21.989221Z", - "iopub.status.busy": "2023-08-25T18:25:21.987525Z", - "iopub.status.idle": "2023-08-25T18:25:21.998489Z", - "shell.execute_reply": "2023-08-25T18:25:21.997884Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌───┐\n",
-       "q0: ┤ H ├\n",
-       "    └───┘
" - ], - "text/plain": [ - " ┌───┐\n", - "q0: ┤ H ├\n", - " └───┘" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.h(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:24.964793Z", - "start_time": "2021-07-31T05:11:24.956019Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.001597Z", - "iopub.status.busy": "2023-08-25T18:25:22.001167Z", - "iopub.status.idle": "2023-08-25T18:25:22.018196Z", - "shell.execute_reply": "2023-08-25T18:25:22.017551Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 0.707+0.j, 0.707-0.j],\n", - " [ 0.707+0.j, -0.707+0.j]])" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### $S$ (or, $\\sqrt{Z}$ phase) gate\n", - "\n", - "$$\n", - "S = \n", - "\\begin{pmatrix}\n", - "1 & 0\\\\\n", - "0 & i\n", - "\\end{pmatrix}= p(\\pi/2)\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:27.577029Z", - "start_time": "2021-07-31T05:11:27.572229Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.021982Z", - "iopub.status.busy": "2023-08-25T18:25:22.020923Z", - "iopub.status.idle": "2023-08-25T18:25:22.033802Z", - "shell.execute_reply": "2023-08-25T18:25:22.032609Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌───┐\n",
-       "q0: ┤ S ├\n",
-       "    └───┘
" - ], - "text/plain": [ - " ┌───┐\n", - "q0: ┤ S ├\n", - " └───┘" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.s(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:30.150288Z", - "start_time": "2021-07-31T05:11:30.141012Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.037169Z", - "iopub.status.busy": "2023-08-25T18:25:22.036923Z", - "iopub.status.idle": "2023-08-25T18:25:22.051047Z", - "shell.execute_reply": "2023-08-25T18:25:22.050007Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+1.j]])" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### $S^{\\dagger}$ (or, conjugate of $\\sqrt{Z}$ phase) gate\n", - "\n", - "$$\n", - "S^{\\dagger} = \n", - "\\begin{pmatrix}\n", - "1 & 0\\\\\n", - "0 & -i\n", - "\\end{pmatrix}= p(-\\pi/2)\n", - "$$\n" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:31.674786Z", - "start_time": "2021-07-31T05:11:31.669677Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.054192Z", - "iopub.status.busy": "2023-08-25T18:25:22.053948Z", - "iopub.status.idle": "2023-08-25T18:25:22.060799Z", - "shell.execute_reply": "2023-08-25T18:25:22.059655Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌─────┐\n",
-       "q0: ┤ Sdg ├\n",
-       "    └─────┘
" - ], - "text/plain": [ - " ┌─────┐\n", - "q0: ┤ Sdg ├\n", - " └─────┘" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.sdg(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:34.228098Z", - "start_time": "2021-07-31T05:11:34.218323Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.063706Z", - "iopub.status.busy": "2023-08-25T18:25:22.063476Z", - "iopub.status.idle": "2023-08-25T18:25:22.079138Z", - "shell.execute_reply": "2023-08-25T18:25:22.078469Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.-1.j]])" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### $C3$ gates\n", - "#### $T$ (or, $\\sqrt{S}$ phase) gate\n", - "\n", - "$$\n", - "T = \n", - "\\begin{pmatrix}\n", - "1 & 0\\\\\n", - "0 & e^{i \\pi/4}\n", - "\\end{pmatrix}= p(\\pi/4) \n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:35.573569Z", - "start_time": "2021-07-31T05:11:35.568858Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.082620Z", - "iopub.status.busy": "2023-08-25T18:25:22.082377Z", - "iopub.status.idle": "2023-08-25T18:25:22.089711Z", - "shell.execute_reply": "2023-08-25T18:25:22.089020Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌───┐\n",
-       "q0: ┤ T ├\n",
-       "    └───┘
" - ], - "text/plain": [ - " ┌───┐\n", - "q0: ┤ T ├\n", - " └───┘" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.t(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:38.030353Z", - "start_time": "2021-07-31T05:11:38.020798Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.093205Z", - "iopub.status.busy": "2023-08-25T18:25:22.092828Z", - "iopub.status.idle": "2023-08-25T18:25:22.105861Z", - "shell.execute_reply": "2023-08-25T18:25:22.105287Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1. +0.j , 0. +0.j ],\n", - " [0. +0.j , 0.707+0.707j]])" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### $T^{\\dagger}$ (or, conjugate of $\\sqrt{S}$ phase) gate\n", - "\n", - "$$\n", - "T^{\\dagger} = \n", - "\\begin{pmatrix}\n", - "1 & 0\\\\\n", - "0 & e^{-i \\pi/4}\n", - "\\end{pmatrix}= p(-\\pi/4)\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:39.747528Z", - "start_time": "2021-07-31T05:11:39.742799Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.109456Z", - "iopub.status.busy": "2023-08-25T18:25:22.109075Z", - "iopub.status.idle": "2023-08-25T18:25:22.117046Z", - "shell.execute_reply": "2023-08-25T18:25:22.116478Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌─────┐\n",
-       "q0: ┤ Tdg ├\n",
-       "    └─────┘
" - ], - "text/plain": [ - " ┌─────┐\n", - "q0: ┤ Tdg ├\n", - " └─────┘" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.tdg(q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:43.129450Z", - "start_time": "2021-07-31T05:11:43.119304Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.121368Z", - "iopub.status.busy": "2023-08-25T18:25:22.120224Z", - "iopub.status.idle": "2023-08-25T18:25:22.134202Z", - "shell.execute_reply": "2023-08-25T18:25:22.133622Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1. +0.j , 0. +0.j ],\n", - " [0. +0.j , 0.707-0.707j]])" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Standard Rotations\n", - "\n", - "The standard rotation gates are those that define rotations around the Paulis $P=\\{X,Y,Z\\}$. They are defined as \n", - "\n", - "$$ R_P(\\theta) = \\exp(-i \\theta P/2) = \\cos(\\theta/2)I -i \\sin(\\theta/2)P$$\n", - "\n", - "#### Rotation around X-axis\n", - "\n", - "$$\n", - "R_x(\\theta) = \n", - "\\begin{pmatrix}\n", - "\\cos(\\theta/2) & -i\\sin(\\theta/2)\\\\\n", - "-i\\sin(\\theta/2) & \\cos(\\theta/2)\n", - "\\end{pmatrix} = u(\\theta, -\\pi/2,\\pi/2)\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:43.968605Z", - "start_time": "2021-07-31T05:11:43.963670Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.138657Z", - "iopub.status.busy": "2023-08-25T18:25:22.137513Z", - "iopub.status.idle": "2023-08-25T18:25:22.145444Z", - "shell.execute_reply": "2023-08-25T18:25:22.144878Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌─────────┐\n",
-       "q0: ┤ Rx(π/2) ├\n",
-       "    └─────────┘
" - ], - "text/plain": [ - " ┌─────────┐\n", - "q0: ┤ Rx(π/2) ├\n", - " └─────────┘" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.rx(pi/2,q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:47.140262Z", - "start_time": "2021-07-31T05:11:47.128900Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.149868Z", - "iopub.status.busy": "2023-08-25T18:25:22.148712Z", - "iopub.status.idle": "2023-08-25T18:25:22.163632Z", - "shell.execute_reply": "2023-08-25T18:25:22.163028Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 0.707+0.j , -0. -0.707j],\n", - " [ 0. -0.707j, 0.707+0.j ]])" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Rotation around Y-axis\n", - "\n", - "$$\n", - "R_y(\\theta) =\n", - "\\begin{pmatrix}\n", - "\\cos(\\theta/2) & - \\sin(\\theta/2)\\\\\n", - "\\sin(\\theta/2) & \\cos(\\theta/2).\n", - "\\end{pmatrix} =u(\\theta,0,0)\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:48.483090Z", - "start_time": "2021-07-31T05:11:48.477062Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.169215Z", - "iopub.status.busy": "2023-08-25T18:25:22.168045Z", - "iopub.status.idle": "2023-08-25T18:25:22.177919Z", - "shell.execute_reply": "2023-08-25T18:25:22.177314Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌─────────┐\n",
-       "q0: ┤ Ry(π/2) ├\n",
-       "    └─────────┘
" - ], - "text/plain": [ - " ┌─────────┐\n", - "q0: ┤ Ry(π/2) ├\n", - " └─────────┘" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.ry(pi/2,q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:51.011307Z", - "start_time": "2021-07-31T05:11:51.001011Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.183020Z", - "iopub.status.busy": "2023-08-25T18:25:22.181727Z", - "iopub.status.idle": "2023-08-25T18:25:22.195698Z", - "shell.execute_reply": "2023-08-25T18:25:22.195128Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 0.707+0.j, -0.707+0.j],\n", - " [ 0.707+0.j, 0.707+0.j]])" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Rotation around Z-axis\n", - "\n", - "$$\n", - "R_z(\\phi) = \n", - "\\begin{pmatrix}\n", - "e^{-i \\phi/2} & 0 \\\\\n", - "0 & e^{i \\phi/2}\n", - "\\end{pmatrix}\\equiv p(\\phi)\n", - "$$\n", - "\n", - "Note that here we have used an equivalent as it is different to $p$ by a global phase $e^{-i \\phi/2}$." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:51.729618Z", - "start_time": "2021-07-31T05:11:51.724574Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.199959Z", - "iopub.status.busy": "2023-08-25T18:25:22.198830Z", - "iopub.status.idle": "2023-08-25T18:25:22.206644Z", - "shell.execute_reply": "2023-08-25T18:25:22.206083Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
    ┌─────────┐\n",
-       "q0: ┤ Rz(π/2) ├\n",
-       "    └─────────┘
" - ], - "text/plain": [ - " ┌─────────┐\n", - "q0: ┤ Rz(π/2) ├\n", - " └─────────┘" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.rz(pi/2,q)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:54.372720Z", - "start_time": "2021-07-31T05:11:54.363623Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.210777Z", - "iopub.status.busy": "2023-08-25T18:25:22.209676Z", - "iopub.status.idle": "2023-08-25T18:25:22.221439Z", - "shell.execute_reply": "2023-08-25T18:25:22.220877Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[0.707-0.707j, 0. +0.j ],\n", - " [0. +0.j , 0.707+0.707j]])" - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note this is different due only to a global phase." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Multi-Qubit Gates
\n", - "\n", - "### Mathematical Preliminaries\n", - "\n", - "The space of a quantum computer grows exponentially with the number of qubits. For $n$ qubits the complex vector space has dimension $d=2^n$. To describe states of a multi-qubit system, the tensor product is used to \"glue together\" operators and basis vectors.\n", - "\n", - "Let's start by considering a 2-qubit system. Given two operators $A$ and $B$ that each act on one qubit, the joint operator $A \\otimes B$ acting on two qubits is\n", - "\n", - "$$\\begin{equation}\n", - "\tA\\otimes B = \n", - "\t\\begin{pmatrix} \n", - "\t\tA_{00} \\begin{pmatrix} \n", - "\t\t\tB_{00} & B_{01} \\\\\n", - "\t\t\tB_{10} & B_{11}\n", - "\t\t\\end{pmatrix} & A_{01} \t\\begin{pmatrix} \n", - "\t\t\t\tB_{00} & B_{01} \\\\\n", - "\t\t\t\tB_{10} & B_{11}\n", - "\t\t\t\\end{pmatrix} \\\\\n", - "\t\tA_{10} \t\\begin{pmatrix} \n", - "\t\t\t\t\tB_{00} & B_{01} \\\\\n", - "\t\t\t\t\tB_{10} & B_{11}\n", - "\t\t\t\t\\end{pmatrix} & A_{11} \t\\begin{pmatrix} \n", - "\t\t\t\t\t\t\tB_{00} & B_{01} \\\\\n", - "\t\t\t\t\t\t\tB_{10} & B_{11}\n", - "\t\t\t\t\t\t\\end{pmatrix}\n", - "\t\\end{pmatrix},\t\t\t\t\t\t\n", - "\\end{equation}$$\n", - "\n", - "where $A_{jk}$ and $B_{lm}$ are the matrix elements of $A$ and $B$, respectively.\n", - "\n", - "Analogously, the basis vectors for the 2-qubit system are formed using the tensor product of basis vectors for a single qubit:\n", - "$$\\begin{equation}\\begin{split}\n", - "\t\\left|{00}\\right\\rangle &= \\begin{pmatrix} \n", - "\t\t1 \\begin{pmatrix} \n", - "\t\t\t1 \\\\\n", - "\t\t\t0\n", - "\t\t\\end{pmatrix} \\\\\n", - "\t\t0 \\begin{pmatrix} \n", - "\t\t\t1 \\\\\n", - "\t\t\t0 \n", - "\t\t\\end{pmatrix}\n", - "\t\\end{pmatrix} = \\begin{pmatrix} 1 \\\\ 0 \\\\ 0 \\\\0 \\end{pmatrix}~~~\\left|{01}\\right\\rangle = \\begin{pmatrix} \n", - "\t1 \\begin{pmatrix} \n", - "\t0 \\\\\n", - "\t1\n", - "\t\\end{pmatrix} \\\\\n", - "\t0 \\begin{pmatrix} \n", - "\t0 \\\\\n", - "\t1 \n", - "\t\\end{pmatrix}\n", - "\t\\end{pmatrix} = \\begin{pmatrix}0 \\\\ 1 \\\\ 0 \\\\ 0 \\end{pmatrix}\\end{split}\n", - "\\end{equation}$$\n", - " \n", - "$$\\begin{equation}\\begin{split}\\left|{10}\\right\\rangle = \\begin{pmatrix} \n", - "\t0\\begin{pmatrix} \n", - "\t1 \\\\\n", - "\t0\n", - "\t\\end{pmatrix} \\\\\n", - "\t1\\begin{pmatrix} \n", - "\t1 \\\\\n", - "\t0 \n", - "\t\\end{pmatrix}\n", - "\t\\end{pmatrix} = \\begin{pmatrix} 0 \\\\ 0 \\\\ 1 \\\\ 0 \\end{pmatrix}~~~ \t\\left|{11}\\right\\rangle = \\begin{pmatrix} \n", - "\t0 \\begin{pmatrix} \n", - "\t0 \\\\\n", - "\t1\n", - "\t\\end{pmatrix} \\\\\n", - "\t1\\begin{pmatrix} \n", - "\t0 \\\\\n", - "\t1 \n", - "\t\\end{pmatrix}\n", - "\t\\end{pmatrix} = \\begin{pmatrix} 0 \\\\ 0 \\\\ 0 \\\\1 \\end{pmatrix}\\end{split}\n", - "\\end{equation}.$$\n", - "\n", - "Note we've introduced a shorthand for the tensor product of basis vectors, wherein $\\left|0\\right\\rangle \\otimes \\left|0\\right\\rangle$ is written as $\\left|00\\right\\rangle$. The state of an $n$-qubit system can be described using the $n$-fold tensor product of single-qubit basis vectors. Notice that the basis vectors for a 2-qubit system are 4-dimensional; in general, the basis vectors of an $n$-qubit system are $2^{n}$-dimensional, as noted earlier.\n", - "\n", - "### Basis vector ordering in Qiskit\n", - "\n", - "Within the physics community, the qubits of a multi-qubit systems are typically ordered with the first qubit on the left-most side of the tensor product and the last qubit on the right-most side. For instance, if the first qubit is in state $\\left|0\\right\\rangle$ and second is in state $\\left|1\\right\\rangle$, their joint state would be $\\left|01\\right\\rangle$. Qiskit uses a slightly different ordering of the qubits, in which the qubits are represented from the most significant bit (MSB) on the left to the least significant bit (LSB) on the right (little-endian). This is similar to bitstring representation on classical computers, and enables easy conversion from bitstrings to integers after measurements are performed. For the example just given, the joint state would be represented as $\\left|10\\right\\rangle$. Importantly, *this change in the representation of multi-qubit states affects the way multi-qubit gates are represented in Qiskit*, as discussed below.\n", - "\n", - "The representation used in Qiskit enumerates the basis vectors in increasing order of the integers they represent. For instance, the basis vectors for a 2-qubit system would be ordered as $\\left|00\\right\\rangle$, $\\left|01\\right\\rangle$, $\\left|10\\right\\rangle$, and $\\left|11\\right\\rangle$. Thinking of the basis vectors as bit strings, they encode the integers 0,1,2 and 3, respectively.\n", - "\n", - "\n", - "### Controlled operations on qubits\n", - "\n", - "A common multi-qubit gate involves the application of a gate to one qubit, conditioned on the state of another qubit. For instance, we might want to flip the state of the second qubit when the first qubit is in $\\left|0\\right\\rangle$. Such gates are known as _controlled gates_. The standard multi-qubit gates consist of two-qubit gates and three-qubit gates. The two-qubit gates are:\n", - "- controlled Pauli gates\n", - "- controlled Hadamard gate\n", - "- controlled rotation gates\n", - "- controlled phase gate\n", - "- controlled u3 gate\n", - "- swap gate\n", - "\n", - "The three-qubit gates are: \n", - "- Toffoli gate \n", - "- Fredkin gate" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Two-qubit gates \n", - "\n", - "Most of the two-qubit gates are of the controlled type (the SWAP gate being the exception). In general, a controlled two-qubit gate $C_{U}$ acts to apply the single-qubit unitary $U$ to the second qubit when the state of the first qubit is in $\\left|1\\right\\rangle$. Suppose $U$ has a matrix representation\n", - "\n", - "$$U = \\begin{pmatrix} u_{00} & u_{01} \\\\ u_{10} & u_{11}\\end{pmatrix}.$$\n", - "\n", - "We can work out the action of $C_{U}$ as follows. Recall that the basis vectors for a two-qubit system are ordered as $\\left|00\\right\\rangle, \\left|01\\right\\rangle, \\left|10\\right\\rangle, \\left|11\\right\\rangle$. Suppose the **control qubit** is **qubit 0** (which, according to Qiskit's convention, is one the _right-hand_ side of the tensor product). If the control qubit is in $\\left|1\\right\\rangle$, $U$ should be applied to the **target** (qubit 1, on the _left-hand_ side of the tensor product). Therefore, under the action of $C_{U}$, the basis vectors are transformed according to\n", - "\n", - "$$\\begin{align*}\n", - "C_{U}: \\underset{\\text{qubit}~1}{\\left|0\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|0\\right\\rangle} &\\rightarrow \\underset{\\text{qubit}~1}{\\left|0\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|0\\right\\rangle}\\\\\n", - "C_{U}: \\underset{\\text{qubit}~1}{\\left|0\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|1\\right\\rangle} &\\rightarrow \\underset{\\text{qubit}~1}{U\\left|0\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|1\\right\\rangle}\\\\\n", - "C_{U}: \\underset{\\text{qubit}~1}{\\left|1\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|0\\right\\rangle} &\\rightarrow \\underset{\\text{qubit}~1}{\\left|1\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|0\\right\\rangle}\\\\\n", - "C_{U}: \\underset{\\text{qubit}~1}{\\left|1\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|1\\right\\rangle} &\\rightarrow \\underset{\\text{qubit}~1}{U\\left|1\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|1\\right\\rangle}\\\\\n", - "\\end{align*}.$$\n", - "\n", - "In matrix form, the action of $C_{U}$ is\n", - "\n", - "$$\\begin{equation}\n", - "\tC_U = \\begin{pmatrix}\n", - "\t1 & 0 & 0 & 0 \\\\\n", - "\t0 & u_{00} & 0 & u_{01} \\\\\n", - "\t0 & 0 & 1 & 0 \\\\\n", - "\t0 & u_{10} &0 & u_{11}\n", - "\t\t\\end{pmatrix}.\n", - "\\end{equation}$$\n", - "\n", - "To work out these matrix elements, let\n", - "\n", - "$$C_{(jk), (lm)} = \\left(\\underset{\\text{qubit}~1}{\\left\\langle j \\right|} \\otimes \\underset{\\text{qubit}~0}{\\left\\langle k \\right|}\\right) C_{U} \\left(\\underset{\\text{qubit}~1}{\\left| l \\right\\rangle} \\otimes \\underset{\\text{qubit}~0}{\\left| m \\right\\rangle}\\right),$$\n", - "\n", - "compute the action of $C_{U}$ (given above), and compute the inner products.\n", - "\n", - "As shown in the examples below, this operation is implemented in Qiskit as `cU(q[0],q[1])`.\n", - "\n", - "\n", - "If **qubit 1 is the control and qubit 0 is the target**, then the basis vectors are transformed according to\n", - "$$\\begin{align*}\n", - "C_{U}: \\underset{\\text{qubit}~1}{\\left|0\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|0\\right\\rangle} &\\rightarrow \\underset{\\text{qubit}~1}{\\left|0\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|0\\right\\rangle}\\\\\n", - "C_{U}: \\underset{\\text{qubit}~1}{\\left|0\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|1\\right\\rangle} &\\rightarrow \\underset{\\text{qubit}~1}{\\left|0\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|1\\right\\rangle}\\\\\n", - "C_{U}: \\underset{\\text{qubit}~1}{\\left|1\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|0\\right\\rangle} &\\rightarrow \\underset{\\text{qubit}~1}{\\left|1\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{U\\left|0\\right\\rangle}\\\\\n", - "C_{U}: \\underset{\\text{qubit}~1}{\\left|1\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{\\left|1\\right\\rangle} &\\rightarrow \\underset{\\text{qubit}~1}{\\left|1\\right\\rangle}\\otimes \\underset{\\text{qubit}~0}{U\\left|1\\right\\rangle}\\\\\n", - "\\end{align*},$$\n", - "\n", - "\n", - "which implies the matrix form of $C_{U}$ is\n", - "$$\\begin{equation}\n", - "\tC_U = \\begin{pmatrix}\n", - "\t1 & 0 & 0 & 0 \\\\\n", - "\t0 & 1 & 0 & 0 \\\\\n", - "\t0 & 0 & u_{00} & u_{01} \\\\\n", - "\t0 & 0 & u_{10} & u_{11}\n", - "\t\t\\end{pmatrix}.\n", - "\\end{equation}$$" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:57.864527Z", - "start_time": "2021-07-31T05:11:57.861603Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.225855Z", - "iopub.status.busy": "2023-08-25T18:25:22.224724Z", - "iopub.status.idle": "2023-08-25T18:25:22.229215Z", - "shell.execute_reply": "2023-08-25T18:25:22.228656Z" - } - }, - "outputs": [], - "source": [ - "q = QuantumRegister(2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Controlled Pauli Gates\n", - "\n", - "#### Controlled-X (or, Controlled-NOT) gate\n", - "The Controlled-NOT gate flips the `target` qubit when the control qubit is in the state $\\left|1\\right\\rangle$. If we take the MSB as the control qubit (e.g. `cx(q[1],q[0])`), then the matrix would look like\n", - "\n", - "$$\n", - "C_X = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & 1 & 0 & 0\\\\\n", - "0 & 0 & 0 & 1\\\\\n", - "0 & 0 & 1 & 0\n", - "\\end{pmatrix}. \n", - "$$\n", - "\n", - "However, when the LSB is the control qubit, (e.g. `cx(q[0],q[1])`), this gate is equivalent to the following matrix:\n", - "\n", - "$$\n", - "C_X = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & 0 & 0 & 1\\\\\n", - "0 & 0 & 1 & 0\\\\\n", - "0 & 1 & 0 & 0\n", - "\\end{pmatrix}. \n", - "$$\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:11:59.251955Z", - "start_time": "2021-07-31T05:11:59.245837Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.233531Z", - "iopub.status.busy": "2023-08-25T18:25:22.232399Z", - "iopub.status.idle": "2023-08-25T18:25:22.240648Z", - "shell.execute_reply": "2023-08-25T18:25:22.240089Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
            \n",
-       "q15_0: ──■──\n",
-       "       ┌─┴─┐\n",
-       "q15_1: ┤ X ├\n",
-       "       └───┘
" - ], - "text/plain": [ - " \n", - "q15_0: ──■──\n", - " ┌─┴─┐\n", - "q15_1: ┤ X ├\n", - " └───┘" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.cx(q[0],q[1])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:02.300793Z", - "start_time": "2021-07-31T05:12:02.292637Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.244987Z", - "iopub.status.busy": "2023-08-25T18:25:22.243825Z", - "iopub.status.idle": "2023-08-25T18:25:22.255919Z", - "shell.execute_reply": "2023-08-25T18:25:22.255337Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]])" - ] - }, - "execution_count": 34, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Controlled $Y$ gate\n", - "\n", - "Apply the $Y$ gate to the target qubit if the control qubit is the MSB\n", - "\n", - "$$\n", - "C_Y = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & 1 & 0 & 0\\\\\n", - "0 & 0 & 0 & -i\\\\\n", - "0 & 0 & i & 0\n", - "\\end{pmatrix},\n", - "$$\n", - "\n", - "or when the LSB is the control\n", - "\n", - "$$\n", - "C_Y = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & 0 & 0 & -i\\\\\n", - "0 & 0 & 1 & 0\\\\\n", - "0 & i & 0 & 0\n", - "\\end{pmatrix}.\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:04.293988Z", - "start_time": "2021-07-31T05:12:04.285941Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.260250Z", - "iopub.status.busy": "2023-08-25T18:25:22.259096Z", - "iopub.status.idle": "2023-08-25T18:25:22.267354Z", - "shell.execute_reply": "2023-08-25T18:25:22.266795Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
            \n",
-       "q15_0: ──■──\n",
-       "       ┌─┴─┐\n",
-       "q15_1: ┤ Y ├\n",
-       "       └───┘
" - ], - "text/plain": [ - " \n", - "q15_0: ──■──\n", - " ┌─┴─┐\n", - "q15_1: ┤ Y ├\n", - " └───┘" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.cy(q[0],q[1])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:07.042556Z", - "start_time": "2021-07-31T05:12:07.031367Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.271653Z", - "iopub.status.busy": "2023-08-25T18:25:22.270508Z", - "iopub.status.idle": "2023-08-25T18:25:22.285937Z", - "shell.execute_reply": "2023-08-25T18:25:22.285360Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j]])" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Controlled $Z$ (or, controlled Phase-Flip) gate\n", - "\n", - "Similarly, the controlled Z gate flips the phase of the target qubit if the control qubit is $\\left|1\\right\\rangle$. The matrix looks the same regardless of whether the MSB or LSB is the control qubit:\n", - "\n", - "$$\n", - "C_Z = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & 1 & 0 & 0\\\\\n", - "0 & 0 & 1 & 0\\\\\n", - "0 & 0 & 0 & -1\n", - "\\end{pmatrix}\n", - "$$\n" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:08.397786Z", - "start_time": "2021-07-31T05:12:08.392065Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.290561Z", - "iopub.status.busy": "2023-08-25T18:25:22.289347Z", - "iopub.status.idle": "2023-08-25T18:25:22.297861Z", - "shell.execute_reply": "2023-08-25T18:25:22.297291Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
          \n",
-       "q15_0: ─■─\n",
-       "        │ \n",
-       "q15_1: ─■─\n",
-       "          
" - ], - "text/plain": [ - " \n", - "q15_0: ─■─\n", - " │ \n", - "q15_1: ─■─\n", - " " - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.cz(q[0],q[1])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:10.259460Z", - "start_time": "2021-07-31T05:12:10.246478Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.302324Z", - "iopub.status.busy": "2023-08-25T18:25:22.301180Z", - "iopub.status.idle": "2023-08-25T18:25:22.315428Z", - "shell.execute_reply": "2023-08-25T18:25:22.314841Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [ 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j]])" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Controlled Hadamard gate\n", - "\n", - "Apply $H$ gate to the target qubit if the control qubit is $\\left|1\\right\\rangle$. Below is the case where the control is the LSB qubit.\n", - "\n", - "$$\n", - "C_H = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & \\frac{1}{\\sqrt{2}} & 0 & \\frac{1}{\\sqrt{2}}\\\\\n", - "0 & 0 & 1 & 0\\\\\n", - "0 & \\frac{1}{\\sqrt{2}} & 0& -\\frac{1}{\\sqrt{2}}\n", - "\\end{pmatrix}\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:11.524751Z", - "start_time": "2021-07-31T05:12:11.518946Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.320002Z", - "iopub.status.busy": "2023-08-25T18:25:22.318841Z", - "iopub.status.idle": "2023-08-25T18:25:22.327144Z", - "shell.execute_reply": "2023-08-25T18:25:22.326577Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
            \n",
-       "q15_0: ──■──\n",
-       "       ┌─┴─┐\n",
-       "q15_1: ┤ H ├\n",
-       "       └───┘
" - ], - "text/plain": [ - " \n", - "q15_0: ──■──\n", - " ┌─┴─┐\n", - "q15_1: ┤ H ├\n", - " └───┘" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.ch(q[0],q[1])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:13.607426Z", - "start_time": "2021-07-31T05:12:13.594156Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.331499Z", - "iopub.status.busy": "2023-08-25T18:25:22.330361Z", - "iopub.status.idle": "2023-08-25T18:25:22.347083Z", - "shell.execute_reply": "2023-08-25T18:25:22.346504Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 1. +0.j, 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0.707+0.j, 0. +0.j, 0.707-0.j],\n", - " [ 0. +0.j, 0. +0.j, 1. -0.j, 0. +0.j],\n", - " [ 0. +0.j, 0.707+0.j, 0. +0.j, -0.707+0.j]])" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Controlled rotation gates\n", - "\n", - "#### Controlled rotation around Z-axis\n", - "\n", - "Perform rotation around Z-axis on the target qubit if the control qubit (here LSB) is $\\left|1\\right\\rangle$.\n", - "\n", - "$$\n", - "C_{Rz}(\\lambda) = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & e^{-i\\lambda/2} & 0 & 0\\\\\n", - "0 & 0 & 1 & 0\\\\\n", - "0 & 0 & 0 & e^{i\\lambda/2}\n", - "\\end{pmatrix}\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:14.970661Z", - "start_time": "2021-07-31T05:12:14.961038Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.351593Z", - "iopub.status.busy": "2023-08-25T18:25:22.350451Z", - "iopub.status.idle": "2023-08-25T18:25:22.359062Z", - "shell.execute_reply": "2023-08-25T18:25:22.358494Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
                  \n",
-       "q15_0: ─────■─────\n",
-       "       ┌────┴────┐\n",
-       "q15_1: ┤ Rz(π/2) ├\n",
-       "       └─────────┘
" - ], - "text/plain": [ - " \n", - "q15_0: ─────■─────\n", - " ┌────┴────┐\n", - "q15_1: ┤ Rz(π/2) ├\n", - " └─────────┘" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.crz(pi/2,q[0],q[1])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:17.186696Z", - "start_time": "2021-07-31T05:12:17.171628Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.363454Z", - "iopub.status.busy": "2023-08-25T18:25:22.362318Z", - "iopub.status.idle": "2023-08-25T18:25:22.376938Z", - "shell.execute_reply": "2023-08-25T18:25:22.376366Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1. +0.j , 0. +0.j , 0. +0.j , 0. +0.j ],\n", - " [0. +0.j , 0.707-0.707j, 0. +0.j , 0. +0.j ],\n", - " [0. +0.j , 0. +0.j , 1. +0.j , 0. +0.j ],\n", - " [0. +0.j , 0. +0.j , 0. +0.j , 0.707+0.707j]])" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Controlled phase rotation\n", - "\n", - "Perform a phase rotation if both qubits are in the $\\left|11\\right\\rangle$ state. The matrix looks the same regardless of whether the MSB or LSB is the control qubit.\n", - "\n", - "$$\n", - "C_{p}(\\lambda) = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & 1 & 0 & 0\\\\\n", - "0 & 0 & 1 & 0\\\\\n", - "0 & 0 & 0 & e^{i\\lambda}\n", - "\\end{pmatrix}\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:36.826264Z", - "start_time": "2021-07-31T05:12:36.820988Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.381441Z", - "iopub.status.busy": "2023-08-25T18:25:22.380264Z", - "iopub.status.idle": "2023-08-25T18:25:22.388845Z", - "shell.execute_reply": "2023-08-25T18:25:22.388271Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
                \n",
-       "q15_0: ─■───────\n",
-       "        │P(π/2) \n",
-       "q15_1: ─■───────\n",
-       "                
" - ], - "text/plain": [ - " \n", - "q15_0: ─■───────\n", - " │P(π/2) \n", - "q15_1: ─■───────\n", - " " - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.cp(pi/2,q[0], q[1])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:12:39.234989Z", - "start_time": "2021-07-31T05:12:39.222992Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.393361Z", - "iopub.status.busy": "2023-08-25T18:25:22.392216Z", - "iopub.status.idle": "2023-08-25T18:25:22.408008Z", - "shell.execute_reply": "2023-08-25T18:25:22.407411Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j]])" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Controlled $u$ rotation\n", - "\n", - "Perform controlled-$u$ rotation on the target qubit if the control qubit (here LSB) is $\\left|1\\right\\rangle$. \n", - "\n", - "$$\n", - "C_{u}(\\theta, \\phi, \\lambda) \\equiv \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & e^{-i(\\phi+\\lambda)/2}\\cos(\\theta/2) & 0 & -e^{-i(\\phi-\\lambda)/2}\\sin(\\theta/2)\\\\\n", - "0 & 0 & 1 & 0\\\\\n", - "0 & e^{i(\\phi-\\lambda)/2}\\sin(\\theta/2) & 0 & e^{i(\\phi+\\lambda)/2}\\cos(\\theta/2)\n", - "\\end{pmatrix}.\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:19.155213Z", - "start_time": "2021-07-31T05:13:19.148466Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.412607Z", - "iopub.status.busy": "2023-08-25T18:25:22.411429Z", - "iopub.status.idle": "2023-08-25T18:25:22.421102Z", - "shell.execute_reply": "2023-08-25T18:25:22.420449Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
                           \n",
-       "q15_0: ─────────■──────────\n",
-       "       ┌────────┴─────────┐\n",
-       "q15_1: ┤ U(π/2,π/2,π/2,0) ├\n",
-       "       └──────────────────┘
" - ], - "text/plain": [ - " \n", - "q15_0: ─────────■──────────\n", - " ┌────────┴─────────┐\n", - "q15_1: ┤ U(π/2,π/2,π/2,0) ├\n", - " └──────────────────┘" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.cu(pi/2, pi/2, pi/2, 0, q[0], q[1])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:24.393740Z", - "start_time": "2021-07-31T05:13:24.378958Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.426029Z", - "iopub.status.busy": "2023-08-25T18:25:22.424825Z", - "iopub.status.idle": "2023-08-25T18:25:22.444138Z", - "shell.execute_reply": "2023-08-25T18:25:22.443466Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 1. +0.j , 0. +0.j , 0. +0.j , 0. +0.j ],\n", - " [ 0. +0.j , 0.707+0.j , 0. +0.j , -0. -0.707j],\n", - " [ 0. +0.j , 0. +0.j , 1. +0.j , 0. +0.j ],\n", - " [ 0. +0.j , 0. +0.707j, 0. +0.j , -0.707+0.j ]])" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SWAP gate\n", - "\n", - "The SWAP gate exchanges the two qubits. It transforms the basis vectors as\n", - "\n", - "$$\\left|00\\right\\rangle \\rightarrow \\left|00\\right\\rangle~,~\\left|01\\right\\rangle \\rightarrow \\left|10\\right\\rangle~,~\\left|10\\right\\rangle \\rightarrow \\left|01\\right\\rangle~,~\\left|11\\right\\rangle \\rightarrow \\left|11\\right\\rangle,$$\n", - "\n", - "which gives a matrix representation of the form\n", - "\n", - "$$\n", - "\\mathrm{SWAP} = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0\\\\\n", - "0 & 0 & 1 & 0\\\\\n", - "0 & 1 & 0 & 0\\\\\n", - "0 & 0 & 0 & 1\n", - "\\end{pmatrix}.\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:26.189896Z", - "start_time": "2021-07-31T05:13:26.184311Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.449068Z", - "iopub.status.busy": "2023-08-25T18:25:22.447851Z", - "iopub.status.idle": "2023-08-25T18:25:22.456538Z", - "shell.execute_reply": "2023-08-25T18:25:22.455927Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
          \n",
-       "q15_0: ─X─\n",
-       "        │ \n",
-       "q15_1: ─X─\n",
-       "          
" - ], - "text/plain": [ - " \n", - "q15_0: ─X─\n", - " │ \n", - "q15_1: ─X─\n", - " " - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.swap(q[0], q[1])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:28.555864Z", - "start_time": "2021-07-31T05:13:28.545777Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.461198Z", - "iopub.status.busy": "2023-08-25T18:25:22.460023Z", - "iopub.status.idle": "2023-08-25T18:25:22.474891Z", - "shell.execute_reply": "2023-08-25T18:25:22.474277Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]])" - ] - }, - "execution_count": 48, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Three-qubit gates
\n", - "\n", - "\n", - "There are two commonly-used three-qubit gates. For three qubits, the basis vectors are ordered as\n", - "\n", - "$$\\left|000\\right\\rangle, \\left|001\\right\\rangle, \\left|010\\right\\rangle, \\left|011\\right\\rangle, \\left|100\\right\\rangle, \\left|101\\right\\rangle, \\left|110\\right\\rangle, \\left|111\\right\\rangle,$$\n", - "\n", - "which, as bitstrings, represent the integers $0,1,2,\\cdots, 7$. Again, Qiskit uses a representation in which the first qubit is on the right-most side of the tensor product and the third qubit is on the left-most side:\n", - "\n", - "$$\\left|abc\\right\\rangle : \\underset{\\text{qubit 2}}{\\left|a\\right\\rangle}\\otimes \\underset{\\text{qubit 1}}{\\left|b\\right\\rangle}\\otimes \\underset{\\text{qubit 0}}{\\left|c\\right\\rangle}.$$" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Toffoli gate ($ccx$ gate)\n", - "\n", - "The [Toffoli gate](https://en.wikipedia.org/wiki/Quantum_logic_gate#Toffoli_(CCNOT)_gate) flips the third qubit if the first two qubits (LSB) are both $\\left|1\\right\\rangle$:\n", - "\n", - "$$\\left|abc\\right\\rangle \\rightarrow \\left|bc\\oplus a\\right\\rangle \\otimes \\left|b\\right\\rangle \\otimes \\left|c\\right\\rangle.$$\n", - "\n", - "In matrix form, the Toffoli gate is\n", - "$$\n", - "C_{CX} = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", - "0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", - "0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\\n", - "0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\\\\n", - "0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\\\\n", - "0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\\\\n", - "0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\\\\n", - "0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\n", - "\\end{pmatrix}.\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:31.317251Z", - "start_time": "2021-07-31T05:13:31.314100Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.479703Z", - "iopub.status.busy": "2023-08-25T18:25:22.478525Z", - "iopub.status.idle": "2023-08-25T18:25:22.483244Z", - "shell.execute_reply": "2023-08-25T18:25:22.482657Z" - } - }, - "outputs": [], - "source": [ - "q = QuantumRegister(3)" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:31.638567Z", - "start_time": "2021-07-31T05:13:31.633001Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.487813Z", - "iopub.status.busy": "2023-08-25T18:25:22.486656Z", - "iopub.status.idle": "2023-08-25T18:25:22.495885Z", - "shell.execute_reply": "2023-08-25T18:25:22.495279Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
            \n",
-       "q24_0: ──■──\n",
-       "         │  \n",
-       "q24_1: ──■──\n",
-       "       ┌─┴─┐\n",
-       "q24_2: ┤ X ├\n",
-       "       └───┘
" - ], - "text/plain": [ - " \n", - "q24_0: ──■──\n", - " │ \n", - "q24_1: ──■──\n", - " ┌─┴─┐\n", - "q24_2: ┤ X ├\n", - " └───┘" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.ccx(q[0], q[1], q[2])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:33.704467Z", - "start_time": "2021-07-31T05:13:33.686210Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.500503Z", - "iopub.status.busy": "2023-08-25T18:25:22.499330Z", - "iopub.status.idle": "2023-08-25T18:25:22.519282Z", - "shell.execute_reply": "2023-08-25T18:25:22.518629Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Controlled swap gate (Fredkin Gate)\n", - "\n", - "The [Fredkin gate](https://en.wikipedia.org/wiki/Quantum_logic_gate#Fredkin_(CSWAP)_gate), or the *controlled swap gate*, exchanges the second and third qubits if the first qubit (LSB) is $\\left|1\\right\\rangle$:\n", - "\n", - "$$ \\left|abc\\right\\rangle \\rightarrow \\begin{cases} \\left|bac\\right\\rangle~~\\text{if}~c=1 \\cr \\left|abc\\right\\rangle~~\\text{if}~c=0 \\end{cases}.$$\n", - "\n", - "In matrix form, the Fredkin gate is\n", - "\n", - "$$\n", - "C_{\\mathrm{SWAP}} = \n", - "\\begin{pmatrix}\n", - "1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", - "0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\\n", - "0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\\n", - "0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\\\\n", - "0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\\\\n", - "0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\\n", - "0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\\\\n", - "0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\n", - "\\end{pmatrix}.\n", - "$$" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:35.557144Z", - "start_time": "2021-07-31T05:13:35.551515Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.524224Z", - "iopub.status.busy": "2023-08-25T18:25:22.522983Z", - "iopub.status.idle": "2023-08-25T18:25:22.532151Z", - "shell.execute_reply": "2023-08-25T18:25:22.531523Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
          \n",
-       "q24_0: ─■─\n",
-       "        │ \n",
-       "q24_1: ─X─\n",
-       "        │ \n",
-       "q24_2: ─X─\n",
-       "          
" - ], - "text/plain": [ - " \n", - "q24_0: ─■─\n", - " │ \n", - "q24_1: ─X─\n", - " │ \n", - "q24_2: ─X─\n", - " " - ] - }, - "execution_count": 52, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q)\n", - "qc.cswap(q[0], q[1], q[2])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:38.233786Z", - "start_time": "2021-07-31T05:13:38.215070Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.536724Z", - "iopub.status.busy": "2023-08-25T18:25:22.535546Z", - "iopub.status.idle": "2023-08-25T18:25:22.556341Z", - "shell.execute_reply": "2023-08-25T18:25:22.555652Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.-0.j]])" - ] - }, - "execution_count": 53, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_unitary(qc, decimals=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Non-unitary operations
\n", - "\n", - "Now that we have gone through all the unitary operations in quantum circuits, we also have access to non-unitary operations. These include measurements, reset of qubits, and classical conditional operations." - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:39.580221Z", - "start_time": "2021-07-31T05:13:39.576248Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.561348Z", - "iopub.status.busy": "2023-08-25T18:25:22.560134Z", - "iopub.status.idle": "2023-08-25T18:25:22.565009Z", - "shell.execute_reply": "2023-08-25T18:25:22.564414Z" - } - }, - "outputs": [], - "source": [ - "q = QuantumRegister(1)\n", - "c = ClassicalRegister(1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Measurements\n", - "\n", - "We don't have access to all the information when we make a measurement in a quantum computer. The quantum state is projected onto the standard basis. Below are two examples showing a circuit that is prepared in a basis state and the quantum computer prepared in a superposition state." - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:40.203535Z", - "start_time": "2021-07-31T05:13:40.197117Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.569632Z", - "iopub.status.busy": "2023-08-25T18:25:22.568462Z", - "iopub.status.idle": "2023-08-25T18:25:22.577075Z", - "shell.execute_reply": "2023-08-25T18:25:22.576472Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
      ┌─┐\n",
-       " q27: ┤M├\n",
-       "      └╥┘\n",
-       "c0: 1/═╩═\n",
-       "       0 
" - ], - "text/plain": [ - " ┌─┐\n", - " q27: ┤M├\n", - " └╥┘\n", - "c0: 1/═╩═\n", - " 0 " - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q, c)\n", - "qc.measure(q, c)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:47.891765Z", - "start_time": "2021-07-31T05:13:47.879060Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.581673Z", - "iopub.status.busy": "2023-08-25T18:25:22.580458Z", - "iopub.status.idle": "2023-08-25T18:25:22.595267Z", - "shell.execute_reply": "2023-08-25T18:25:22.594622Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'0': 1024}" - ] - }, - "execution_count": 56, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "backend = BasicAer.get_backend('qasm_simulator')\n", - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_counts(qc)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " The simulator predicts that 100 percent of the time the classical register returns 0. " - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:49.501725Z", - "start_time": "2021-07-31T05:13:49.495404Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.600208Z", - "iopub.status.busy": "2023-08-25T18:25:22.598985Z", - "iopub.status.idle": "2023-08-25T18:25:22.608060Z", - "shell.execute_reply": "2023-08-25T18:25:22.607433Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
      ┌───┐┌─┐\n",
-       " q27: ┤ H ├┤M├\n",
-       "      └───┘└╥┘\n",
-       "c0: 1/══════╩═\n",
-       "            0 
" - ], - "text/plain": [ - " ┌───┐┌─┐\n", - " q27: ┤ H ├┤M├\n", - " └───┘└╥┘\n", - "c0: 1/══════╩═\n", - " 0 " - ] - }, - "execution_count": 57, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q, c)\n", - "qc.h(q)\n", - "qc.measure(q, c)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:51.913804Z", - "start_time": "2021-07-31T05:13:51.902540Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.612587Z", - "iopub.status.busy": "2023-08-25T18:25:22.611416Z", - "iopub.status.idle": "2023-08-25T18:25:22.627820Z", - "shell.execute_reply": "2023-08-25T18:25:22.627076Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'0': 501, '1': 523}" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_counts(qc)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " The simulator predicts that 50 percent of the time the classical register returns 0 or 1. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reset\n", - "It is also possible to `reset` qubits to the $\\left|0\\right\\rangle$ state in the middle of computation. Note that `reset` is not a Gate operation, since it is irreversible." - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:55.048748Z", - "start_time": "2021-07-31T05:13:55.043325Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.631481Z", - "iopub.status.busy": "2023-08-25T18:25:22.631099Z", - "iopub.status.idle": "2023-08-25T18:25:22.640332Z", - "shell.execute_reply": "2023-08-25T18:25:22.639656Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
           ┌─┐\n",
-       " q27: ─|0>─┤M├\n",
-       "           └╥┘\n",
-       "c0: 1/══════╩═\n",
-       "            0 
" - ], - "text/plain": [ - " ┌─┐\n", - " q27: ─|0>─┤M├\n", - " └╥┘\n", - "c0: 1/══════╩═\n", - " 0 " - ] - }, - "execution_count": 59, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q, c)\n", - "qc.reset(q[0])\n", - "qc.measure(q, c)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:57.317513Z", - "start_time": "2021-07-31T05:13:57.305706Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.644768Z", - "iopub.status.busy": "2023-08-25T18:25:22.643441Z", - "iopub.status.idle": "2023-08-25T18:25:22.729431Z", - "shell.execute_reply": "2023-08-25T18:25:22.728752Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'0': 1024}" - ] - }, - "execution_count": 60, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_counts(qc)" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:13:57.866910Z", - "start_time": "2021-07-31T05:13:57.860240Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.733380Z", - "iopub.status.busy": "2023-08-25T18:25:22.732854Z", - "iopub.status.idle": "2023-08-25T18:25:22.742233Z", - "shell.execute_reply": "2023-08-25T18:25:22.741653Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
      ┌───┐     ┌─┐\n",
-       " q27: ┤ H ├─|0>─┤M├\n",
-       "      └───┘     └╥┘\n",
-       "c0: 1/═══════════╩═\n",
-       "                 0 
" - ], - "text/plain": [ - " ┌───┐ ┌─┐\n", - " q27: ┤ H ├─|0>─┤M├\n", - " └───┘ └╥┘\n", - "c0: 1/═══════════╩═\n", - " 0 " - ] - }, - "execution_count": 61, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q, c)\n", - "qc.h(q)\n", - "qc.reset(q[0])\n", - "qc.measure(q, c)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 62, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:01.210349Z", - "start_time": "2021-07-31T05:14:01.065350Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.745843Z", - "iopub.status.busy": "2023-08-25T18:25:22.745356Z", - "iopub.status.idle": "2023-08-25T18:25:22.861373Z", - "shell.execute_reply": "2023-08-25T18:25:22.860686Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'0': 1024}" - ] - }, - "execution_count": 62, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_counts(qc)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we see that for both of these circuits the simulator always predicts that the output is 100 percent in the 0 state." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Conditional operations\n", - "It is also possible to do operations conditioned on the state of the classical register" - ] - }, - { - "cell_type": "code", - "execution_count": 63, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:03.421890Z", - "start_time": "2021-07-31T05:14:03.416090Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.865366Z", - "iopub.status.busy": "2023-08-25T18:25:22.864905Z", - "iopub.status.idle": "2023-08-25T18:25:22.874474Z", - "shell.execute_reply": "2023-08-25T18:25:22.873784Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
       ┌───┐ ┌─┐\n",
-       " q27: ─┤ X ├─┤M├\n",
-       "       └─╥─┘ └╥┘\n",
-       "      ┌──╨──┐ ║ \n",
-       "c0: 1/╡ 0x0 ╞═╩═\n",
-       "      └─────┘ 0 
" - ], - "text/plain": [ - " ┌───┐ ┌─┐\n", - " q27: ─┤ X ├─┤M├\n", - " └─╥─┘ └╥┘\n", - " ┌──╨──┐ ║ \n", - "c0: 1/╡ 0x0 ╞═╩═\n", - " └─────┘ 0 " - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q, c)\n", - "qc.x(q[0]).c_if(c, 0)\n", - "qc.measure(q,c)\n", - "qc.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here the classical bit always takes the value 0 so the qubit state is always flipped. " - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:05.763405Z", - "start_time": "2021-07-31T05:14:05.753665Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.878932Z", - "iopub.status.busy": "2023-08-25T18:25:22.877802Z", - "iopub.status.idle": "2023-08-25T18:25:22.892229Z", - "shell.execute_reply": "2023-08-25T18:25:22.891486Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'1': 1024}" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_counts(qc)" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:07.187387Z", - "start_time": "2021-07-31T05:14:07.180366Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.900265Z", - "iopub.status.busy": "2023-08-25T18:25:22.899059Z", - "iopub.status.idle": "2023-08-25T18:25:22.909216Z", - "shell.execute_reply": "2023-08-25T18:25:22.908624Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
      ┌───┐┌─┐ ┌───┐ ┌─┐\n",
-       " q27: ┤ H ├┤M├─┤ X ├─┤M├\n",
-       "      └───┘└╥┘ └─╥─┘ └╥┘\n",
-       "            ║ ┌──╨──┐ ║ \n",
-       "c0: 1/══════╩═╡ 0x0 ╞═╩═\n",
-       "            0 └─────┘ 0 
" - ], - "text/plain": [ - " ┌───┐┌─┐ ┌───┐ ┌─┐\n", - " q27: ┤ H ├┤M├─┤ X ├─┤M├\n", - " └───┘└╥┘ └─╥─┘ └╥┘\n", - " ║ ┌──╨──┐ ║ \n", - "c0: 1/══════╩═╡ 0x0 ╞═╩═\n", - " 0 └─────┘ 0 " - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(q, c)\n", - "qc.h(q)\n", - "qc.measure(q,c)\n", - "qc.x(q[0]).c_if(c, 0)\n", - "qc.measure(q,c)\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 66, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:09.366351Z", - "start_time": "2021-07-31T05:14:09.199972Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:22.913560Z", - "iopub.status.busy": "2023-08-25T18:25:22.912448Z", - "iopub.status.idle": "2023-08-25T18:25:23.047462Z", - "shell.execute_reply": "2023-08-25T18:25:23.046714Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'1': 1024}" - ] - }, - "execution_count": 66, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "job = backend.run(transpile(qc, backend))\n", - "job.result().get_counts(qc)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here the classical bit by the first measurement is random but the conditional operation results in the qubit being deterministically put into $\\left|1\\right\\rangle$." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Arbitrary initialization
\n", - "What if we want to initialize a qubit register to an arbitrary state? An arbitrary state for $n$ qubits may be specified by a vector of $2^n$ amplitudes, where the sum of amplitude-norms-squared equals 1. For example, the following three-qubit state can be prepared:\n", - "\n", - "$$\\left|\\psi\\right\\rangle = \\frac{i}{4}\\left|000\\right\\rangle + \\frac{1}{\\sqrt{8}}\\left|001\\right\\rangle + \\frac{1+i}{4}\\left|010\\right\\rangle + \\frac{1+2i}{\\sqrt{8}}\\left|101\\right\\rangle + \\frac{1}{4}\\left|110\\right\\rangle$$" - ] - }, - { - "cell_type": "code", - "execution_count": 67, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:12.127664Z", - "start_time": "2021-07-31T05:14:12.118291Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:23.050953Z", - "iopub.status.busy": "2023-08-25T18:25:23.050533Z", - "iopub.status.idle": "2023-08-25T18:25:23.059498Z", - "shell.execute_reply": "2023-08-25T18:25:23.058934Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
       ┌───────────────────────────────────────────────────────────────────┐\n",
-       "q41_0: ┤0                                                                  ├\n",
-       "       │                                                                   │\n",
-       "q41_1: ┤1 Initialize(0.25j,0.35355,0.25+0.25j,0,0,0.35355+0.70711j,0.25,0) ├\n",
-       "       │                                                                   │\n",
-       "q41_2: ┤2                                                                  ├\n",
-       "       └───────────────────────────────────────────────────────────────────┘
" - ], - "text/plain": [ - " ┌───────────────────────────────────────────────────────────────────┐\n", - "q41_0: ┤0 ├\n", - " │ │\n", - "q41_1: ┤1 Initialize(0.25j,0.35355,0.25+0.25j,0,0,0.35355+0.70711j,0.25,0) ├\n", - " │ │\n", - "q41_2: ┤2 ├\n", - " └───────────────────────────────────────────────────────────────────┘" - ] - }, - "execution_count": 67, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Initializing a three-qubit quantum state\n", - "import math\n", - "desired_vector = [\n", - " 1 / math.sqrt(16) * complex(0, 1),\n", - " 1 / math.sqrt(8) * complex(1, 0),\n", - " 1 / math.sqrt(16) * complex(1, 1),\n", - " 0,\n", - " 0,\n", - " 1 / math.sqrt(8) * complex(1, 2),\n", - " 1 / math.sqrt(16) * complex(1, 0),\n", - " 0]\n", - "\n", - "\n", - "q = QuantumRegister(3)\n", - "\n", - "qc = QuantumCircuit(q)\n", - "\n", - "qc.initialize(desired_vector, [q[0],q[1],q[2]])\n", - "qc.draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:16.873547Z", - "start_time": "2021-07-31T05:14:16.800233Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:23.062747Z", - "iopub.status.busy": "2023-08-25T18:25:23.062361Z", - "iopub.status.idle": "2023-08-25T18:25:23.124744Z", - "shell.execute_reply": "2023-08-25T18:25:23.123743Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1.80411242e-16+2.50000000e-01j, 3.53553391e-01-1.04083409e-16j,\n", - " 2.50000000e-01+2.50000000e-01j, 0.00000000e+00+0.00000000e+00j,\n", - " 0.00000000e+00+0.00000000e+00j, 3.53553391e-01+7.07106781e-01j,\n", - " 2.50000000e-01-5.55111512e-17j, 0.00000000e+00+0.00000000e+00j])" - ] - }, - "execution_count": 68, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "backend = BasicAer.get_backend('statevector_simulator')\n", - "job = backend.run(transpile(qc, backend))\n", - "qc_state = job.result().get_statevector(qc)\n", - "qc_state " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Fidelity](https://en.wikipedia.org/wiki/Fidelity_of_quantum_states) is useful to check whether two states are the same or not.\n", - "For quantum (pure) states $\\left|\\psi_1\\right\\rangle$ and $\\left|\\psi_2\\right\\rangle$, the fidelity is\n", - "\n", - "$$\n", - "F\\left(\\left|\\psi_1\\right\\rangle,\\left|\\psi_2\\right\\rangle\\right) = \\left|\\left\\langle\\psi_1\\middle|\\psi_2\\right\\rangle\\right|^2.\n", - "$$\n", - "\n", - "The fidelity is equal to $1$ if and only if two states are equal." - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:19.163449Z", - "start_time": "2021-07-31T05:14:19.157945Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:23.129326Z", - "iopub.status.busy": "2023-08-25T18:25:23.128808Z", - "iopub.status.idle": "2023-08-25T18:25:23.136930Z", - "shell.execute_reply": "2023-08-25T18:25:23.136215Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "1.0" - ] - }, - "execution_count": 69, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "state_fidelity(desired_vector,qc_state)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Further details:\n", - "\n", - "How does the desired state get generated behind the scenes? There are multiple methods for doing this. Qiskit uses a [method proposed by Shende et al](https://arxiv.org/abs/quant-ph/0406176). Here, the idea is to assume the quantum register to have started from our desired state, and construct a circuit that takes it to the $\\left|00..0\\right\\rangle$ state. The initialization circuit is then the reverse of such circuit.\n", - "\n", - "To take an arbitrary quantum state to the zero state in the computational basis, we perform an iterative procedure that disentangles qubits from the register one-by-one. We know that any arbitrary single-qubit state $\\left|\\rho\\right\\rangle$ can be taken to the $\\left|0\\right\\rangle$ state using a $\\phi$-degree rotation about the Z axis followed by a $\\theta$-degree rotation about the Y axis:\n", - "\n", - "$$R_y(-\\theta)R_z(-\\phi)\\left|\\rho\\right\\rangle = re^{it}\\left|0\\right\\rangle$$\n", - "\n", - "Since now we are dealing with $n$ qubits instead of just 1, we must factorize the state vector to separate the Least Significant Bit (LSB):\n", - "\n", - "$$\\begin{align*}\n", - " \\left|\\psi\\right\\rangle =& \\alpha_{0_0}\\left|00..00\\right\\rangle + \\alpha_{0_1}\\left|00..01\\right\\rangle + \\alpha_{1_0}\\left|00..10\\right\\rangle + \\alpha_{1_1}\\left|00..11\\right\\rangle + ... \\\\&+ \\alpha_{(2^{n-1}-1)_0}\\left|11..10\\right\\rangle + \\alpha_{(2^{n-1}-1)_1}\\left|11..11\\right\\rangle \\\\\n", - "=& \\left|00..0\\right\\rangle (\\alpha_{0_0}\\left|0\\right\\rangle + \\alpha_{0_1}\\left|1\\right\\rangle) + \\left|00..1\\right\\rangle (\\alpha_{1_0}\\left|0\\right\\rangle + \\alpha_{1_1}\\left|1\\right\\rangle) + ... \\\\&+ \\left|11..1\\right\\rangle (\\alpha_{(2^{n-1}-1)_0}(\\left|0\\right\\rangle + \\alpha_{(2^{n-1}-1)_1}\\left|1\\right\\rangle) \\\\\n", - "=& \\left|00..0\\right\\rangle\\left|\\rho_0\\right\\rangle + \\left|00..1\\right\\rangle\\left|\\rho_1\\right\\rangle + ... + \\left|11..1\\right\\rangle\\left|\\rho_{2^{n-1}-1}\\right\\rangle\n", - "\\end{align*}$$\n", - "\n", - "Now each of the single-qubit states $\\left|\\rho_0\\right\\rangle, ..., \\left|\\rho_{2^{n-1}-1}\\right\\rangle$ can be taken to $\\left|0\\right\\rangle$ by finding appropriate $\\phi$ and $\\theta$ angles per the equation above. Doing this simultaneously on all states amounts to the following unitary, which disentangles the LSB:\n", - "\n", - "$$U = \\begin{pmatrix} \n", - "R_{y}(-\\theta_0)R_{z}(-\\phi_0) & & & &\\\\ \n", - "& R_{y}(-\\theta_1)R_{z}(-\\phi_1) & & &\\\\\n", - "& . & & &\\\\\n", - "& & . & &\\\\\n", - "& & & & R_y(-\\theta_{2^{n-1}-1})R_z(-\\phi_{2^{n-1}-1})\n", - "\\end{pmatrix} $$\n", - "\n", - "Hence,\n", - "\n", - "$$U\\left|\\psi\\right\\rangle = \\begin{pmatrix} r_0e^{it_0}\\\\ r_1e^{it_1}\\\\ . \\\\ . \\\\ r_{2^{n-1}-1}e^{it_{2^{n-1}-1}} \\end{pmatrix}\\otimes\\left|0\\right\\rangle$$\n", - "\n", - "\n", - "U can be implemented as a \"quantum multiplexor\" gate, since it is a block diagonal matrix. In the quantum multiplexor formalism, a block diagonal matrix of size $2^n \\times 2^n$, and consisting of $2^s$ blocks, is equivalent to a multiplexor with $s$ select qubits and $n-s$ data qubits. Depending on the state of the select qubits, the corresponding blocks are applied to the data qubits. A multiplexor of this kind can be implemented after recursive decomposition to primitive gates of cx, rz and ry." - ] - }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:14:22.630626Z", - "start_time": "2021-07-31T05:14:21.651868Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:23.140694Z", - "iopub.status.busy": "2023-08-25T18:25:23.140232Z", - "iopub.status.idle": "2023-08-25T18:25:23.262369Z", - "shell.execute_reply": "2023-08-25T18:25:23.261610Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:23 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "2a9540e2970f4a5dba213e1ead87fec3": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "aeed539f49c5413cba794ef0586082a5": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_fb93f19c96824cf791ace2fc8a5d4c48", - "placeholder": "​", - "style": "IPY_MODEL_2a9540e2970f4a5dba213e1ead87fec3", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "fb93f19c96824cf791ace2fc8a5d4c48": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/tutorials/circuits_advanced/01_advanced_circuits.ipynb b/docs/tutorials/circuits_advanced/01_advanced_circuits.ipynb deleted file mode 100644 index 62901099a0ec..000000000000 --- a/docs/tutorials/circuits_advanced/01_advanced_circuits.ipynb +++ /dev/null @@ -1,1012 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Advanced Circuits" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T04:57:22.790689Z", - "start_time": "2021-07-31T04:57:20.366197Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:23.874767Z", - "iopub.status.busy": "2023-08-25T18:25:23.873164Z", - "iopub.status.idle": "2023-08-25T18:25:24.310732Z", - "shell.execute_reply": "2023-08-25T18:25:24.309968Z" - } - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "from qiskit import *" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Opaque gates" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T04:57:22.795419Z", - "start_time": "2021-07-31T04:57:22.792399Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:24.316646Z", - "iopub.status.busy": "2023-08-25T18:25:24.315006Z", - "iopub.status.idle": "2023-08-25T18:25:24.320567Z", - "shell.execute_reply": "2023-08-25T18:25:24.319972Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.circuit import Gate\n", - "\n", - "my_gate = Gate(name='my_gate', num_qubits=2, params=[])" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T04:57:22.809920Z", - "start_time": "2021-07-31T04:57:22.798318Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:24.325062Z", - "iopub.status.busy": "2023-08-25T18:25:24.323888Z", - "iopub.status.idle": "2023-08-25T18:25:24.355410Z", - "shell.execute_reply": "2023-08-25T18:25:24.354740Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌──────────┐            \n",
-       "q_0: ┤0         ├────────────\n",
-       "     │  my_gate │┌──────────┐\n",
-       "q_1: ┤1         ├┤0         ├\n",
-       "     └──────────┘│  my_gate │\n",
-       "q_2: ────────────┤1         ├\n",
-       "                 └──────────┘
" - ], - "text/plain": [ - " ┌──────────┐ \n", - "q_0: ┤0 ├────────────\n", - " │ my_gate │┌──────────┐\n", - "q_1: ┤1 ├┤0 ├\n", - " └──────────┘│ my_gate │\n", - "q_2: ────────────┤1 ├\n", - " └──────────┘" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qr = QuantumRegister(3, 'q')\n", - "circ = QuantumCircuit(qr)\n", - "circ.append(my_gate, [qr[0], qr[1]])\n", - "circ.append(my_gate, [qr[1], qr[2]])\n", - "\n", - "circ.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Composite Gates" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T04:57:35.232865Z", - "start_time": "2021-07-31T04:57:35.219747Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:24.360596Z", - "iopub.status.busy": "2023-08-25T18:25:24.359326Z", - "iopub.status.idle": "2023-08-25T18:25:24.376686Z", - "shell.execute_reply": "2023-08-25T18:25:24.376018Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌───┐                       \n",
-       "q_0: ┤ H ├──■────────────────────\n",
-       "     └───┘┌─┴─┐     ┌───────────┐\n",
-       "q_1: ─────┤ X ├──■──┤0          ├\n",
-       "          └───┘┌─┴─┐│  sub_circ │\n",
-       "q_2: ──────────┤ X ├┤1          ├\n",
-       "               └───┘└───────────┘
" - ], - "text/plain": [ - " ┌───┐ \n", - "q_0: ┤ H ├──■────────────────────\n", - " └───┘┌─┴─┐ ┌───────────┐\n", - "q_1: ─────┤ X ├──■──┤0 ├\n", - " └───┘┌─┴─┐│ sub_circ │\n", - "q_2: ──────────┤ X ├┤1 ├\n", - " └───┘└───────────┘" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Build a sub-circuit\n", - "sub_q = QuantumRegister(2)\n", - "sub_circ = QuantumCircuit(sub_q, name='sub_circ')\n", - "sub_circ.h(sub_q[0])\n", - "sub_circ.crz(1, sub_q[0], sub_q[1])\n", - "sub_circ.barrier()\n", - "sub_circ.id(sub_q[1])\n", - "sub_circ.u(1, 2, -2, sub_q[0])\n", - "\n", - "# Convert to a gate and stick it into an arbitrary place in the bigger circuit\n", - "sub_inst = sub_circ.to_instruction()\n", - "\n", - "qr = QuantumRegister(3, 'q')\n", - "circ = QuantumCircuit(qr)\n", - "circ.h(qr[0])\n", - "circ.cx(qr[0], qr[1])\n", - "circ.cx(qr[1], qr[2])\n", - "circ.append(sub_inst, [qr[1], qr[2]])\n", - "\n", - "circ.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Circuits are not immediately decomposed upon conversion `to_instruction` to allow circuit design at higher levels of abstraction.\n", - "When desired, or before compilation, sub-circuits will be decomposed via the `decompose` method." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T04:57:37.297451Z", - "start_time": "2021-07-31T04:57:37.287919Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:24.380024Z", - "iopub.status.busy": "2023-08-25T18:25:24.379405Z", - "iopub.status.idle": "2023-08-25T18:25:24.415974Z", - "shell.execute_reply": "2023-08-25T18:25:24.415209Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌─────────┐                                        \n",
-       "q_0: ┤ U2(0,π) ├──■─────────────────────────────────────\n",
-       "     └─────────┘┌─┴─┐     ┌───┐          ░ ┌───────────┐\n",
-       "q_1: ───────────┤ X ├──■──┤ H ├────■─────░─┤ U(1,2,-2) ├\n",
-       "                └───┘┌─┴─┐└───┘┌───┴───┐ ░ └───┬───┬───┘\n",
-       "q_2: ────────────────┤ X ├─────┤ Rz(1) ├─░─────┤ I ├────\n",
-       "                     └───┘     └───────┘ ░     └───┘    
" - ], - "text/plain": [ - " ┌─────────┐ \n", - "q_0: ┤ U2(0,π) ├──■─────────────────────────────────────\n", - " └─────────┘┌─┴─┐ ┌───┐ ░ ┌───────────┐\n", - "q_1: ───────────┤ X ├──■──┤ H ├────■─────░─┤ U(1,2,-2) ├\n", - " └───┘┌─┴─┐└───┘┌───┴───┐ ░ └───┬───┬───┘\n", - "q_2: ────────────────┤ X ├─────┤ Rz(1) ├─░─────┤ I ├────\n", - " └───┘ └───────┘ ░ └───┘ " - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "decomposed_circ = circ.decompose() # Does not modify original circuit\n", - "decomposed_circ.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Parameterized circuits" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T04:57:40.495951Z", - "start_time": "2021-07-31T04:57:39.017413Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:24.419777Z", - "iopub.status.busy": "2023-08-25T18:25:24.419211Z", - "iopub.status.idle": "2023-08-25T18:25:25.656081Z", - "shell.execute_reply": "2023-08-25T18:25:25.655195Z" - }, - "tags": [ - "nbsphinx-thumbnail" - ] - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.circuit import Parameter\n", - "\n", - "theta = Parameter('θ')\n", - "\n", - "n = 5\n", - "\n", - "qc = QuantumCircuit(5, 1)\n", - "\n", - "qc.h(0)\n", - "for i in range(n-1):\n", - " qc.cx(i, i+1)\n", - "\n", - "qc.barrier()\n", - "qc.rz(theta, range(5))\n", - "qc.barrier()\n", - "\n", - "for i in reversed(range(n-1)):\n", - " qc.cx(i, i+1)\n", - "qc.h(0)\n", - "qc.measure(0, 0)\n", - "\n", - "qc.draw('mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can inspect the circuit's parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T04:57:44.155858Z", - "start_time": "2021-07-31T04:57:44.152156Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:25.660788Z", - "iopub.status.busy": "2023-08-25T18:25:25.660202Z", - "iopub.status.idle": "2023-08-25T18:25:25.666910Z", - "shell.execute_reply": "2023-08-25T18:25:25.666094Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ParameterView([Parameter(θ)])\n" - ] - } - ], - "source": [ - "print(qc.parameters)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Binding parameters to values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All circuit parameters must be bound before sending the circuit to a backend. This can be done as follows:\n", - "- The `bind_parameters` method accepts a dictionary mapping `Parameter`s to values, and returns a new circuit with each parameter replaced by its corresponding value. Partial binding is supported, in which case the returned circuit will be parameterized by any `Parameter`s that were not mapped to a value." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T04:57:45.686901Z", - "start_time": "2021-07-31T04:57:45.566396Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:25.671158Z", - "iopub.status.busy": "2023-08-25T18:25:25.670696Z", - "iopub.status.idle": "2023-08-25T18:25:25.722090Z", - "shell.execute_reply": "2023-08-25T18:25:25.721262Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌───┐                     ░ ┌────────┐ ░                     ┌───┐┌─┐\n",
-       "q_0: ┤ H ├──■──────────────────░─┤ Rz(2π) ├─░──────────────────■──┤ H ├┤M├\n",
-       "     └───┘┌─┴─┐                ░ ├────────┤ ░                ┌─┴─┐└───┘└╥┘\n",
-       "q_1: ─────┤ X ├──■─────────────░─┤ Rz(2π) ├─░─────────────■──┤ X ├──────╫─\n",
-       "          └───┘┌─┴─┐           ░ ├────────┤ ░           ┌─┴─┐└───┘      ║ \n",
-       "q_2: ──────────┤ X ├──■────────░─┤ Rz(2π) ├─░────────■──┤ X ├───────────╫─\n",
-       "               └───┘┌─┴─┐      ░ ├────────┤ ░      ┌─┴─┐└───┘           ║ \n",
-       "q_3: ───────────────┤ X ├──■───░─┤ Rz(2π) ├─░───■──┤ X ├────────────────╫─\n",
-       "                    └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘                ║ \n",
-       "q_4: ────────────────────┤ X ├─░─┤ Rz(2π) ├─░─┤ X ├─────────────────────╫─\n",
-       "                         └───┘ ░ └────────┘ ░ └───┘                     ║ \n",
-       "c: 1/═══════════════════════════════════════════════════════════════════╩═\n",
-       "                                                                        0 
" - ], - "text/plain": [ - " ┌───┐ ░ ┌────────┐ ░ ┌───┐┌─┐\n", - "q_0: ┤ H ├──■──────────────────░─┤ Rz(2π) ├─░──────────────────■──┤ H ├┤M├\n", - " └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘└╥┘\n", - "q_1: ─────┤ X ├──■─────────────░─┤ Rz(2π) ├─░─────────────■──┤ X ├──────╫─\n", - " └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘ ║ \n", - "q_2: ──────────┤ X ├──■────────░─┤ Rz(2π) ├─░────────■──┤ X ├───────────╫─\n", - " └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘ ║ \n", - "q_3: ───────────────┤ X ├──■───░─┤ Rz(2π) ├─░───■──┤ X ├────────────────╫─\n", - " └───┘┌─┴─┐ ░ ├────────┤ ░ ┌─┴─┐└───┘ ║ \n", - "q_4: ────────────────────┤ X ├─░─┤ Rz(2π) ├─░─┤ X ├─────────────────────╫─\n", - " └───┘ ░ └────────┘ ░ └───┘ ║ \n", - "c: 1/═══════════════════════════════════════════════════════════════════╩═\n", - " 0 " - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import numpy as np\n", - "\n", - "theta_range = np.linspace(0, 2 * np.pi, 128)\n", - "\n", - "circuits = [qc.bind_parameters({theta: theta_val})\n", - " for theta_val in theta_range]\n", - "\n", - "circuits[-1].draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:01:45.524926Z", - "start_time": "2021-07-31T05:01:44.775721Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:25.726290Z", - "iopub.status.busy": "2023-08-25T18:25:25.725790Z", - "iopub.status.idle": "2023-08-25T18:25:28.636459Z", - "shell.execute_reply": "2023-08-25T18:25:28.635599Z" - } - }, - "outputs": [], - "source": [ - "backend = BasicAer.get_backend('qasm_simulator')\n", - "job = backend.run(transpile(circuits, backend))\n", - "counts = job.result().get_counts()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the example circuit, we apply a global $R_z(\\theta)$ rotation on a five-qubit entangled state, and so expect to see oscillation in qubit-0 at $5\\theta$." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:01:48.240446Z", - "start_time": "2021-07-31T05:01:47.870931Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:28.640421Z", - "iopub.status.busy": "2023-08-25T18:25:28.639982Z", - "iopub.status.idle": "2023-08-25T18:25:28.884438Z", - "shell.execute_reply": "2023-08-25T18:25:28.883653Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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Hw6KFWbv3r0AgIULFx73u6amJlRWVhrPYRhi586d+NznPjdhf+3atS4eEaMql1xyCfbv34/169fjpptuwhe/+EXjd5TR+c53voM5c+a4dISMSpx//vnYvn07+vr68F//9V/4y7/8S2zZsgUNDQ28QsWckPb2djz//PNobm6ekKBRcfxh+YTChEIhAEIuMRXV1dXGcxgGEDr0Xbt2GQOPruv44IMPePmbOSE7duwAAJx11lkTfr59+3YEg0EOiBmDsrIyLFiwABdccAF+9rOfwefz4f777wfAK1TM1MRiMdx4442IRCK45557jiv0Vm384aCYYfKIPXv2IBqNYsmSJQAATdOwZMkSfPGLX8S+fftcPjpGRd5++21omjYhgBkaGsKBAweOu1ExjBVd1xGJRACIzB6NO7TPQXFhk0wm8dnPfhabN2/GF77wBdx4443HPUe18YflEwpDGeITZYOHhoZQW1vr5CExinPmmWdisqHMtm3bXDoaxgu8/fbbmDdvHqqqqoyfbd++Hbqu4+yzz3bxyBiV+PrXv46Pf/zjmDlzJvr7+/GTn/wER44cwfXXX88rVMxxJJNJfO5zn8PDDz+Mz3zmM7jvvvumfJ5q4w9nihWGtMRT6Ya7urowMjIypd6YYRgmHdrb2zEwMHDczeett94CAA6KGYOjR4/ihhtuwKJFi3DNNdegu7sbL730Es444wxeoWImkEwmccstt2D9+vX49Kc/jQceeAA+3/HhporjD2eKFWbVqlW4++678eyzz+KGG26Y8LtnnnnGeA7DMEw2nEzPB3BQzJj88pe/POHveIWKISggfvDBB7FmzRr88pe/PGHDKBXHH84UK8zll1+OefPm4eGHHza+JICQU3z3u99FcXExbrrpJvcOkFGOdDwfuRqcId5++20Ax9983nvvPQDA4sWLHT8mhmG8CUkmHnzwQXzqU5/CQw89dNIOqiqOP5wpVphAIICf/exnWL16NS6++OIp2zxzZThjhRtUMplAmZrJN6X+/n4AovHC0qVLDS9ahmGYE/Gd73wH69evR2VlJRYtWoR/+Zd/Oe45n/jEJ4xmZEqOPzqjPFu2bNGvuuoqvbq6Wi8rK9NXrFihP/roo24fFqMo3/3ud/Vly5bplZWV+rRp0/RPfepT+oEDB9w+LEZBFi1apFdXV+vJZHLCz7/1rW/plZWVem1trf7qq6+6dHSMKgCQ8o/Jb26++eZTfgfuv/9+4/kqjj+arnNqiWHyiauuugqf/vSnsXz5ckQiEXzta19DR0cHdu7cOaGbEMMwDMMwJhwUM0yec+DAAcybNw87duxg31mGYaRw991347e//S12796N8vJyrFq1Cv/2b//Gkj7G03ChHcPkOeRzzbpQhmFksWnTJnz5y1/Gli1b8PTTT6O/vx9XX3014vG424fGMFnDmWKGyWMSiQRWr16NkpISPPnkk24fDsMweQqvSDH5AAsMGSZP0XUdX/ziF9He3o5XXnnF7cNhGCaP4RUpJh/gTDHD5CG6ruNLX/oSnn76aWzevBmzZs1y+5AYhslTeEWKyRc4U8wweYau61i7di2efPJJbNq0iQNihmFsg1ekmHyCg2KGyTPWrl2LRx55BI8//jjKysrQ1dUFQCxrFhcXu3x0DMPkC7Qi9fzzz2Pz5s1obGx0+5AYJidYPsEwecaJ2ji/8MILuOSSS5w9GIZh8hJakXriiSewadMmzJ071+1DYpic4Uwxw+QZPM9lGMZueEWKyUc4U8wwDMMwTEbwihSTj3CmmGEYhmGYjOB8GpOPcEc7hmEYhmEYpuDhoJhhGIZhGIYpeDgoZhiGYRiGYQoeDooZhmEYhmGYgoeDYoZhGIZhGKbg4aCYYRiGYRiGKXg4KGYYhmEYhmEKHg6KGYZhGIZhmIKHg2KGYRgG27dvx7XXXotgMIhgMIhrr70WO3bscPuwGIZhHIPbPDMMwxQ4b7zxBlatWoWKigrccMMNSCaTeOSRRxCJRPDiiy9ixYoVbh8iwzCM7XBQzDAMU8Akk0ksWbIEXV1d2LZtG+bMmQMA2Lt3L5YvX46Wlhbs3LkTPh8vLDIMk9/wKMcwDFPAPPfcc3j//ffx93//90ZADAALFy7E2rVr8d577+H555937wAZhmEcgoNihmGYAuaPf/wjAODaa6897ndXXnnlhOcwDMPkMxwUMwzDFDA7d+4EACxZsuS43y1dunTCcxiGYfKZgNsHwDAMw7hHd3c3iouLcc8990z5e7/fj+7uboePimEYxnm40I5hGKaAmT9/Pj744INTPmffvn0OHRHDMIw7cKaYYRimgCkrK4Pf70c8Hp/y94FAAOXl5Q4fFcMwjPOwpphhGKaAaWpqQiKRQDgcPu53IyMjSCQSmD59ugtHxjAM4ywcFDMMwxQwVEw3lYRi7969AKYuwmMYhsk3OChmGIYpYK6++moAwq94MmTFds011zh6TAzDMG7AhXYMwzAFTDKZxFlnnYXe3l68+eabmDlzJgDg4MGDOP/889Hc3IwdO3ZwRzuGYfIeDooZhmEKnLa2NqxatQoVFRW44YYboOs6HnnkEYTDYWzevBnnn3++24fIMAxjOxwUMwzDMNixYwfuuusuvPzyywCAD3/4w/jud7+Ls88+2+UjYxiGcQYOihmGYRiGYZiCh0ViDMMwDMMwTMHDQTHDMAzDMAxT8HBQzDAMwzAMwxQ8HBQzDMMwDMMwBQ8HxQzDMAzDMEzBw0ExwzAMwzAMU/BwUMwwDMMwDMMUPBwUMwzDMAzDMAUPB8UMwzAMwzBMwcNBMcMwDMMwDFPwcFDMMAzDMAzDFDwcFDMMwzAMwzAFDwfFDMMwDMMwTMHz/wEhQzeYaxnhGAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "fig = plt.figure(figsize=(8,6))\n", - "ax = fig.add_subplot(111)\n", - "\n", - "ax.plot(theta_range, list(map(lambda c: c.get('0', 0), counts)), '.-', label='0')\n", - "ax.plot(theta_range, list(map(lambda c: c.get('1', 0), counts)), '.-', label='1') \n", - "\n", - "ax.set_xticks([i * np.pi / 2 for i in range(5)])\n", - "ax.set_xticklabels(['0', r'$\\frac{\\pi}{2}$', r'$\\pi$', r'$\\frac{3\\pi}{2}$', r'$2\\pi$'], fontsize=14)\n", - "ax.set_xlabel('θ', fontsize=14)\n", - "ax.set_ylabel('Counts', fontsize=14)\n", - "ax.legend(fontsize=14)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reducing compilation cost" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Compiling over a parameterized circuit prior to binding can, in some cases, significantly reduce compilation time as compared to compiling over a set of bound circuits." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:01:54.386016Z", - "start_time": "2021-07-31T05:01:51.644668Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:28.889039Z", - "iopub.status.busy": "2023-08-25T18:25:28.888283Z", - "iopub.status.idle": "2023-08-25T18:25:48.569886Z", - "shell.execute_reply": "2023-08-25T18:25:48.569052Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Time compiling over set of bound circuits: 19.617682218551636\n" - ] - } - ], - "source": [ - "import time\n", - "from itertools import combinations\n", - "from qiskit.compiler import assemble\n", - "from qiskit.providers.fake_provider import FakeVigo\n", - "\n", - "start = time.time()\n", - "qcs = []\n", - "\n", - "theta_range = np.linspace(0, 2*np.pi, 32)\n", - "\n", - "for n in theta_range:\n", - " qc = QuantumCircuit(5)\n", - "\n", - " for k in range(8):\n", - " for i,j in combinations(range(5), 2):\n", - " qc.cx(i,j)\n", - " qc.rz(n, range(5))\n", - " for i,j in combinations(range(5), 2):\n", - " qc.cx(i,j)\n", - "\n", - " qcs.append(qc)\n", - " \n", - "compiled_circuits = transpile(qcs, backend=FakeVigo())\n", - "qobj = assemble(compiled_circuits, backend=FakeVigo())\n", - "\n", - "end = time.time()\n", - "print('Time compiling over set of bound circuits: ', end-start)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:02:12.453279Z", - "start_time": "2021-07-31T05:02:10.997358Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:48.573408Z", - "iopub.status.busy": "2023-08-25T18:25:48.573122Z", - "iopub.status.idle": "2023-08-25T18:25:49.602664Z", - "shell.execute_reply": "2023-08-25T18:25:49.601065Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Time compiling over parameterized circuit, then binding: 1.0221805572509766\n" - ] - } - ], - "source": [ - "start = time.time()\n", - "qc = QuantumCircuit(5)\n", - "theta = Parameter('theta')\n", - "\n", - "for k in range(8):\n", - " for i,j in combinations(range(5), 2):\n", - " qc.cx(i,j)\n", - " qc.rz(theta, range(5))\n", - " for i,j in combinations(range(5), 2):\n", - " qc.cx(i,j)\n", - "\n", - "transpiled_qc = transpile(qc, backend=FakeVigo())\n", - "qobj = assemble([transpiled_qc.bind_parameters({theta: n})\n", - " for n in theta_range], backend=FakeVigo())\n", - "end = time.time()\n", - "print('Time compiling over parameterized circuit, then binding: ', end-start)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Composition" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Parameterized circuits can be composed like standard `QuantumCircuit`s.\n", - "Generally, when composing two parameterized circuits, the resulting circuit will be parameterized by the union of the parameters of the input circuits." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, parameter names must be unique within a given circuit.\n", - "When attempting to add a parameter whose name is already present in the target circuit:\n", - " - if the source and target share the same `Parameter` instance, the parameters will be assumed to be the same and combined\n", - " - if the source and target have different `Parameter` instances, an error will be raised\n" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:02:12.469556Z", - "start_time": "2021-07-31T05:02:12.455911Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:49.606900Z", - "iopub.status.busy": "2023-08-25T18:25:49.606196Z", - "iopub.status.idle": "2023-08-25T18:25:49.622414Z", - "shell.execute_reply": "2023-08-25T18:25:49.621702Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌────────────┐┌────────────┐\n",
-       "q_0: ┤0           ├┤0           ├\n",
-       "     │  sc_1(phi) ││  sc_2(phi) │\n",
-       "q_1: ┤1           ├┤1           ├\n",
-       "     ├────────────┤└────────────┘\n",
-       "q_2: ┤0           ├──────────────\n",
-       "     │  sc_2(phi) │              \n",
-       "q_3: ┤1           ├──────────────\n",
-       "     └────────────┘              
" - ], - "text/plain": [ - " ┌────────────┐┌────────────┐\n", - "q_0: ┤0 ├┤0 ├\n", - " │ sc_1(phi) ││ sc_2(phi) │\n", - "q_1: ┤1 ├┤1 ├\n", - " ├────────────┤└────────────┘\n", - "q_2: ┤0 ├──────────────\n", - " │ sc_2(phi) │ \n", - "q_3: ┤1 ├──────────────\n", - " └────────────┘ " - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "phi = Parameter('phi')\n", - "\n", - "sub_circ1 = QuantumCircuit(2, name='sc_1')\n", - "sub_circ1.rz(phi, 0)\n", - "sub_circ1.rx(phi, 1)\n", - "\n", - "sub_circ2 = QuantumCircuit(2, name='sc_2')\n", - "sub_circ2.rx(phi, 0)\n", - "sub_circ2.rz(phi, 1)\n", - "\n", - "qc = QuantumCircuit(4)\n", - "qr = qc.qregs[0]\n", - "\n", - "qc.append(sub_circ1.to_instruction(), [qr[0], qr[1]])\n", - "qc.append(sub_circ2.to_instruction(), [qr[0], qr[1]])\n", - "\n", - "qc.append(sub_circ2.to_instruction(), [qr[2], qr[3]])\n", - "\n", - "qc.draw()\n", - "\n", - "# The following raises an error: \"QiskitError: 'Name conflict on adding parameter: phi'\"\n", - "# phi2 = Parameter('phi')\n", - "# qc.u3(0.1, phi2, 0.3, 0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To insert a subcircuit under a different parameterization, the `to_instruction` method accepts an optional argument (`parameter_map`) which, when present, will generate instructions with the source parameter replaced by a new parameter." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:02:13.856680Z", - "start_time": "2021-07-31T05:02:13.837242Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:49.625849Z", - "iopub.status.busy": "2023-08-25T18:25:49.625370Z", - "iopub.status.idle": "2023-08-25T18:25:49.649519Z", - "shell.execute_reply": "2023-08-25T18:25:49.648726Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
        ┌───────────┐                                    \n",
-       "q740_0: ┤ Rz(theta) ├──■─────────────────────────────────\n",
-       "        └───────────┘┌─┴─┐┌───────────┐                  \n",
-       "q740_1: ─────────────┤ X ├┤ Rz(theta) ├──■───────────────\n",
-       "                     └───┘└───────────┘┌─┴─┐┌───────────┐\n",
-       "q740_2: ───────────────────────────────┤ X ├┤ Rz(theta) ├\n",
-       "         ┌─────────┐                   └───┘└───────────┘\n",
-       "q740_3: ─┤ Rz(phi) ├───■─────────────────────────────────\n",
-       "         └─────────┘ ┌─┴─┐ ┌─────────┐                   \n",
-       "q740_4: ─────────────┤ X ├─┤ Rz(phi) ├───■───────────────\n",
-       "                     └───┘ └─────────┘ ┌─┴─┐ ┌─────────┐ \n",
-       "q740_5: ───────────────────────────────┤ X ├─┤ Rz(phi) ├─\n",
-       "        ┌───────────┐                  └───┘ └─────────┘ \n",
-       "q740_6: ┤ Rz(gamma) ├──■─────────────────────────────────\n",
-       "        └───────────┘┌─┴─┐┌───────────┐                  \n",
-       "q740_7: ─────────────┤ X ├┤ Rz(gamma) ├──■───────────────\n",
-       "                     └───┘└───────────┘┌─┴─┐┌───────────┐\n",
-       "q740_8: ───────────────────────────────┤ X ├┤ Rz(gamma) ├\n",
-       "                                       └───┘└───────────┘
" - ], - "text/plain": [ - " ┌───────────┐ \n", - "q740_0: ┤ Rz(theta) ├──■─────────────────────────────────\n", - " └───────────┘┌─┴─┐┌───────────┐ \n", - "q740_1: ─────────────┤ X ├┤ Rz(theta) ├──■───────────────\n", - " └───┘└───────────┘┌─┴─┐┌───────────┐\n", - "q740_2: ───────────────────────────────┤ X ├┤ Rz(theta) ├\n", - " ┌─────────┐ └───┘└───────────┘\n", - "q740_3: ─┤ Rz(phi) ├───■─────────────────────────────────\n", - " └─────────┘ ┌─┴─┐ ┌─────────┐ \n", - "q740_4: ─────────────┤ X ├─┤ Rz(phi) ├───■───────────────\n", - " └───┘ └─────────┘ ┌─┴─┐ ┌─────────┐ \n", - "q740_5: ───────────────────────────────┤ X ├─┤ Rz(phi) ├─\n", - " ┌───────────┐ └───┘ └─────────┘ \n", - "q740_6: ┤ Rz(gamma) ├──■─────────────────────────────────\n", - " └───────────┘┌─┴─┐┌───────────┐ \n", - "q740_7: ─────────────┤ X ├┤ Rz(gamma) ├──■───────────────\n", - " └───┘└───────────┘┌─┴─┐┌───────────┐\n", - "q740_8: ───────────────────────────────┤ X ├┤ Rz(gamma) ├\n", - " └───┘└───────────┘" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "p = Parameter('p')\n", - "qc = QuantumCircuit(3, name='oracle')\n", - "qc.rz(p, 0)\n", - "qc.cx(0, 1)\n", - "qc.rz(p, 1)\n", - "qc.cx(1, 2)\n", - "qc.rz(p, 2)\n", - "\n", - "theta = Parameter('theta')\n", - "phi = Parameter('phi')\n", - "gamma = Parameter('gamma')\n", - "\n", - "qr = QuantumRegister(9)\n", - "larger_qc = QuantumCircuit(qr)\n", - "larger_qc.append(qc.to_instruction({p: theta}), qr[0:3])\n", - "larger_qc.append(qc.to_instruction({p: phi}), qr[3:6])\n", - "larger_qc.append(qc.to_instruction({p: gamma}), qr[6:9])\n", - "larger_qc.draw()\n", - "\n", - "larger_qc.decompose().draw()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "end_time": "2021-07-31T05:02:16.955198Z", - "start_time": "2021-07-31T05:02:16.224522Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:49.653048Z", - "iopub.status.busy": "2023-08-25T18:25:49.652604Z", - "iopub.status.idle": "2023-08-25T18:25:49.740621Z", - "shell.execute_reply": "2023-08-25T18:25:49.739819Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:49 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "nteract": { - "version": "0.22.4" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "04367b25e0df44de95aeaf03d3547d91": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "064f7d089ffc49fd94964cac56e7f6b8": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_8fa5795e64724e0bbfe760c5383b1348", - "placeholder": "​", - "style": "IPY_MODEL_04367b25e0df44de95aeaf03d3547d91", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "8fa5795e64724e0bbfe760c5383b1348": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/tutorials/circuits_advanced/02_operators_overview.ipynb b/docs/tutorials/circuits_advanced/02_operators_overview.ipynb deleted file mode 100644 index d67b4dc58306..000000000000 --- a/docs/tutorials/circuits_advanced/02_operators_overview.ipynb +++ /dev/null @@ -1,1383 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Operators" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.554914Z", - "start_time": "2019-08-21T09:02:54.249612Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.473739Z", - "iopub.status.busy": "2023-08-25T18:25:51.471199Z", - "iopub.status.idle": "2023-08-25T18:25:52.134205Z", - "shell.execute_reply": "2023-08-25T18:25:52.133467Z" - } - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister\n", - "from qiskit import BasicAer\n", - "from qiskit.compiler import transpile\n", - "from qiskit.quantum_info.operators import Operator, Pauli\n", - "from qiskit.quantum_info import process_fidelity\n", - "\n", - "from qiskit.extensions import RXGate, XGate, CXGate" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Operator Class\n", - "\n", - "The `Operator` class is used in Qiskit to represent matrix operators acting on a quantum system. It has several methods to build composite operators using tensor products of smaller operators, and to compose operators.\n", - "\n", - "### Creating Operators\n", - "\n", - "The easiest way to create an operator object is to initialize it with a matrix given as a list or a Numpy array. For example, to create a two-qubit Pauli-XX operator:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.572857Z", - "start_time": "2019-08-21T09:02:56.566140Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.140012Z", - "iopub.status.busy": "2023-08-25T18:25:52.138643Z", - "iopub.status.idle": "2023-08-25T18:25:52.186397Z", - "shell.execute_reply": "2023-08-25T18:25:52.185694Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "XX = Operator([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])\n", - "XX" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Operator Properties\n", - "\n", - "The operator object stores the underlying matrix, and the input and output dimension of subsystems. \n", - "\n", - "* `data`: To access the underlying Numpy array, we may use the `Operator.data` property.\n", - "* `dims`: To return the total input and output dimension of the operator, we may use the `Operator.dim` property. *Note: the output is returned as a tuple* `(input_dim, output_dim)`, *which is the reverse of the shape of the underlying matrix.*" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.589962Z", - "start_time": "2019-08-21T09:02:56.585681Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.192862Z", - "iopub.status.busy": "2023-08-25T18:25:52.191017Z", - "iopub.status.idle": "2023-08-25T18:25:52.212198Z", - "shell.execute_reply": "2023-08-25T18:25:52.211471Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "XX.data" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.615497Z", - "start_time": "2019-08-21T09:02:56.611146Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.217181Z", - "iopub.status.busy": "2023-08-25T18:25:52.215888Z", - "iopub.status.idle": "2023-08-25T18:25:52.223513Z", - "shell.execute_reply": "2023-08-25T18:25:52.222889Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(4, 4)" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "input_dim, output_dim = XX.dim\n", - "input_dim, output_dim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Input and Output Dimensions\n", - "\n", - "The operator class also keeps track of subsystem dimensions, which can be used for composing operators together. These can be accessed using the `input_dims` and `output_dims` functions.\n", - "\n", - "For $2^N$ by $2^M$ operators, the input and output dimension will be automatically assumed to be M-qubit and N-qubit:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:56.804167Z", - "start_time": "2019-08-21T09:02:56.798857Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.228252Z", - "iopub.status.busy": "2023-08-25T18:25:52.227024Z", - "iopub.status.idle": "2023-08-25T18:25:52.235632Z", - "shell.execute_reply": "2023-08-25T18:25:52.235002Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Input dimensions: (2, 2)\n", - "Output dimensions: (2,)\n" - ] - } - ], - "source": [ - "op = Operator(np.random.rand(2 ** 1, 2 ** 2))\n", - "print('Input dimensions:', op.input_dims())\n", - "print('Output dimensions:', op.output_dims())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If the input matrix is not divisible into qubit subsystems, then it will be stored as a single-qubit operator. For example, if we have a $6\\times6$ matrix:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:57.764881Z", - "start_time": "2019-08-21T09:02:57.760401Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.241702Z", - "iopub.status.busy": "2023-08-25T18:25:52.239813Z", - "iopub.status.idle": "2023-08-25T18:25:52.249621Z", - "shell.execute_reply": "2023-08-25T18:25:52.248995Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Input dimensions: (6,)\n", - "Output dimensions: (6,)\n" - ] - } - ], - "source": [ - "op = Operator(np.random.rand(6, 6))\n", - "print('Input dimensions:', op.input_dims())\n", - "print('Output dimensions:', op.output_dims())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The input and output dimension can also be manually specified when initializing a new operator:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:58.292849Z", - "start_time": "2019-08-21T09:02:58.287354Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.254404Z", - "iopub.status.busy": "2023-08-25T18:25:52.253222Z", - "iopub.status.idle": "2023-08-25T18:25:52.260364Z", - "shell.execute_reply": "2023-08-25T18:25:52.259752Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Input dimensions: (4,)\n", - "Output dimensions: (2,)\n" - ] - } - ], - "source": [ - "# Force input dimension to be (4,) rather than (2, 2)\n", - "op = Operator(np.random.rand(2 ** 1, 2 ** 2), input_dims=[4])\n", - "print('Input dimensions:', op.input_dims())\n", - "print('Output dimensions:', op.output_dims())" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:02:58.779572Z", - "start_time": "2019-08-21T09:02:58.774878Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.265013Z", - "iopub.status.busy": "2023-08-25T18:25:52.263826Z", - "iopub.status.idle": "2023-08-25T18:25:52.272637Z", - "shell.execute_reply": "2023-08-25T18:25:52.270867Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Input dimensions: (2, 3)\n", - "Output dimensions: (2, 3)\n" - ] - } - ], - "source": [ - "# Specify system is a qubit and qutrit\n", - "op = Operator(np.random.rand(6, 6),\n", - " input_dims=[2, 3], output_dims=[2, 3])\n", - "print('Input dimensions:', op.input_dims())\n", - "print('Output dimensions:', op.output_dims())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also extract just the input or output dimensions of a subset of subsystems using the `input_dims` and `output_dims` functions:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:02.187313Z", - "start_time": "2019-08-21T09:03:02.183719Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.277989Z", - "iopub.status.busy": "2023-08-25T18:25:52.277724Z", - "iopub.status.idle": "2023-08-25T18:25:52.285231Z", - "shell.execute_reply": "2023-08-25T18:25:52.284567Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dimension of input system 0: (2,)\n", - "Dimension of input system 1: (3,)\n" - ] - } - ], - "source": [ - "print('Dimension of input system 0:', op.input_dims([0]))\n", - "print('Dimension of input system 1:', op.input_dims([1]))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Converting classes to Operators\n", - "\n", - "Several other classes in Qiskit can be directly converted to an `Operator` object using the operator initialization method. For example:\n", - "\n", - "* `Pauli` objects\n", - "* `Gate` and `Instruction` objects\n", - "* `QuantumCircuit` objects\n", - "\n", - "Note that the last point means we can use the `Operator` class as a unitary simulator to compute the final unitary matrix for a quantum circuit, without having to call a simulator backend. If the circuit contains any unsupported operations, an exception will be raised. Unsupported operations are: measure, reset, conditional operations, or a gate that does not have a matrix definition or decomposition in terms of gate with matrix definitions." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:02.854419Z", - "start_time": "2019-08-21T09:03:02.842387Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.288815Z", - "iopub.status.busy": "2023-08-25T18:25:52.288332Z", - "iopub.status.idle": "2023-08-25T18:25:52.296566Z", - "shell.execute_reply": "2023-08-25T18:25:52.295969Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "# Create an Operator from a Pauli object\n", - "\n", - "pauliXX = Pauli('XX')\n", - "Operator(pauliXX)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:03.064145Z", - "start_time": "2019-08-21T09:03:03.058953Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.300278Z", - "iopub.status.busy": "2023-08-25T18:25:52.299776Z", - "iopub.status.idle": "2023-08-25T18:25:52.307248Z", - "shell.execute_reply": "2023-08-25T18:25:52.306643Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", - " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "# Create an Operator for a Gate object\n", - "Operator(CXGate())" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:03.353613Z", - "start_time": "2019-08-21T09:03:03.345462Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.310727Z", - "iopub.status.busy": "2023-08-25T18:25:52.310257Z", - "iopub.status.idle": "2023-08-25T18:25:52.317792Z", - "shell.execute_reply": "2023-08-25T18:25:52.317197Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[0.70710678+0.j , 0. -0.70710678j],\n", - " [0. -0.70710678j, 0.70710678+0.j ]],\n", - " input_dims=(2,), output_dims=(2,))\n" - ] - } - ], - "source": [ - "# Create an operator from a parameterized Gate object\n", - "Operator(RXGate(np.pi / 2))" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:47.550069Z", - "start_time": "2019-08-21T09:03:47.408126Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.321257Z", - "iopub.status.busy": "2023-08-25T18:25:52.320796Z", - "iopub.status.idle": "2023-08-25T18:25:52.623115Z", - "shell.execute_reply": "2023-08-25T18:25:52.622444Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.70710678+0.j, 0.70710678+0.j, 0. +0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0.70710678+0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " ...,\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0.70710678+0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0.70710678+0.j, -0.70710678+0.j, 0. +0.j, ...,\n", - " 0. +0.j, 0. +0.j, 0. +0.j]],\n", - " input_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2), output_dims=(2, 2, 2, 2, 2, 2, 2, 2, 2, 2))\n" - ] - } - ], - "source": [ - "# Create an operator from a QuantumCircuit object\n", - "circ = QuantumCircuit(10)\n", - "circ.h(0)\n", - "for j in range(1, 10):\n", - " circ.cx(j-1, j)\n", - "\n", - "# Convert circuit to an operator by implicit unitary simulation\n", - "Operator(circ)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using Operators in circuits\n", - "\n", - "Unitary `Operators` can be directly inserted into a `QuantumCircuit` using the `QuantumCircuit.append` method. This converts the `Operator` into a `UnitaryGate` object, which is added to the circuit.\n", - "\n", - "If the operator is not unitary, an exception will be raised. This can be checked using the `Operator.is_unitary()` function, which will return `True` if the operator is unitary and `False` otherwise." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:03:49.196556Z", - "start_time": "2019-08-21T09:03:49.161398Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.627989Z", - "iopub.status.busy": "2023-08-25T18:25:52.626783Z", - "iopub.status.idle": "2023-08-25T18:25:53.275876Z", - "shell.execute_reply": "2023-08-25T18:25:53.275164Z" - }, - "tags": [ - "nbsphinx-thumbnail" - ] - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXEAAADuCAYAAADPwDeGAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAgE0lEQVR4nO3de1hU5b4H8O9wGe4IqAkKKigYclNAEzUVN6goqVm2S7daeS3b4s7AtLNL99ORvGT7WJmSpmmhZnnUFEXNvIRXQMgLmpoUt9GNojhc4jJz/iDmODIoDDMs3uH7eZ4emXet9a7fEHx517suI1Or1WoQEZGQzKQugIiI9McQJyISGEOciEhgDHEiIoExxImIBMYQJyISGEOciEhgDHEiIoExxImIBMYQJyISGEOciEhgDHEiIoExxImIBMYQJyISGEOciEhgDHEiIoExxImIBMYQJyISGEOciEhgDHEiIoExxImIBMYQJyISGEOciEhgDHEiIoExxImIBMYQJyISGEOciEhgDHEiIoExxImIBMYQJyISGEOciEhgDHEiIoExxImIBMYQJyISGEOciEhgDHEiIoExxImIBGYhdQHUNGo1oKqUugrTZWYJyGTNv1+1Wo3S0tLm33ET2NraQibFN6uVY4gLTlUJ/LhK6ipMV/gcwFze/PstLS2Fvb198++4CZRKJezs7KQuo9XhdAoRkcAY4kREAmOIExEJjCFORCQwhjgRkcAY4kREAmOIExEJjCFORCQwhjgRkcAY4kREAmOIE7USbdu2haenJ7y8vODq6tro7WfNmgUPDw8jVEZNwWenEJkod3d3TJ48Gf369UNISAg6duyotfzu3btIT0/H2bNnsWXLFmRmZtbb14IFC7BkyRJcv34d4eHhyMnJMXb51EAytVqtlroI0l91BR+AZUxSPQCrpKRE7wdgDRw4EG+++SZGjx4Nc3PzBm934sQJrFq1Ctu2bdNqrw3wWjNnzkRCQkKd7fkALGmY/HRKYWEh4uLi0L17d1hbW8PDwwMxMTEoKSnB1KlTIZPJ8Mknn0hdJlGT2dvb47PPPsPx48fx7LPPagV4UVERfvjhByQmJuLrr7/G3r17kZubq7V9//79sXXrVhw4cACdO3cGUDfA4+LidAY4Scekp1MyMjIQFRUFhUIBOzs79OzZE/n5+Vi1ahWuX7+OO3fuAAB69eolbaFGplKp8L8//Q/2nloLRVE2nOzaY1DQC5gy/F+wkXPkZAqCg4OxY8cOdOnSRdOWl5eHhIQEJCYm4tq1azq3c3V1xZgxY/D6668jMDAQABAZGYkLFy5gz549eOmllzTrxsXFYfny5cZ9I9RoJjudUlhYiN69eyM3Nxfz5s3De++9BwcHBwDAsmXLMH/+fFhYWKC6uhp3796Fo6OjxBXrpyHTKZ/uisHOn1ZhgP+z6PtkFH6/mYWdKR8jwPNpLJ1xCGZmJn9ApjcRplP69++Pffv2aX6GlUol5s+fj4SEBFRVVTV4n1FRUVi7dq3Ok5cNCXBOp0jDZEfic+bMQW5uLt544w2sWLFCa1lcXBwSExORmZkJT09PYQO8IbIVF7Er5WMM9B+H96Z8p2l3dfHEp7vm4EjmVgztPUHCCqkpAgICkJSUpPkZPnHiBCZOnIjs7OxG97Vv3z74+/vjyJEj6N27t6b9q6++4gi8BTPJIVhWVha2bduGdu3aIT4+Xuc6ISEhAICgoCCt9hs3bmD06NFwcHCAs7MzJk+ejNu3bxu9ZmP5MWML1Go1xj09V6t95FPTYW1pi0PpX0lTGDWZXC5HYmIi2rRpAwBITk5GRESEXgFea/bs2VoBDgCjR4/mpYUtmEmG+JYtW6BSqTBx4sR6D0ltbGwAaIf4/fv3ER4ejtzcXGzZsgUJCQk4fvw4oqOjoVKpmqV2Q7uScxZmMjP06NxXq11uaQ2vjr3wS85ZiSqjpnr33Xfh7+8PADh37hzGjRuHsrIyvft7+CRmWloaAMDR0RHr1q1rWrFkNCYZ4ocPHwYAhIeH17tO7Zn5B0M8ISEBeXl52LlzJ6KjozF+/HgkJibi1KlT2L17t3GLNpLbxflwtGsHuYVVnWXt2nTCvZJCVFZVSFAZNUX37t0xf/58AEBFRQUmT57cpA9W1nUVytChQzXXgw8bNgzjx49vWtFkFCY5J/7bb78BgNaZ+gdVVVUhJSUFgHaI79mzBwMHDtRcXgUAYWFh8PLywvfff4+xY8fqVU9oaCgUCoVe2z6O3MIGCW9crXf5HxWlsNQR4DXbWtesU1kKSwsJzt4JwNvHGxVV+o9u9fW4I7/XXnsNFhY1v77x8fG4cOGC3vvSFeC1c+CzZs3C3r17AQB///vfsX379nr78fb25knyJnB1dUVqamqjtzPJEC8pKQGAeg8tt23bhsLCQjg4OMDT01PTfunSJZ2jDT8/P1y6dEnvehQKBfLy8vTe/lGsLW0fudxKbosy5S2dyyqqymvWeUwfrVlBfj7KK/Uf4RqDjY0NXnnlFQA1P+OrVul/t9ejAhwAkpKScOHCBfj7++Ppp59GQEAAzp8/r7OvgoICvesg/ZlkiLu6uqKoqAjp6ekICwvTWlZQUIDY2FgAQGBgIGQymWZZUVERnJyc6vTn4uKCK1euNKkeY5Fb2DxyeVvHjvj95iVUVP1RZ0ql8F4e2ti14yj8Edw6dpRsJF5fKI4YMQLOzs4AgK1bt2rud2isxwV4rdWrV2P16tUAgAkTJmDBggU6+3Nzc+NIvAn0zQmTDPGIiAhkZWVh6dKliIyMhI+PDwDg7NmzmDRpEgoLCwE0300++hwiNdTjrhPv4dEHab8cwJXfzyDA62lNe0VlOX7Nz0CA1yCj1WYKrv5ytcVdJx4aGqr5eteuXXr139AAr91HbYg/uO+HXb16ldeJS8Ak/2zGxcWhbdu2yMnJgZ+fHwICAuDt7Y2+ffvCy8sLQ4cOBVD38kJnZ2fcvXu3Tn937tyBi4tLc5RucEOC/gqZTIYdx/+t1Z50+nOUV5ZiaO+J0hRGequ9PBbQb4DQmAAHgPz8fM1RQXBwcKP3R8ZlkiHu7u6O48ePY9SoUbC2tkZ2djZcXFywdu1a7N27F7/88guAuiHu6+urc+770qVL8PX1bZbaDc3TLQCj+8/GTxd2YNGX45B0eh3WfD8Pa75/E4Feg3mjj4CefPJJAMDt27cbfa6lsQFeKyMjA0DN1GKHDh0atU8yLpOcTgFqAnnPnj112pVKJbKzs2FmZqa5xrZWdHQ0Fi5ciNzcXLi7uwMATp8+jevXrwt9x9pro/+NDs5dkXQ6AWey9sLRrh3GDvg7pgz/F+cwBaRQKGBmZtboK55iY2P1CnAAyMnJQV5eHsrKyjRXxVDLYLLPTqnP6dOn0a9fP/To0QOXL1/WWlZcXIyAgAC0a9cOixcvRnl5OeLi4tC+fXucPHmyRQYeH0VrXCI8O6Whhg8fjp07d8La2tooD7Pis1Ok0er+pNZeHvXwVApQc2fa4cOHERMTgxdffBEWFhaIjo7GRx991CIDnKgxkpOTMXbsWPj5+WHlypVSl0MGwhB/SLdu3XROwxCZguTkZCQnJ0tdBhlQqxtePi7EiYhE0upG4rXPVSEiMgWtbiRORGRKGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCazVPTuFSAS2trZQKpUG62/52q0oLimFo50tYme+WOe1Idja2hqkH2ochjhRCySTyQz6AQtyK2vIK6sht7KGnZ1dndckLk6nULNatvVlRMbKpC6DyGQwxElL8tmNiIyVIfnsRp3LFXeyERkrw7KtLxtsnykXdmLTgUUG64+oNWGIU7N6c/zn2LukTKst5cJObD64WKKKiMTGOXFqVhbmloC5ZbPtr6q6EipVNeSW1s22T6LmxJE4NUnt9MqmA4tw6tIezP6fPhi5wBp//ZcbEvbEorq6Smv9h+fE5302BAfTvgQARMbKNP/VTuf8fusyVu14HdNW+GH0fzkgeqEtXv93CJJOr6tTy6YDixAZK0O24iLW7H4TL73vjlELrPHzr8fw/KL2iPlkgM738M2R5YiMleHnX48Z6LtC1Hw4EieDOHM5Cd+fWI1RYbMwos+rOHFxF7YfXQF7G2dM+MvCereb8Jd3oFarcP7Gccx/cbOm3a9rfwBA5vUjOP/rMTzlGw1XF0+UV5Tg2M/b8dG303Gv5D94aeiCOn3GJ06ElaUNnh80DzKZDB2cuyAyZAq+PfYhcm5dgccTPbTW33/2C7i390Gg1yADfTeImg9DnAziN8VFfP7WRbi6dAUARIfNwowPA7Ar5eNHhniITyR+SP8a528cR0TI3+osjwiZhGfCZmm1Pff0PxC7dii2/vgBxg9+q2aK5gH2Nk5YNuMQzM3//8d7VL8Z+PbYh9h/Zj2mRy/TtF+4kYKcW5cxbeRSfd42keQ4nUIG0d9/rCbAgZrrnIO6h+POfQXK/tD/phUb+f9fw1xRWY7iktu4X3YHIT7DUFpejJxbl+tsM+7puVoBDuDPkfZgHEzbpDXFs//sepibWWBY6BS9aySSEkfipBeZTPtabzcXrzrrONq2BQAUl96GjZW9Xvsp+0OJTQcW4ejP3+A/d3PqLL9fVlSnzb29j86+RvWbgfjEiTiVtQcD/MeitPw+jmZ+g36+0XB26KBXfURSY4iTFitLGwDAH5WlOpeXV5RorVfLzMy83j7VarXe9SxJnIDTWXsw8qkZCPQaBAfbtjCXmePM5SR8d/wjqNWqOttYWeq+/XtgwHNwtJ2D/WfWY4D/WBzJ3IbyihJEPTVN7/qIpMYQJy2uLp4AgN9vZelcXtteu54hPDyqr6Usu4vTWXsQETwJc59bo7Us/dqhRu9HbmGFyJDJ+N+UVSi8l4/9Z9ajXZtOCO0xQq+6iVoCzomTFu9OwWjv5IEjGVtReC9fa1llVQV2pXwCmUyGsJ6jDbbP2qmW4tI7Wu1msprR/cMj+dvFBdin4xLDhoh6ajpUqmqsS5qPrN9PYVjoyzB/xFEEUUvHkThpMTe3QMy4z7Doy2cxc2UgRvSdio5tu6Ho/k0czdyG7JsX8dLQhXUu02sK3879sCvlE3y843X09R0FC3NLPNn5Kbi5eCLEexh+OPcVrCxt4OPRB7eKfsPeU2vh6uKJ4tLbjd5Xlw6+8PcciB/Sv4JMJsOIPq8a7H0QSYEhTnU85TsK/56dgm1HluFg6pcoLr0Na7kdunfqjf+K3IbBQS8YdH/hvV7CtbxzOJK5Fcd+3g6VWoW3XtgANxdPvD3hK6xLehsns77HgbQv0amdN16J+m+Ym1lixTev6LW/kU/NwIUbPyGoWzjc2tY9IUskEpm6KWedSHLVFcCPq6SuQixHM7/B+1/9FQsmJGJo75ceuW74HMBc3kyFGdGST79GsbIEjvZ2WDh7Yp3XJC7OiVOrs+vEp2hj1w4DA8ZJXQpRk3E6hVqFIuUtnLv6Ay7cOI7zvx7D1Kh4yC2spC6LqMkY4tQq/H7zEuITJ8DexgnR/Wbh+cHzpC6JHkOtVqO0VPf9Ci2Rra1tvZfLGhNDnFqFoG5DcHA5T/+IpLS0FPb2+t3pKwWlUinJR91xTpyISGAMcSIigTHEiYgExhAnIhIYQ5yISGAMcSIigTHEiYgExhAnIhIYQ5yISGAMcSIigTHEiYgawNLSEq6urlKXUQefnUJEJsvGxgahoaEICQlBSEgI3NzcIJfLUVFRgfz8fKSlpSEtLQ2pqakoLy+vtx9LS0ts374d/v7+CA8PR05OTjO+i0djiBORyfHx8cFrr72Gl19+GU5OTvWuN2nSJADAnTt38MUXX2DNmjW4fv261jq1AT5mzBgAwN69e9GrVy+oVCqj1d8YrWI6pbCwEHFxcejevTusra3h4eGBmJgYlJSUYOrUqZDJZPjkk0+kLpOImsjJyQkbNmzAlStXMHfu3EcG+INcXFzw1ltv4dq1a/j888/h6OgIoG6Al5aWYu7cuS0mwIFWMBLPyMhAVFQUFAoF7Ozs0LNnT+Tn52PVqlW4fv067typ+YT1Xr16SVuoEW05HI+reem4mpsGxZ0b6ODcBV8tzJa6LCKDGjFiBNatW4dOnTpp2srKyrB9+3YcO3YMaWlpuHbtGsrLy2FtbQ0fHx+EhIRg8ODBeO6552BtbQ0AmDZtGoYPH44ZM2Zg1qxZWgH+zDPP4PDhw5K8v/qYdIgXFhbimWeegUKhwLx58/Dee+/BwcEBALBs2TLMnz8fFhYWkMlkCAwMlLha4/li30I42LrAu1MwSsruSl0OkcHNnDkTq1evhplZzeTCvXv38P7772P9+vUoKiqqs75SqUR6ejrS09Px+eefIyYmBtOmTcM777wDBwcHeHh4ICkpSfMhDy01wAETn06ZM2cOcnNz8cYbb2DFihWaAAeAuLg4BAUFoaqqCl27dtUcPpmiTW9fx47Ft7F0xkG0dewodTlEBjVt2jSsWbNGE+D79u2Dn58fVqxYoTPAdbl9+zaWLl0Kf39/HDp0CAA0Af7HH3+02AAHTDjEs7KysG3bNrRr1w7x8fE61wkJCQEABAUFadpqQ79v376wsrKS5OOWDM2trZfUJRAZRf/+/bF27VrN66VLl2LkyJHIy8vTq7+CggKUlJRotVlYWKC4uLhJdRqTyYb4li1boFKpMHHixHo/4snGxgaAdohfu3YN3333HVxdXdGnT59mqZWIGs/GxgYbNmzQjMA//PBDvP3223r39/BJzMrKSgCAubk5NmzYALlc3vSijcBkQ7z20Cc8PLzedXJzcwFoh/igQYNQUFCA3bt3IyIiwrhFEpHeFi9eDB8fHwDAyZMnERcXp3dfuq5CGTVqFNLS0gAA/v7++Oc//9n0oo3AZE9s/vbbbwCALl266FxeVVWFlJQUANohXvtX3ZBCQ0OhUCgM3i8AyC1skPDGVaP0TYC3jzcqqsqkLqPJnn1lLuzsHVGgKIC7u3ud1y3Roy7ja9OmDWbPng0AKC8vxyuvvKL3ZX+6Arx2DlyhUCA1NRVyuRxz5szBBx98UGe6pZa3t3eT8sPV1RWpqamN3s5kQ7z2G11WpvsXcNu2bSgsLISDgwM8PT2NWotCodB7ju5xrC1tjdIv1SjIz0d5ZanUZTSZqrpa829eXl6d16KZMmUKbG1rfvbXrVuHK1eu6NXPowIcAM6fP49NmzZh2rRpcHR0xMSJE5GQkKCzr4KCAr1qaCqTDXFXV1cUFRUhPT0dYWFhWssKCgoQGxsLAAgMDDT6yUtjPm9BbmFjtL4JcOvY0SRG4mbm5pp/O3XqVOd1S6RSqeoNxunTp2u+Xr16tV79Py7AH+x/2rRpmv3WF+Jubm5NHonrw2RDPCIiAllZWVi6dCkiIyM1c2dnz57FpEmTUFhYCKB5bvLR5xCpoaorgB9XGa37Vu/qL1dh3jLPZzXKkk+/RrGyBG6ubsjNza3zuiUqKSnReVGCs7Mz/P39AQCnT59GVlZWo/tuaIADwLlz55CZmYmgoCAEBwfDzs5O55TK1atXYWdn1+hamspkT2zGxcWhbdu2yMnJgZ+fHwICAuDt7Y2+ffvCy8sLQ4cOBaA9H05ELV9wcLDm61OnTjV6+8YE+MP7MTMza3F3d5vsSNzd3R3Hjx9HbGwsjh49iuzsbPTs2RNr167F9OnT0a1bNwCtI8QPpm3GraKaE713S/6DquoKfH3ofQDAE85dEBkyScryiBqld+/emq9rrx5pKH0C/OH9BAcHay6KaAlMNsQBwNfXF3v27KnTrlQqkZ2dDTMzM81hmSnbf2Y9fv71qFbbxuSay6UCvQYzxEkoLi4umq8b80hYfQP84f04Ozs3olrjM+kQr8/FixehVqvh4+OjOcP9oG+//RYAcOnSJa3XXbt2RWhoaPMVaiAfvnZE6hKIDCYhIQEHDhyAjY0Nfv755wZvFxQUhOHDhwNo/LNQUlNTERUVhfLycvz666961W0srTLEz58/D6D+qZTx48frfD1lyhRs3LjRqLUR0aNlZ2cjOzu70dulpqZi7NixSExMxPjx4xv1LJTCwkLs37+/0ftsDgxxHdRqdXOWQ0TNJDk5GV27dsX9+/elLsVgTPbqlEd5XIgTkekypQAHWulIvKU+UpKIqLFa5UiciMhUMMSJiATGECciEhhDnIhIYAxxIiKBMcSJiATGECciEhhDnIhIYAxxIiKBMcSJiATGECciElirfHaKKTGzBMLnSF2F6TKzlLqC1svW1hZKpdIgfS1fuxXFJaVwtLNF7MwX621rCl2fTdAcGOKCk8lgEh/kS/QwmUxmsA8elltZQ15ZDbmVtaZPXW0i4nQKEZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiLcAy5cvR1hYGJydneHk5ISBAwdi//79UpdF9EhJSUno1asXrKys0LVrV6xcuVLqkprVsWPHMGbMGHTp0gUymQzvv/++JHUwxFuAw4cP49VXX8WPP/6IM2fOoH///oiOjkZKSorUpRHplJqaijFjxiAqKgoZGRlYtGgRFi5ciDVr1khdWrNRKpXo2bMnli1bBldXV8nqsJBsz6Sxb98+rdfLli3D/v37sWPHDgwYMECiqojqt3LlSvTp0wfx8fEAAF9fX1y8eBEffPABZs2aJXF1zWPkyJEYOXIkAGD+/PmS1cEQb4FUKhWKi4thZ2cndSkkmD8qKvFb3s067VXV1Zp/f7mRW+f1gzq0c0Ybh0f/7KWkpGDq1KlabSNGjMCKFSuQm5sLd3f3pryNJvk97ybKKyq12nS93/q+BzZWcnh0fKKZqm06hngLtGTJEty9exczZsyQuhQSjKWlBY6fycTV7Dydy0vLyvHFN0n1vnZytMfcV59/7H4KCgrqTCHUvi4oKJA0xO/cu4+t3x/Wuezh96ur7W9jI+Fh1AoNi3PiLczq1auxZMkSfPvtt5L+IpCYzGQyPD9yCGysrfTafvzIIbC2khu4qubVq2d3BD7ppde2wf4+8O/haeCKjIsh3oKsWLECsbGx2L17NyIiIqQuhwTVxsEOYyMbfy5lYJ8AdOvSsUHrurm5QaFQaLXdvHlTs0xqY4cNhKO9baO2cXK0x+iI/kaqyHgY4i3Eu+++i8WLFyMpKYkBTk0W1LM7gny7NXj9Du2cMXxQnwavP2DAACQnJ2u17d+/H126dGkRR5C2NtZ4fuSQBq8vA/DCKDGPQhjiLcDcuXOxfPlybN68GT169IBCoYBCocC9e/ekLo0ENmbYQDjaP/7kuLmZGV6IDoelRcNPkf3jH//AmTNn8M477+Dy5cv48ssv8fHHH+Ptt99uSskG5ePpjrBgvwatO7BPILw6N+wopJZSqURGRgYyMjJQUVEBhUKBjIwMXLt2TZ9y9SZTq9XqZt0j1SGTyXS2T5kyBRs3bmzeYsikXL2Ri/UPnch72PBBfRAe1rvRfe/duxcLFy7E5cuX4erqipiYGLz55pv6lmoUFZVV+Hjjd/jPnfoHRB3aOeONKc826o8YABw5cgTh4eF12gcPHowjR440tlS9McQFcyOnAO6u7WFpyQuLqGF2H0rBibSLOpd16dQBMyc8AzMz0z0ozym4hc8274JKR9SZm5lh9pRn0fGJthJUZhim+3/OBN1XlmL9N0lYlrAV94qVUpdDghgx+Cm0d3Gq0y63tMALo8JNOsABwMPtCQztH6xzWeTToUIHOMAQF8rR05moqqqGs6MDHB9zMwZRLbmlBf4aHQ4zM+1pu+i/9EdbZ0eJqmpe4WG94eHWXqutq7srBvUNlKgiw2GIP6C6uhqbN2/GsGHD0L59e1hZWaFz584YMWIE1q1bh+o/7/CSwn1lKU5lXAIARAwMqXcenUgXd7f2+Ev/EM1r3+6d0Sewh4QVNS9z89qTt+YAALncEuNHDTGJoxDx34GBFBcXIzIyEpMnT8bBgwchl8sRFBQElUqFAwcOYPr06bh//75k9dWOwjt37ADvrp0kq4PENSSsFzzcnoCdjTXGjRjU6gYC7V2cMDK8HwDgmaFhaOtkGkchPLH5p/Hjx2vukty0aZPWWeebN29i/fr1iImJ0et5Jh9/uQP3lWV616ZWq3G/pBRAzfWvFubmevdFrVu1SgWVStXoKzFMhVqtRkVlFeSWFi3uj5iDvQ3+PmVco7djiANIS0tDaGgoLCwscO7cOfj7+xu0/yWffo1iZYlB+yQi0+Job4eFsyc2ervW+ef4ITt37gQAjBo1yuABDtT8hdUXR+FErYO+OcEQB3DpUs0Jw7CwMKP0r88hUq09P5zET6nn0bljB7z2t9Et7hCQiKTFEEfNSU0AaNOmjVH613dO/MFReGHRPcSvTjR0aUTUQug7J84QB+DoWHOW2ljPKrmvLGvynHhpWbmBqiEiU8IQB+Dn54cdO3bg5MmTRulfn7kuzoUTtS76zonz6hQA586dQ3BwMCwtLZGRkYGePXtKXRLnwomoQXizD4DevXvjhRdeQGVlJaKionD06FGt5Tdv3kR8fDxKSprnMkHenUlEDcWR+J+Ki4sxZswYzSMkO3XqhI4dO6KgoAB5eXlQq9UoKiqCk5OT0WvhKJyIGooj8T85Ojri0KFDWL9+PYYMGYLS0lJkZmbCzMwMw4cPx/r16+Hg4NAstdjb2cDaSs5ROBE9FkfiLVT5HxWwklsyxInokRjiREQC43QKEZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCYwhTkQkMIY4EZHAGOJERAJjiBMRCez/AKOWX82rVct3AAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Create an operator\n", - "XX = Operator(Pauli('XX'))\n", - "\n", - "# Add to a circuit\n", - "circ = QuantumCircuit(2, 2)\n", - "circ.append(XX, [0, 1])\n", - "circ.measure([0,1], [0,1])\n", - "circ.draw('mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that in the above example we initialize the operator from a `Pauli` object. However, the `Pauli` object may also be directly inserted into the circuit itself and will be converted into a sequence of single-qubit Pauli gates:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.281182Z", - "iopub.status.busy": "2023-08-25T18:25:53.279806Z", - "iopub.status.idle": "2023-08-25T18:25:53.307182Z", - "shell.execute_reply": "2023-08-25T18:25:53.306520Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'11': 1024}" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "backend = BasicAer.get_backend('qasm_simulator')\n", - "circ = transpile(circ, backend, basis_gates=['u1','u2','u3','cx'])\n", - "job = backend.run(circ)\n", - "job.result().get_counts(0)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:12.017240Z", - "start_time": "2019-08-21T09:04:11.989825Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.312100Z", - "iopub.status.busy": "2023-08-25T18:25:53.310850Z", - "iopub.status.idle": "2023-08-25T18:25:53.321763Z", - "shell.execute_reply": "2023-08-25T18:25:53.321146Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌────────────┐┌─┐   \n",
-       "q_0: ┤0           ├┤M├───\n",
-       "     │  Pauli(XX) │└╥┘┌─┐\n",
-       "q_1: ┤1           ├─╫─┤M├\n",
-       "     └────────────┘ ║ └╥┘\n",
-       "c: 2/═══════════════╩══╩═\n",
-       "                    0  1 
" - ], - "text/plain": [ - " ┌────────────┐┌─┐ \n", - "q_0: ┤0 ├┤M├───\n", - " │ Pauli(XX) │└╥┘┌─┐\n", - "q_1: ┤1 ├─╫─┤M├\n", - " └────────────┘ ║ └╥┘\n", - "c: 2/═══════════════╩══╩═\n", - " 0 1 " - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Add to a circuit\n", - "circ2 = QuantumCircuit(2, 2)\n", - "circ2.append(Pauli('XX'), [0, 1])\n", - "circ2.measure([0,1], [0,1])\n", - "circ2.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Combining Operators\n", - "\n", - "Operators may be combined using several methods. \n", - "\n", - "### Tensor Product\n", - "\n", - "Two operators $A$ and $B$ may be combined into a tensor product operator $A\\otimes B$ using the `Operator.tensor` function. Note that if both $A$ and $B$ are single-qubit operators, then `A.tensor(B)` = $A\\otimes B$ will have the subsystems indexed as matrix $B$ on subsystem 0, and matrix $A$ on subsystem 1." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:14.208734Z", - "start_time": "2019-08-21T09:04:14.201058Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.326461Z", - "iopub.status.busy": "2023-08-25T18:25:53.325270Z", - "iopub.status.idle": "2023-08-25T18:25:53.333536Z", - "shell.execute_reply": "2023-08-25T18:25:53.332951Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", - " [ 0.+0.j, -0.+0.j, 0.+0.j, -1.+0.j],\n", - " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [ 0.+0.j, -1.+0.j, 0.+0.j, -0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "A = Operator(Pauli('X'))\n", - "B = Operator(Pauli('Z'))\n", - "A.tensor(B)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Tensor Expansion\n", - "\n", - "A closely related operation is `Operator.expand`, which acts like a tensor product but in the reverse order. Hence, for two operators $A$ and $B$ we have `A.expand(B)` = $B\\otimes A$ where the subsystems indexed as matrix $A$ on subsystem 0, and matrix $B$ on subsystem 1." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:14.899024Z", - "start_time": "2019-08-21T09:04:14.891072Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.338091Z", - "iopub.status.busy": "2023-08-25T18:25:53.336935Z", - "iopub.status.idle": "2023-08-25T18:25:53.345005Z", - "shell.execute_reply": "2023-08-25T18:25:53.344420Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],\n", - " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, -0.+0.j, -1.+0.j],\n", - " [ 0.+0.j, 0.+0.j, -1.+0.j, -0.+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "A = Operator(Pauli('X'))\n", - "B = Operator(Pauli('Z'))\n", - "A.expand(B)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Composition\n", - "\n", - "We can also compose two operators $A$ and $B$ to implement matrix multiplication using the `Operator.compose` method. We have that `A.compose(B)` returns the operator with matrix $B.A$:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:15.655155Z", - "start_time": "2019-08-21T09:04:15.648295Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.349566Z", - "iopub.status.busy": "2023-08-25T18:25:53.348405Z", - "iopub.status.idle": "2023-08-25T18:25:53.356382Z", - "shell.execute_reply": "2023-08-25T18:25:53.355782Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, 1.+0.j],\n", - " [-1.+0.j, -0.+0.j]],\n", - " input_dims=(2,), output_dims=(2,))\n" - ] - } - ], - "source": [ - "A = Operator(Pauli('X'))\n", - "B = Operator(Pauli('Z'))\n", - "A.compose(B)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also compose in the reverse order by applying $B$ in front of $A$ using the `front` kwarg of `compose`: `A.compose(B, front=True)` = $A.B$:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:16.460560Z", - "start_time": "2019-08-21T09:04:16.452319Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.360935Z", - "iopub.status.busy": "2023-08-25T18:25:53.359752Z", - "iopub.status.idle": "2023-08-25T18:25:53.367811Z", - "shell.execute_reply": "2023-08-25T18:25:53.367233Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, -1.+0.j],\n", - " [ 1.+0.j, -0.+0.j]],\n", - " input_dims=(2,), output_dims=(2,))\n" - ] - } - ], - "source": [ - "A = Operator(Pauli('X'))\n", - "B = Operator(Pauli('Z'))\n", - "A.compose(B, front=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Subsystem Composition\n", - "\n", - "Note that the previous compose requires that the total output dimension of the first operator $A$ is equal to total input dimension of the composed operator $B$ (and similarly, the output dimension of $B$ must be equal to the input dimension of $A$ when composing with `front=True`).\n", - "\n", - "We can also compose a smaller operator with a selection of subsystems on a larger operator using the `qargs` kwarg of `compose`, either with or without `front=True`. In this case, the relevant input and output dimensions of the subsystems being composed must match. *Note that the smaller operator must always be the argument of* `compose` *method.*\n", - "\n", - "For example, to compose a two-qubit gate with a three-qubit Operator:" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:17.113510Z", - "start_time": "2019-08-21T09:04:17.105398Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.372378Z", - "iopub.status.busy": "2023-08-25T18:25:53.371204Z", - "iopub.status.idle": "2023-08-25T18:25:53.379925Z", - "shell.execute_reply": "2023-08-25T18:25:53.379316Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " -1.+0.j],\n", - " [ 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j],\n", - " [ 0.+0.j, 0.+0.j, 0.+0.j, -1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j,\n", - " 0.+0.j]],\n", - " input_dims=(2, 2, 2), output_dims=(2, 2, 2))\n" - ] - } - ], - "source": [ - "# Compose XZ with a 3-qubit identity operator\n", - "op = Operator(np.eye(2 ** 3))\n", - "XZ = Operator(Pauli('XZ'))\n", - "op.compose(XZ, qargs=[0, 2])" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:17.324353Z", - "start_time": "2019-08-21T09:04:17.315952Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.384464Z", - "iopub.status.busy": "2023-08-25T18:25:53.383281Z", - "iopub.status.idle": "2023-08-25T18:25:53.392074Z", - "shell.execute_reply": "2023-08-25T18:25:53.391474Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j, 0.+0.j],\n", - " [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [0.+0.j, 0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]],\n", - " input_dims=(2, 2, 2), output_dims=(2, 2, 2))\n" - ] - } - ], - "source": [ - "# Compose YX in front of the previous operator\n", - "op = Operator(np.eye(2 ** 3))\n", - "YX = Operator(Pauli('YX'))\n", - "op.compose(YX, qargs=[0, 2], front=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Linear combinations\n", - "\n", - "Operators may also be combined using standard linear operators for addition, subtraction and scalar multiplication by complex numbers. " - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:18.829988Z", - "start_time": "2019-08-21T09:04:18.812834Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.396637Z", - "iopub.status.busy": "2023-08-25T18:25:53.395467Z", - "iopub.status.idle": "2023-08-25T18:25:53.404132Z", - "shell.execute_reply": "2023-08-25T18:25:53.403530Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[-1.5+0.j, 0. +0.j, 0. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 1.5+0.j, 1. +0.j, 0. +0.j],\n", - " [ 0. +0.j, 1. +0.j, 1.5+0.j, 0. +0.j],\n", - " [ 0. +0.j, 0. +0.j, 0. +0.j, -1.5+0.j]],\n", - " input_dims=(2, 2), output_dims=(2, 2))\n" - ] - } - ], - "source": [ - "XX = Operator(Pauli('XX'))\n", - "YY = Operator(Pauli('YY'))\n", - "ZZ = Operator(Pauli('ZZ'))\n", - "\n", - "op = 0.5 * (XX + YY - 3 * ZZ)\n", - "op" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "An important point is that while `tensor`, `expand` and `compose` will preserve the unitarity of unitary operators, linear combinations will not; hence, adding two unitary operators will, in general, result in a non-unitary operator:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:19.151814Z", - "start_time": "2019-08-21T09:04:19.147497Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.408680Z", - "iopub.status.busy": "2023-08-25T18:25:53.407507Z", - "iopub.status.idle": "2023-08-25T18:25:53.414829Z", - "shell.execute_reply": "2023-08-25T18:25:53.414251Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "op.is_unitary()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Implicit Conversion to Operators\n", - "\n", - "Note that for all the following methods, if the second object is not already an `Operator` object, it will be implicitly converted into one by the method. This means that matrices can be passed in directly without being explicitly converted to an `Operator` first. If the conversion is not possible, an exception will be raised." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:20.045005Z", - "start_time": "2019-08-21T09:04:20.039841Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.419407Z", - "iopub.status.busy": "2023-08-25T18:25:53.418251Z", - "iopub.status.idle": "2023-08-25T18:25:53.425787Z", - "shell.execute_reply": "2023-08-25T18:25:53.425211Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Operator([[0.+0.j, 1.+0.j],\n", - " [1.+0.j, 0.+0.j]],\n", - " input_dims=(2,), output_dims=(2,))\n" - ] - } - ], - "source": [ - "# Compose with a matrix passed as a list\n", - "Operator(np.eye(2)).compose([[0, 1], [1, 0]])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Comparison of Operators\n", - "\n", - "Operators implement an equality method that can be used to check if two operators are approximately equal. " - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:20.821642Z", - "start_time": "2019-08-21T09:04:20.815611Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.430378Z", - "iopub.status.busy": "2023-08-25T18:25:53.429219Z", - "iopub.status.idle": "2023-08-25T18:25:53.437171Z", - "shell.execute_reply": "2023-08-25T18:25:53.436569Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Operator(Pauli('X')) == Operator(XGate())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that this checks that each matrix element of the operators is approximately equal; two unitaries that differ by a global phase will not be considered equal:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:21.146256Z", - "start_time": "2019-08-21T09:04:21.141242Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.441787Z", - "iopub.status.busy": "2023-08-25T18:25:53.440627Z", - "iopub.status.idle": "2023-08-25T18:25:53.448489Z", - "shell.execute_reply": "2023-08-25T18:25:53.447893Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Operator(XGate()) == np.exp(1j * 0.5) * Operator(XGate())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Process Fidelity\n", - "\n", - "We may also compare operators using the `process_fidelity` function from the *Quantum Information* module. This is an information theoretic quantity for how close two quantum channels are to each other, and in the case of unitary operators it does not depend on global phase." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:22.171481Z", - "start_time": "2019-08-21T09:04:22.147477Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.453047Z", - "iopub.status.busy": "2023-08-25T18:25:53.451874Z", - "iopub.status.idle": "2023-08-25T18:25:53.460309Z", - "shell.execute_reply": "2023-08-25T18:25:53.459703Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Process fidelity = 1.0\n" - ] - } - ], - "source": [ - "# Two operators which differ only by phase\n", - "op_a = Operator(XGate()) \n", - "op_b = np.exp(1j * 0.5) * Operator(XGate())\n", - "\n", - "# Compute process fidelity\n", - "F = process_fidelity(op_a, op_b)\n", - "print('Process fidelity =', F)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that process fidelity is generally only a valid measure of closeness if the input operators are unitary (or CP in the case of quantum channels), and an exception will be raised if the inputs are not CP." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:04:44.743744Z", - "start_time": "2019-08-21T09:04:44.734826Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.464957Z", - "iopub.status.busy": "2023-08-25T18:25:53.463773Z", - "iopub.status.idle": "2023-08-25T18:25:53.585901Z", - "shell.execute_reply": "2023-08-25T18:25:53.585253Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:53 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "04474d4c73d94b4582feb3061037ba71": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "c7c46bab4ea941eda77aeca5b21d55d8": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "ca13945e4dfa45e9aab1fc9549ef706e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_04474d4c73d94b4582feb3061037ba71", - "placeholder": "​", - "style": "IPY_MODEL_c7c46bab4ea941eda77aeca5b21d55d8", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/tutorials/circuits_advanced/03_advanced_circuit_visualization.ipynb b/docs/tutorials/circuits_advanced/03_advanced_circuit_visualization.ipynb deleted file mode 100644 index 4f31f621050d..000000000000 --- a/docs/tutorials/circuits_advanced/03_advanced_circuit_visualization.ipynb +++ /dev/null @@ -1,872 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualizing a Quantum Circuit" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:19.545078Z", - "start_time": "2019-08-21T09:07:19.541701Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:24.786843Z", - "iopub.status.busy": "2023-08-25T18:25:24.786393Z", - "iopub.status.idle": "2023-08-25T18:25:25.255766Z", - "shell.execute_reply": "2023-08-25T18:25:25.255048Z" - } - }, - "outputs": [], - "source": [ - "from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Drawing a Quantum Circuit\n", - "\n", - "When building a quantum circuit, it often helps to draw the circuit. This is supported natively by a `QuantumCircuit` object. You can either call `print()` on the circuit, or call the `draw()` method on the object. This will render a [ASCII art version](https://en.wikipedia.org/wiki/ASCII_art) of the circuit diagram." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:20.108439Z", - "start_time": "2019-08-21T09:07:20.103752Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:25.259680Z", - "iopub.status.busy": "2023-08-25T18:25:25.259134Z", - "iopub.status.idle": "2023-08-25T18:25:25.263803Z", - "shell.execute_reply": "2023-08-25T18:25:25.263227Z" - } - }, - "outputs": [], - "source": [ - "# Build a quantum circuit\n", - "circuit = QuantumCircuit(3, 3)\n", - "\n", - "circuit.x(1)\n", - "circuit.h(range(3))\n", - "circuit.cx(0, 1)\n", - "circuit.measure(range(3), range(3));" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:20.325315Z", - "start_time": "2019-08-21T09:07:20.317485Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:25.267081Z", - "iopub.status.busy": "2023-08-25T18:25:25.266655Z", - "iopub.status.idle": "2023-08-25T18:25:25.290281Z", - "shell.execute_reply": "2023-08-25T18:25:25.289671Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " ┌───┐ ┌─┐ \n", - "q_0: ┤ H ├───────■──┤M├───\n", - " ├───┤┌───┐┌─┴─┐└╥┘┌─┐\n", - "q_1: ┤ X ├┤ H ├┤ X ├─╫─┤M├\n", - " ├───┤└┬─┬┘└───┘ ║ └╥┘\n", - "q_2: ┤ H ├─┤M├───────╫──╫─\n", - " └───┘ └╥┘ ║ ║ \n", - "c: 3/═══════╩════════╩══╩═\n", - " 2 0 1 \n" - ] - } - ], - "source": [ - "print(circuit)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:20.500036Z", - "start_time": "2019-08-21T09:07:20.491928Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:25.293662Z", - "iopub.status.busy": "2023-08-25T18:25:25.293206Z", - "iopub.status.idle": "2023-08-25T18:25:25.302874Z", - "shell.execute_reply": "2023-08-25T18:25:25.302279Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     ┌───┐          ┌─┐   \n",
-       "q_0: ┤ H ├───────■──┤M├───\n",
-       "     ├───┤┌───┐┌─┴─┐└╥┘┌─┐\n",
-       "q_1: ┤ X ├┤ H ├┤ X ├─╫─┤M├\n",
-       "     ├───┤└┬─┬┘└───┘ ║ └╥┘\n",
-       "q_2: ┤ H ├─┤M├───────╫──╫─\n",
-       "     └───┘ └╥┘       ║  ║ \n",
-       "c: 3/═══════╩════════╩══╩═\n",
-       "            2        0  1 
" - ], - "text/plain": [ - " ┌───┐ ┌─┐ \n", - "q_0: ┤ H ├───────■──┤M├───\n", - " ├───┤┌───┐┌─┴─┐└╥┘┌─┐\n", - "q_1: ┤ X ├┤ H ├┤ X ├─╫─┤M├\n", - " ├───┤└┬─┬┘└───┘ ║ └╥┘\n", - "q_2: ┤ H ├─┤M├───────╫──╫─\n", - " └───┘ └╥┘ ║ ║ \n", - "c: 3/═══════╩════════╩══╩═\n", - " 2 0 1 " - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "circuit.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Alternative Renderers for Circuits\n", - "\n", - "A text output is useful for quickly seeing the output while developing a circuit, but it doesn't provide the most flexibility in its output. There are two alternative output renderers for the quantum circuit. One uses [matplotlib](https://matplotlib.org/), and the other uses [LaTeX](https://www.latex-project.org/), which leverages the [qcircuit package](https://github.com/CQuIC/qcircuit). These can be specified by using `mpl` and `latex` values for the `output` kwarg on the draw() method." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:21.031200Z", - "start_time": "2019-08-21T09:07:20.821727Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:25.306179Z", - "iopub.status.busy": "2023-08-25T18:25:25.305738Z", - "iopub.status.idle": "2023-08-25T18:25:26.033679Z", - "shell.execute_reply": "2023-08-25T18:25:26.032833Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Matplotlib Drawing\n", - "circuit.draw(output='mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Controlling output from circuit.draw()\n", - "\n", - "By default, the `draw()` method returns the rendered image as an object and does not output anything. The exact class returned depends on the output specified: `'text'` (the default) returns a `TextDrawer` object, `'mpl'` returns a `matplotlib.Figure` object, and `latex` returns a `PIL.Image` object. Having the return types enables modifying or directly interacting with the rendered output from the drawers. Jupyter notebooks understand these return types and render them for us in this tutorial, but when running outside of Jupyter, you do not have this feature automatically. However, the `draw()` method has optional arguments to display or save the output. When specified, the `filename` kwarg takes a path to which it saves the rendered output. Alternatively, if you're using the `mpl` or `latex` outputs, you can leverage the `interactive` kwarg to open the image in a new window (this will not always work from within a notebook but will be demonstrated anyway)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Customizing the output\n", - "\n", - "Depending on the output, there are also options to customize the circuit diagram rendered by the circuit.\n", - "\n", - "### Disable Plot Barriers and Reversing Bit Order\n", - "The first two options are shared among all three backends. They allow you to configure both the bit orders and whether or not you draw barriers. These can be set by the `reverse_bits` kwarg and `plot_barriers` kwarg, respectively. The examples below will work with any output backend; `mpl` is used here for brevity." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:22.894879Z", - "start_time": "2019-08-21T09:07:22.884270Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:26.037757Z", - "iopub.status.busy": "2023-08-25T18:25:26.037245Z", - "iopub.status.idle": "2023-08-25T18:25:26.047388Z", - "shell.execute_reply": "2023-08-25T18:25:26.046662Z" - } - }, - "outputs": [], - "source": [ - "# Draw a new circuit with barriers and more registers\n", - "\n", - "q_a = QuantumRegister(3, name='qa')\n", - "q_b = QuantumRegister(5, name='qb')\n", - "c_a = ClassicalRegister(3)\n", - "c_b = ClassicalRegister(5)\n", - "\n", - "circuit = QuantumCircuit(q_a, q_b, c_a, c_b)\n", - "\n", - "circuit.x(q_a[1])\n", - "circuit.x(q_b[1])\n", - "circuit.x(q_b[2])\n", - "circuit.x(q_b[4])\n", - "circuit.barrier()\n", - "circuit.h(q_a)\n", - "circuit.barrier(q_a)\n", - "circuit.h(q_b)\n", - "circuit.cswap(q_b[0], q_b[1], q_b[2])\n", - "circuit.cswap(q_b[2], q_b[3], q_b[4])\n", - "circuit.cswap(q_b[3], q_b[4], q_b[0])\n", - "circuit.barrier(q_b)\n", - "circuit.measure(q_a, c_a)\n", - "circuit.measure(q_b, c_b);" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:26.192606Z", - "start_time": "2019-08-21T09:07:23.731988Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:26.050994Z", - "iopub.status.busy": "2023-08-25T18:25:26.050516Z", - "iopub.status.idle": "2023-08-25T18:25:26.485930Z", - "shell.execute_reply": "2023-08-25T18:25:26.485059Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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2mt1//33q0ePHrJYLJ77t2zZomHDhmnJkiXatWuXunTpUqvHVFust42Ta+M3cs59S5ael8jSvJnnMdeST+XeuUvWO8cThgBAPZecnKykpCTP7dzcXL3zzjv629/+pr17955z3S5dumjChAm6/fbbZbfb1bZtW3355ZfKzs5Wq1atJBGGAACAusvUl8w89NBDcjgceuCBBzRr1qxyXXmTkpLUrVs3lZSUKC4uThEREeXWHTNmjC655JJyYYgk9erVSwMHDpQk7dmzp+YPwiCW4GAFTZ0inT4t50t/9dzvPuKQa8G7slx8kayjbzCuwFpmt9vVr18/2e12o0uBQTgHYEavv/56uTDks88+U8eOHTVp0qTfDEMkadeuXZo4caI6d+6s1atXe+4nDAEAAPWBaXuIpKWladGiRWrWrJmef/75Spfp2bOnUlJS1K1btwqPFRYWaunSpVq7dq0yMjKUm5srl8sl6dcgJCQkxLO8w+HQCy+8oE2bNiklJUVFRUVyu901cGS1x5LQXtaxN8m18AO5lq+QZdC1cs6YJbndsk2dIovNZnSJtSYuLk7z5s0zugwYiHMAZvO///u/uv/++yVJLpdLDz74oObMmVOtbR08eFA33XSTdu7cqdatW3vunz9/PmEIAACos0wbiHzwwQdyuVwaN26cGjZsWOkyDRo0kKQKgciqVat0xx136MiRI+fcR7t27Tx/p6ena/Hixerdu7dCQkK0fv368zyCusE67ma5Nnwj51vzZD3wndzf7pP13rtliY0xurRa5XQ6derUKTVo0EC2AAqCSooKdCo3x+gy6oRAPQdgTj169NDTTz8tqTQMuf3227Vw4cJqby8yMlJffvlluTBEkh555BG99957+vbbb8+rXgAAgJpg2ktmyrru9u/fv8plHA6HpPKByDfffKMhQ4bI4XDorrvu0urVq/XDDz+ouLhYbrdbaWlpkqSgoCDPYHKSdMUVVygrK0tLly7VgAEDauKQDGEJClLQ1IelomK5li2XpXOirNf/3uiyat3evXvVu3dvr7qQm8nGxU/qzQnNK/0JNIF6DsB8goOD9c477ygoqPQ7kWeeeea8w5CzB1CdP3++JCksLEzvvPOOrFbTNjcAAEA9ZtoeIocOHZIktW3bttLHS0pKPL04zgxEHn30URUVFemFF17QtGnTKqy3fPlySVKnTp0U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UFFRkdHl+URVVZUyMjJ0/fXXa/78+UaX41NbtmzRrFmzlJ2drYiICK1evVqFhYV6//33NWLECKPL84m4uDg98MAD6tu3r0JDQ/XBBx9o7ty5stlsuvPOO40uz6fKy8s1c+ZMTZgwQQcPHvT5+QlE/JzNZlNCQoLRZQAAAAAuWbFihbKzs/XII49IktLT07V//349+uijpglEJk+erMmTJ0uS7r33XoOr8a2NGzc2eb106VJt2rRJ69atM00gMnHixCave/ToofXr1+u9994zVSBit9t1ww036LbbblNNTY0hgQi3zPi5kpISpaSkKCUlRZMmTdKOHTuMLgkAAABo1fbt25Wfn99kWX5+vo4cOaKSkhKDqoJR7Ha7KisrFRkZaXQphnA4HNq5c6e2b9+uvLw8o8vxqUWLFslisRgaCtIh4scuv/xyPf/888rIyFBlZaWefvppjRo1Sps2bdL48eONLg8AAAB+5Nujx1XzU22TZXX19Y2/f/lNSavLJCksNESpSXEXPE9ZWVmzDmfn67KyMqWkpHg+iDY4UVmlYz9UNFv+8/G2Nn5JSkuOV0hI8EWv9WKw2+366shR/XwWC3fGH9shSl06dXTrvEuWLFFFRYVuueUWT0v3mvJ//FOVVdXNlrv6HtisVvVITZTFYrnguU6cOKHk5GTV1tbKbrfroYce0rx587w0Es9Un65RSfn3zZa78zWQFH+JoiLCL3iurVu36qmnntLevXtder8uFgIRP+Zss3MaNWqUSkpKtGzZMgIRAAAAuKXixEm9WPxui+uqT9fouVc2nHfZ9VPGuRSItFfBIcF6dcP7qqw61eL6n4/356/79uiq3mnJF73Oi8VqterAoSP6cM/+FtdfaPwhwUGa98tpbp3zySef1JIlS1RcXGxYEHau2jN1WvPfG2VvZXLTC70HE0dnq2e3JJfOFR0drX379qm6ulo7duzQfffdp6SkJM2ePbttg2iD0JAQvbVtp0pbCEWkC48/vnOsbp859YLn+f7773XDDTfo+eefN3z6B26ZCTA5OTk6fPiw0WUAAADAzwxI76lBGZ495SErs5cG9Ovh0raJiYkqLy9vsuzYsWON64wSERaq6yaP8Wzf8FBNmzTG0J90e8Ok3KHq0qmDR/sWjM1R51jX912+fLkWLFig4uJijRs3zqNzeltqUpzycrI82rdbcrzG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", - "text/plain": [ - "
" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Draw the circuit\n", - "circuit.draw(output='mpl')" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:29.055491Z", - "start_time": "2019-08-21T09:07:26.594527Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:26.490236Z", - "iopub.status.busy": "2023-08-25T18:25:26.489774Z", - "iopub.status.idle": "2023-08-25T18:25:26.993483Z", - "shell.execute_reply": "2023-08-25T18:25:26.992530Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA4MAAAKxCAYAAADpUnnoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAACm/UlEQVR4nOzdeVxU9f4/8NeZYRlnUBBRQUBRBENULMAW/ZaYmrtpi5aWeE3LJZdKrFt9u/d3rUTtmmmLpWZdzSi1Ipe+ddWrXtNCVFQWFQN1WEpSlH2ZOb8/aCaRQWeGYc5wzuv5ePiAmbO9z8zxw7zmnPP5CKIoiiAiIiIiIiJFUUldABERERERETkfwyAREREREZECMQwSEREREREpEMMgERERERGRAjEMEhERERERKRDDIBERERERkQIxDBIRERERESkQwyAREREREZECMQwSEREREREpEMMgERERERGRAjEMEhERERERKRDDIBERERERkQIxDBIRERERESkQwyAREREREZECMQwSEREREREpEMMgERERERGRAjEMEhERERERKRDDIBERERERkQIxDBIRERERESkQwyAREREREZECMQwSEREREREpEMMgERERERGRAjEMEhERERERKRDDIBERERERkQIxDBIRERERESkQwyAREREREZECMQwSEREREREpEMMgERERERGRArlJXQCRVLKysqye97fffsMXX3yBRx99FB06dLB6udtuu82e0oiImp0tbSDAdpCISI54ZpDICpcuXcK7776LS5cuSV0KEZEk2A4SEckPwyAREREREZECMQwSEREREREpEMMgERERERGRAjEMElnB29sbo0ePhre3t9SlEBFJgu0gEZH8CKIoilIXQSQFW3vSswd70SMiV+WMNhBgO0hE5Mp4ZpDIClVVVTh//jyqqqqkLoWISBJsB4mI5IdhkMgK2dnZGDZsGLKzs6UuhYhIEmwHiYjkh4POy4woiig3GqQuwyZalRqCIEhdhmyIIlDdgg4BDzXAt5+IiIgcRRRFlJeXS12GTbRarSSfhxkGZabcaEDbPT9IXYZNrgwaAp2ah6KjVBuARUlSV2G9xAmAJ99+IiIicpDy8nJ4eXlJXYZNSktLodPpnL5dXiZKRERERESkQAyDRERERERECsSLs4isEBkZiczMTKnLICKSDNtBIiL54ZlBIiIi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CYImv/76K7p06WK+V5GIiFyHrMNgY9LT0yGKIsLDw23uRKIxs2fPxpdffokXX3wRWq0Whw8fNk8LDQ21OPQEtRwlJSXYu3cvZs+eLXUpJBEeA9TSVVRUICAgAK1atUJtba1Ny9599912BUEAePTRRyEIgsWO2YiISFqKDIMnT54E4NhLVnbt2gUAWLJkCZYsWVJv2scff2zzmGZERESOJIqi3WPXzpw5E2q1GmVlZTYFQeDPcX+JiMj1MAw6SG5ursPWRURE5EpEUcSMGTMavVWCiIhaJtl2IHMzzREGiYiI5IxBkIhIfhR5ZnDPnj1Sl0AtTMeOHbFo0SJ07NhR6lJIIjwGiIiISG4UGQaJbOXn58f7PhWOxwARERHJjSIvEyWy1dWrV/Hdd9/h6tWrUpdCEuExQERERHLDMEhkBb1ejwULFkCv10tdCkmExwARERHJDcMgERERERGRAjEMEhERERERKRDDIBERERERkQIxDBJZQaPRICIiAhqNRupSSCI8BoiIiEhuOLQEkRVCQ0Oxbds2qcsgCfEYICIiIrnhmUEiIiIiIiIFYhgkskJGRgb69OmDjIwMqUshifAYICIiIrlhGCSygiiKqKmpgSiKUpdCEuExQERERHLDewaJZMZDDSROkLoK63mopa6ASF60Wi1KS0sdtr5laz7HtbJytNFpsfDpiQ0eO4JWq3XIeoiIyDYMg0QyIwiAJ/9nEymWIAjQ6XQOW5+HpwYeNQZ4eGqg0+kaPCYiopaLl4kSEREREREpEM8fEFkhNDQUycnJCA4OlroUkgiPASIiIpIbhkEiK2g0GoSFhUldBkmIxwARERHJDS8TJbJCXl4eXnnlFeTl5UldCkmExwARERHJDcMgkRWKi4uxdetWFBcXS10KSYTHABEREckNwyAREREREZECMQwSEREREREpEMMgERERERGRAjEMEllBpVIhNjYWKhX/yygVjwEiIiKSG36qIbKC0WhESkoKjEaj1KWQRHgMEBERkdwwDBIRERERESkQwyAREREREZECMQwSEREREREpEMMgkRW8vb0xevRoeHt7S10KSYTHABEREcmNm9QFELUEQUFBWLp0qdRlkIR4DBAREZHc8MwgkRWqqqpw/vx5VFVVSV0KSYTHABEREckNwyCRFbKzszFs2DBkZ2dLXQpJhMcAERERyQ0vE5UZURRR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khueGSQiIiIiIlIghkEiK2RkZKBPnz7IyMiQuhSSCI8BIiIikhuGQSIriKKImpoaiKIodSkkER4DREREJDe8Z1BmtCo1rgwaInUZNtGq1FKXICseaiBxgtRVWM+Dbz85mFarRWlpqcPWt2zN57hWVo42Oi0WPj2xwWNH0Gq1DlkPERGRLRgGZUYQBOjUfFuVTBAATx4CpGCCIECn0zlsfR6eGnjUGODhqYFOp2vwmIiIqKXiZaJEREREREQKxPMHRFYIDQ1FcnIygoODpS6FJMJjgIiIiOSGYZDIChqNBmFhYVKXQRLiMUBERERyw8tEiayQl5eHV155BXl5eVKXQhLhMUBERERywzBIZIXi4mJs3boVxcXFUpdCEuExQERERHLDMEhERERERKRADINEREREREQKxDBIRERERESkQAyDRFZQqVSIjY2FSsX/MkrFY4CIiIjkhp9qiKxgNBqRkpICo9EodSkkER4DREREJDcMg0RERERERArEMEhERERERKRADINEREREREQKxDBIZAVvb2+MHj0a3t7eUpdCEuExQERERHLjJnUBRC1BUFAQli5dKnUZJCEeA0RERCQ3PDNIZIWqqiqcP38eVVVVUpdCEuExQERERHLDMEhkhezsbAwbNgzZ2dlSl0IS4TFAREREcsMwSEREREREpEC8Z5BIZkQRqDZIXYX1PNSAIEhdhXyIoojy8nKpy7CJVquFwIOAiIjI6RgGiWSm2gAsSpK6CuslTgA82RI5THl5Oby8vKQuwyalpaXQ6XRSl0FERKQ4vEyUiIiIiIhIgfh9PJEVIiMjkZmZKXUZJCEeA0RERCQ3PDNIRERERESkQAyDRFbIycnBxIkTkZOTI3UpJBEeA0RERCQ3DINEVigvL0daWlqL66WRHIfHABEREckNwyAREREREZECMQwSEREREREpEMMgERERERGRAjEMElkhMDAQiYmJCAwMlLoUkgiPASIiIpIbjjNIZAUfHx+MGTNG6jJIQjwGiIiISG54ZpDICpcvX8amTZtw+fJlqUshiSj5GPD29kZISAhCQ0PRqVMnqFS2/el4/PHHERkZ2UzVERERkb0YBomsUFBQgMWLF6OgoEDqUkgiSjoG/Pz88Nxzz2Hr1q3Izc1FcXExcnJykJ2djby8PFy9ehUHDhzAihUrcM8999x0XVOmTMG//vUv7N27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", - "text/plain": [ - "
" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Draw the circuit with reversed bit order\n", - "circuit.draw(output='mpl', reverse_bits=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:31.528574Z", - "start_time": "2019-08-21T09:07:29.102557Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:26.997893Z", - "iopub.status.busy": "2023-08-25T18:25:26.997382Z", - "iopub.status.idle": "2023-08-25T18:25:27.363877Z", - "shell.execute_reply": "2023-08-25T18:25:27.363067Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA8MAAAKxCAYAAAB36H5IAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAACTQUlEQVR4nOzdeVxU9f7H8feZQbZRUlwCQVzREEXLpSwr166GZmZqpd3yaotm2iZ2b9db3VupafWz7WbXzFbjpl2vadtNKss0V9zLJTe2itIUUIGZ+f1BM4mAMuPAwJzX8/HgAXOW7/l8xwHnPd9zvsdwOp1OAQAAAABgIhZ/FwAAAAAAQHUjDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMJ8ncBgC85nVKh3d9VeCbYKhmGv6tAoHA6nSooKPB3GR4JDw+XwS8BAACoZoRhBJRCuzQ11d9VeGbmSCmE30T4SEFBgerWrevvMjySl5cnm83m7zIAAIDJcJo0AAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAqlRcXJxCQkL8XQYAAEApQf4uAABQ8zRr1kyXXnqpunTpok6dOqlBgwayWq06fvy49uzZow0bNmj9+vVau3at7HZ7he20bt1an3/+ubZt26Zrr71WJ0+erMZeAAAAVIwwDACQJFksFiUnJ2vChAkaMGBAhdtddtlluuWWWyRJBw8e1Ny5czVv3jz9+OOPpbZzBeHY2FjFxsZq1qxZmjRpUpX2AQAAoLJMcZp0bm6uUlJS1KZNG4WGhqpZs2aaPHmy8vPzNXbsWBmGoeeff77UPvv379esWbM0YMAAtW7dWjabTeHh4erUqZNmzJihwsJCP/UGAHyvS5cuSk9P19KlS8sNwg6HQ0VFRWWWx8XF6fHHH9fBgwf14IMPymq1SiodhCVp69at+vvf/161nQAAAPBAwIfh9PR0dezYUbNmzVJOTo7at2+voqIiPfvssxo5cqR27twpSercuXOp/f7xj38oJSVFX331laxWqxITE9WkSRNt2bJFf/7zn3XjjTf6oTeoLhk7Ptec0YY2LJ9d4TZzRhv67+xB1VgV4HtWq1WPPfaY1qxZo44dO7qXf//99/r73/+uQYMGKTo6WlarVcHBwQoLC1P37t01fvx4vf/++3I4HJKkkJAQTZ8+XatXr1b//v3LBOE+ffooNzfXL30EAAAoT0CfJp2bm6vBgwcrJydH999/vx5++GHVq1dPkvTkk09q6tSpCgoKkmEYSkpKKrXvVVddpbvuuksXXnihDMNwL1+/fr0GDRqk9957T1u3bi315hEAapOQkBC98847uvbaa93LNm7cqL/+9a/6+OOP3UH3VCdOnNC6deu0bt06vfTSS2revLnuuece3X333bJarerWrZs++ugjWSwln7UShAEAQE0V0CPDkyZNUkZGhiZOnKjZs2e7g7AkpaSkqFOnTiouLlaLFi0UERFRat+RI0fqoosuKhWEJalr167q37+/JGn79u1V3wkAqAJBQUFKTU11B+GioiL97W9/08UXX6wPP/yw3CBcngMHDujee+/VZZddpj179kiSOwjv3r2bIAwAAGqsgB0Z3rlzp1JTU9WoUSNNnz693G26dOmizZs3q1OnTmXWnTx5UkuXLtUXX3yhffv26dixY+43h64QHBwcXGqfffv2afLkyfrss88UFBSkwYMH65lnnlHDhg193DsAODePPPKIhgwZIknKz8/XkCFDtGLFCq/by83NVVhYWKllNpuN+RUAAECNFbBheOHChXI4HBo1apTq1q1b7jauN26nh+EVK1ZozJgxOnTo0BmP0apVK/fPx44dU+/evRUZGamFCxfq+PHjSklJ0aBBg7Rq1Sr3SAlql+LCAh0/xqgWAkuXLl00depUSVJhYeE5B2HXZFkxMTGSSsK1zWZT06ZN9fTTT2vcuHE+qRsAAMCXAjYM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0aMVEhJSBZUBAOA9wjAAACilcePGmjBhghYsWKCtW7eqqKhIBQUFOn78uAoLC7V582bNnz9fd9xxhxo0aHDGth555BG98cYbWrJkCYEYAFCjEIYBAIAkqVu3bnrjjTd06NAhvfDCC7rlllvUoUMHWa1W9zZBQUFKSkrSmDFj9NJLLykzM1Pz589X586dy7T3yCOP6OGHH5YkDRgwQAMHDqyurgAAcFamCMO5ublKSUlRmzZtFBoaqmbNmmny5MnKz8/X2LFjZRiGnn/++VL77N+/X7NmzdKAAQPUunVr2Ww2hYeHq1OnTpoxY4YKCwv91BtUh4wdn2vOaEMbls+ucJs5ow39d/agaqwKAKqGzWbT888/r7Vr15Y5pbmoqEibN29WWlqa0tLStHXrVhUXF7vXh4WFacyYMdqwYYNmz56tsLAwSaWDsCRNmjRJS5YsqbY+AQBwNkH+LqCqpaena+DAgcrJyZHNZlP79u2VlZWlZ599Vnv37tUvv/wiSWU+0f7HP/6h+fPny2azqWnTpkpMTNSPP/6oLVu2aMuWLVq3bp0WL17shx4BAOA73bp1U2pqqlq2bOle9vPPP+uVV17RokWLtGXLFp08ebLUPqGhoercubNGjBihMWPGqH79+rJYLLr//vs1ePBgpaWl6c4773RvP2nSJD333HPV1icAACojoEeGc3NzNXjwYOXk5Oj+++9Xdna2Nm7cqJycHM2cOVPLly/XunXrZBiGkpKSSu171VVXacOGDTp27Jh27dqltWvXav/+/Vq3bp3OP/98vffee9q6daufegYAwLnr1auX0tLS3EE4Pz9f99xzj2JiYjR16lStW7euTBCWpBMnTmjNmjW677773NueOHFCktS2bVuCMACgVgjoMDxp0iRlZGRo4sSJmj17turVq+del5KSok6dOqm4uFgtWrRQREREqX1Hjhypiy66qMysmV27dlX//v0lSdu3b6/6TgAAUAW6deum999/X3Xr1pUkrVq1SklJSZozZ065AbgiBQUFevLJJ9W5c2dlZWWVWjdjxgyCMACgxgrY06R37typ1NRUNWrUSNOnTy93my5dumjz5s3q1KlTmXUnT57U0qVL9cUXX2jfvn06duyYHA6HpN9DcHBwsHv7RYsWaeHChVq/fr1++uknxcXFadiwYfrzn//sfqOB2qe4sEDHj+X6uwwA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jHQp51up83VSULBPywMAAH7SvHlzdenSRRdccIHCwsLkdDp19OhRbdmyRRs2bNDPP/981jbat2+vxYsXa/To0dqwYUM1VI3KIgxXID09XZKUlJTk30IAVKljP0qbFknFJ6WN/5YuGuF5IM5Il779tOTn9MXShcMlK39dAQColS644AJNmDBBN9xwgxo3bnzGbXfv3q358+frlVde0U8//VRmffv27fXZZ5+pSZMm+t///qdevXppy5YtVVU6PMR4fQUIw4A5hEZIYfVLfi4sKAnEebmV3//UICxJ9WMki9WXFQLwN6dTktP1wJ+VAKhK7dq10yeffKKdO3fq7rvvPmsQlqT4+HhNnz5dGRkZevnll1W/fn33ulODsCTt2bNHBw4cqKry4QXGLiqwefNmSVKnTp38XAn8IWPH51r8RG/1vHGWuiQ/UO42c0YbatE5WUMeWFbN1cGX6oRKFw2XNr4rHfvh90BcmRHi04Nwi+5S68slw6jSkgFUk+KTUvb2kt/1U21aJMV2lhq1kgyGFYBaz2Kx6L777tM//vEPhYaGupcXFBToiy++0Pr167V582YdOXJEhmEoKipKF110kbp3764ePXrIYrEoODhYt912m5KTk3X77bdr3759pYLwunXr1L9/f/3666/+6ibKYYo/4bm5uUpJSVGbNm0UGhqqZs2aafLkycrPz9fYsWNlGIaef/75UvucOjK8evVqDR06VI0bN1Z4eLguvfRSffLJJ37oCYCq4ArE9c4veVyZEWKCMBDYfjkoffWy9F2alP9L6XU/75c2L5HWviWdzPNHdQB8JSwsTO+//75mzZrlDsLff/+9Jk+erJiYGF199dX629/+psWLF2vFihX69NNP9eabb+q+++5Tz5491aZNG82cOdMdcps2baply5Zp7dq1BOFaIODDcHp6ujp27KhZs2YpJydH7du3V1FRkZ599lmNHDlSO3fulCR17tzZvU9eXp727t0rq9WqFStWqGfPnvr666/dE2mtXr1aAwcO1NKlS/3RJQBVwJNATBAGAtvhQ9KmxSUjw2dy7AdpQ2rJ3wsAtU9oaKiWLVumq6++WpLkcDj01FNPKTExUc8++6yOHDly1jb27dunBx98UImJifrggw/cy202mySCcE0X0GE4NzdXgwcPVk5Oju6//35lZ2dr48aNysnJ0cyZM7V8+XKt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p9Po0gyxc+dOBQQEKCUlxevXtnn9ivCo5ORk7d27t9u24cOHG1QNAADA1R04cEBlZWX68Y9/rNdee0379u1TRUWFQkNDVVFRYXR5XtHU1KS0tDTdfvvteuCBB4wux+t27typJUuWKCsrS6GhoVq3bp1KS0v19ttvKzc31+jyrrmYmBg99thjGjt2rIKDg/Xuu+/qBz/4gQICAnT//fcbXZ5XnT59WnfddZdmz56tjz/+2OvXJwz7uICAAI0cOdLoMgAAAFyyevVqZWVl6emnn5Ykpaam6vDhw1q5cqVpwvDcuXM1d+5cSdIjjzxicDXet23btm6vV61aperqalVWVpoiDBcVFXV7PWbMGG3evFm7du0yVRh2OBy64447dN9996mlpcWQMMxt0j6uvr5eCQkJSkhI0Jw5c7Rnzx6jSwIAALii3bt3q7i4uNu24uJiffbZZ6qvrzeoKhjJ4XCosbFRYWFhRpfidU6nU/v379fu3btVWFhodDletXz5clksFkN/IURn2IdNnjxZGzZsUFpamhobG7VmzRrl5eWpurpas2bNMro8AADgQz7/skHnzjf12N7e0dH150fH63u87mSRdMOoOFmtV++12O32Hne1db622+1KSEgYyDDc1trapk9Pnumx3dXxS9KI66MUGTH02hd7jfzfqbNq+bq127bexnul78GQ4CAlxcX0+7orVqxQQ0OD7r33XndL94jzjU0680VDj+39+Qwkx49QUFBg39c6f17x8fFqbW2Vw+HQE088oWXLlg18EAPgcDj0yWen9M2Zy/0Zf3TkUA2/LqrPa9XU1OiFF15QbW2tLBaLJ8p3C2HYh3XeXtMpLy9P9fX1euaZZwjDAACgXyyyaOOf3lRbW3uv+5svtGj9pqorvp46MV03jjYmyHpCYKBN7/3vB/ro+Ile9/c1/sjwMN2/ZME1r/Naajj/lV7d+lav+7453t623V42s99h+Le//a1WrFihrVu3GvaLkE6BQYH6r6q31dj091739/UZGDsmUTcmx7t0rfDwcNXV1am5uVl79uzRT37yE8XFxemee+4Z2CAGwGq16sixz7T34OFe9/c1/qBAm5bdPb/P6/ztb3/THXfcoQ0bNhg+3ZPbpP1MTk6OPv30U6PLAAAAPmbYdZEqKcx269zh10WpOH+KS8fGxsbq9OnT3badOXOma59RLBaL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", - "text/plain": [ - "
" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Draw the circuit without barriers\n", - "circuit.draw(output='mpl', plot_barriers=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:34.023384Z", - "start_time": "2019-08-21T09:07:31.568046Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:27.369074Z", - "iopub.status.busy": "2023-08-25T18:25:27.367601Z", - "iopub.status.idle": "2023-08-25T18:25:27.728504Z", - "shell.execute_reply": "2023-08-25T18:25:27.727814Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0IAAAKxCAYAAAB+Lxx3AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAACQlklEQVR4nOzdeXhTVf7H8c9NupFAgbLYQoGylK1sUkDBDRSUCoigiAoOIqKCCy4D6jhu4wIIyijKiAuijiD+FBXBbQQVAUG2IvsmW0uLVkDoRtskvz9qIqUpNCVt2tz363l4bHLvPfd7Ypd8cu49x3C5XC4BAAAAgIlYAl0AAAAAAFQ0ghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADCdkEAXAP9yuVzKdjoCXYZPbBarDMPwS1sul5RXtbqvMKvkp+4DAAAUvh/Mzg50GaVms9n89l7QFwShIJPtdKj2kv8FugyfHLm0j+xW/3wr5jmkB+f5pakKM3moFM5PIgAA8JPs7GxVr1490GWUWmZmpux2e4Wfl0vjAAAAAJgOQQgAAACA6RCEAAAAAJgOQQgAAACA6RCEAAAAAJgOQQgAAACA6RCEAAAAAJgOQQgAAACA6RCEAAAAAJgOQQgAAACA6RCESlCvXj0ZhqGUlJRAlwKgAmQdPrvjnQ4p+6hfSgmInD8kR8HZtXG2ryEAIDiFh4ercePGgS6jmJBAF1AZpaSkKCMjQ1FRUYqNjQ10OQDK2f510o5vpYS+UkyC78c7HdKmRdKRA1LidVL1ev6vsTxlHZbWfVBYd4eBkrUMfxkO7ZA2LZSa9ZCanu//GgEAFSsyMlI9evRQYmKiEhMTFRMTo7CwMOXl5SktLU1r167V2rVrtWLFCh07dqzEdsLDw/Xxxx+rffv26tWrl3bt2lWBvTg9gpAXycnJkqQOHToEthAA5e7wfmnHksKvN39R+F9fwpA7BP26o/Dx+vlSj1FlCxOB4HRKGz6WTmQW/vv5U9/DkDsEuZzS7mWFgape8/KrGQBQfjp27KixY8dq2LBhstvtJe43aNAgSVJWVpb++9//asaMGfr555+L7OMOQUlJSZKkzz77TO3atZPD4Si/DviAS+O8IAgB5lG7kRTb6a/Hm7+Q0jaX7thTQ5DFKrW5vOqEIEmyWAprtvxZ8+97CsNQaS+TOzkESYUhsm7T8qkVAFB+6tatq3nz5ik5OVm33Xab1xCUl5dX7Dm73a7bb79dGzZs0Pvvv6+6detKKh6CMjMzNXr06EoTgiSCkFcbNmyQVJiIzcoxa7byL79Szi+/LrbN5XKp4O8PKr/fVXLt2VvxxVWAlC3f6cXhhtYumlriPi8ON/Tp1P4VWBXKg2FIrS7zPQx5C0Edrq6aIaB2I+nca3wPQ95CUNsrJIO/LABQpQwaNEhbtmzRdddd53nu2LFjevXVV3XjjTeqVatWslqtCg8Pl9VqVatWrXTjjTdq5syZRS6LGzp0qDZv3qwhQ4YUC0FJSUlatmxZhfftdIL+z1VGRoYmTJigFi1aKCIiQo0aNdK4ceOUlZWlUaNGyTAMvfzyy0WOOXlE6Mcff9SgQYNUr1492Ww29ejRQ19/XTwcBBvLTcOkuCZyzHxdrt8yimxzzv9Erp83ynLTcBlN4wJTIOBHvoahYApBbr6GIUIQAASHcePGaf78+apXr/AG14yMDN15551q2LChxowZo7lz52rHjh1yOgt/4TudTu3YsUNz587VHXfcoYYNG+rOO+/U77//LkmqX7++Pvjgg0ofgqQgD0LJyclq3769pkyZovT0dLVt21b5+fl66aWXNHToUG3dulWS1KlTJ88xmZmZ2r17t6xWqxYvXqwLL7xQK1as8Eya8OOPPyopKUkLFiwIRJcqjBEaqpDxD0i5uXK88G/P864DKXLOfkdG61ayDLkmcAUCflbaMBSMIcittGGIEAQAweGuu+7Sv//9b8/j+fPnKyEhQTNmzFBmZmap2sjMzNSMGTOUkJCgTz/9tMi2EydOVNoQJAVxEMrIyNCAAQOUnp6uBx54QGlpaVq3bp3S09M1efJkLVq0SKtXr5ZhGEXuBdqwYYNcLpck6emnn9abb76pgwcPav369UpLS9Nll10mp9Op++67z7NfsDLiW8hy/XVyrV0n56Iv5HI45HhuquRyyTr+ARlWa6BLBPzqTGEomEOQ25nCECEIAILDRRddpBdffNHz+IknntA111yjX3/9tUztHT16VGFhYUWeCw0NlWEYZ1VneQraP1333HOPUlJSdNddd2nq1KmqUaOGZ9uECRPUsWNHFRQUKC4uTpGRkZ5t7vuDHA6H3nnnHd18882y/vmGv2bNmnrjjTdksVj0yy+/aMeOHRXbqQCwDLtBatZMjtffkPOVV+XavkOWm/8mo5E5phUvyMtWzvEMr/8QnEoKQ6kbgz8EuZUUhtK2EoIAIBjYbDa99dZbslgKf4E/++yzevLJJ8vc3qkTI7gnVbBYLJo1a5ZsNtvZF10OgvLP19atWzVv3jzVrVtXEydO9LpPYmKipOITIrjvD0pKSvJMC3iyuLg4NWzYUJKUmprqte2kpCQZhqEnnniijD2oPIyQEIWMv1/Ky5dz4SIZ7RJkGXx1oMuqMCs/elyvjann9R+Cl7cwtPUrc4QgN29haPMiQhAABIOnnnpKzZsXrnPwww8/6J///GeZ2/I2O1yfPn08l8O1aNFC//rXv86+6HJQhSZ5Lb25c+fK6XRq2LBhql69utd9qlWrJqnkIDRmzJgS23ePIHlr+4MPPvC0cba6dOmi9PR0n45xhYVJM1/xy/k97HYpNFQqKJDRtYsMi3/f+bSMbynDy3SMZWENraZBT+/0S1uS1K7XbYo/b4jXbR9P6uOXc7SMj5cjP8cvbcH/bur1jC7rOMLz2OVyacqHN2nj898FrqgK1LLheRo/aI5CQ8I9zy3b8n9688UH5HKnIgBApeKe2MCbWrVq6Y477pAkZWdn65Zbbinz7R7eQpD7nqBbbrlFycnJstlsGjNmjJ566in98ccfXtuJj4/3jE6VRXR0tNasWePzcUEZhJYsKVwdsVevXiXuk5KSIqloEHI4HNq0aZMsFot69uzp9TiHw6H9+/dLkho3blxk27Fjx3Tvvfdq6tSpGj58+Nl0QZKUnp5e4qhTiSLCFXrWZ/6Ly+WS4/lpUkG+1LiRnHPel+WSi2U0iPHbOQ6mHZRyT/ilrZBw/w691oqOV+N2vf3a5qkOph1UwYnscj0HysZqCVGoin9PFeQavv9sVlFNoyTDKHo/YKhh16H0X5Vf4J+fWwBAxRkxYoTnUrU33nhDu3btKlM7pwtBkrRz5069+eabuvvuu2Wz2TRixAi99NJLXttKS0srUw1nKyiD0L59+yRJTZo08bq9oKBAy5cvl1Q0CG3fvl05OTmqX79+kXuKTrZy5UodP35cCQkJio6OLrLtkUceUcuWLTVs2DC/BKFT2y8NV1iYfjvrM//F+ckCuTb8LMvIEbJ0P18Fd94tx/PTZJ062W83vzWIaeDXEaGqpkFMA0aEKiGrJUR39H1ZXVsWrhXlcrlkGIYMw9D4699W7dpRWrHtowBXWb66tLhSdyS9opBTVog9r82VmnjbIr288DblOwhDAFDZOJ3OEsPF6NGjPV//5z//KVP7ZwpBJ7d/9913e85bUhCKiYk56xGhsgjKIJSVlSVJysnx/uZy3rx5ysjIUI0aNdS06V8X+bsnSvC2aq7bq6++Kkm68cYbizy/Zs0avf7661q7du1Z1X5qm77KchSo9pL/+eX8rtRUOWfNltGqpSzXXSvDapVl+DA533pbzk8WyDpooF/Os2PnDtmt/vlWPFEgPTjPL01VmB07dyo8KH8Sqy5vs8M5HX8Ff4th0W19X9SLL76omIQAFVnOTp0d7lQdm16m+S/sVoeBkp9+fAEAfpKVleX1Fo7atWsrIaHwD9dPP/2kbdu2+dx2aUOQVHjf/k8//aRu3bqpXbt2qlmzptfL43bu3Cm73e5zLWcrKG9zdafCdevWFduWlpam8ePHSypcMPXkUQ33vT1Hjx71OiPc0qVL9d5776lOnTqedCsVXi53++2366677vJ8c1V1LqdTjikvSE6nrOPv90yVbbnuWhkt4+WcNVuug4EZxgTKU0lTZMvLAOjpFl2tyrxNkX2y0i66CgCoXDp37uz5euXKlT4f70sIclu1apXX81cGQRmEevcuvKdj8uTJRQLN6tWr1atXL2VkFE59fPJCqtJfQSg0NFRjx44tklgXLFjgmUVu9uzZRS6de/nll3Xo0KGgmCXOzfnhfLm2bJVlxHAZJ90LZVitsv79fsnpkOP5aUG/lhLMpbTrBJ1p0dWqrKR1gjxB0CjdoqsAgMrn5Pe+vl7FVJYQdOp5Tn3vHWhBGYQmTJigOnXq6MCBA0pISFD79u0VHx+vbt26qVmzZrr00kslFZ8xzn1p3IwZM/Ttt98qNjZW3bp1U6NGjTRw4EAdPXpU06dPV//+/T3HZGRk6NFHH9Vjjz2mgoICHT16VEePHpUk5ebm6ujRo6eduaMycu3fL+fb78po01qWawYX227ENZFl+DC5Nm6S85MFAagQ8L9SL5Z6hkVXq7LSLpZ6pkVXAQCVU61atTxf+zLpT1lD0KnnqV27dumLrQCGK0g/0t+6davGjx+v77//XpLUtm1bjRo1SqNHj1bz5s21Z88erVq1St26dZNUOENbTEyMoqKi9Pvvv2vRokV68skntWnTJtntdl188cV66KGH1LVr1yLnSU5O1rnnnnvaWvbs2aO4uLhy6eep/HmPUEU5cmkfU98jNHmouEcowEoTgr55XpJLkiH1fkByuaTti6WU5L/2SUgqfhlZVXGmEHRq/yXpyAFp/UeS888AVKepuGcIACqBku4RiomJUWxsrCIiIrRx40bPh/dn0rFjRy1btkzVq1f3KQRJheGrffv2ys3N1YEDB7wuDZOZmRmQe4SCNgiVJDMzU5GRkTIMQ8ePHz/rlW4zMzO9TmrQq1cvjRgxQjfffLPOP/98RUREnNV5SosgRBCCb0o7EuQtCARLGCrNSJC3/kuEIQCojEoKQmfjwgsv1AcffKDrrruu1CGotAIVhEz3p2rz5s1yuVxq2bLlWYcgqXBR1ZLWHIqLiytxG4DK4Via9Nufa/CWeDlcCYw/L5OT/gpDv/wo1W9VdYKA0yntWXHmy+FK4r5Mzh2Gft8jHT1QGIgAAMFj2bJlatasmXJzcwNdit8E5T1Cp7Nx40ZJxe8PAmBOtWKldv0ka6hvIcjNOOmeoWo1pc5Dqk4IkiSLRTr3WskW5XsIcnOHIWtY4fGEIAAITsEUgiQTjghVVBAy2RWHQJV2TmupdmMprIyDxO4wlJ9T9jYCKby61OUGKTTc9xDkVruRdMGtVbP/AABzIggBgM7+DbxhVO0QEFbND21U4f4DAMzHdEFoyZIlgS4BAAAAQICZ7h4hAAAAACAIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0yEIAQAAADAdghAAAAAA0wkJdAHwL5vFqiOX9gl0GT6xWax+ayvMKk0e6rfmKkSY/7oPAAAgm82mzMxMv7Q1Zeb7OpaVrUi7TeNvv77E586GzWY76zbKgiAUZAzDkN1q3v+thiGFm7f7AAAAhe8H7Xa/tBUWHqGwfIfCwiM8bXp7riri0jgAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAAphMS6AIAf3K5pDxHoKvwTZhVMoxAVwEAQHBwuVzKzs4OdBk+sdlsMngzUOEIQggqeQ7pwXmBrsI3k4dK4fwkAgDgF9nZ2apevXqgy/BJZmam7HZ7oMswHS6NAwAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQAgAAAGA6BCEAAAAApkMQOo169erJMAylpKQEuhQA5czlqhxtAACAihES6AIqq5SUFGVkZCgqKkqxsbGBLgdAOco6LG39Wmp3pRQRWbY2Du2QDm2V2vWXLFb/1gcACAyr1aq2bduqQ4cOioyMlMViUXZ2trZt26bk5GTl5OScsY3w8HC9//77evPNN7Vw4cIKqBqlRRAqQXJysiSpQ4cOgS0EQLnKOiytnSflZRX+N3Go72Ho0A5p02eFI0I/L5A6XEUYAoCqKiIiQkOHDtUtt9yirl27qlq1al73czgc2rx5s+bOnas333xTv/32W7F9wsPD9fHHHyspKUlXXnmlrrnmGsJQJcKlcSUgCAHmYA0t/CdJOX8UhqHcY6U//uQQJEmh1SSD36wAUOWEh4frySefVGpqqmbPnq2LL764xBAkFY4WdejQQRMnTlRKSorefvttRUdHF2nPHYIkKS8vT0ePHi3vbsAHjAiVYMOGDZKkjh07BrgSBELKlu/00bO9dOENU5TY7+9e93lxuKG4Tv008O98slOVRdQoHAVaO0/KOfpXGCrNyNCpISimndT2Cskwyr3sCuHIl9K3SakbJLnvf3JJ25dIsR0le51AVgcA/tOtWze99dZbatu2bZHnd+/erdWrV2vdunVKT0+Xy+VSrVq11LFjRyUmJqpjx46yWCwKCwvT3/72N/Xv31933323PvrooyIhKDMzU0lJSVq2bFkguocSmOJzy4yMDE2YMEEtWrRQRESEGjVqpHHjxikrK0ujRo2SYRh6+eWXixxz8ojQjz/+qEGDBqlevXqy2Wzq0aOHvv766wD0BEB5cIeharUKH5dmZCjYQ9Dve6UfZkpbv5KOpRfddmCd9ONb0uYvJacjIOUBgN+MGTNGK1as8ISgvLw8vf322zrvvPPUokUL3XDDDZoyZYreffdd/fe//9XLL7+s0aNHq3PnzmrevLkmTZqk33//XZIUFRWl9957T7t27SIEVQFBH4SSk5PVvn17TZkyRenp6Wrbtq3y8/P10ksvaejQodq6daskqVOnTp5jMjMztXv3blmtVi1evFgXXnihVqxY4Zk04ccff1RSUpIWLFgQiC4BKAe+hKFgD0EZv0jJ86WC3NPvl7ZJ2viZ5HJWTF0A4G/33nuvZsyYIau18MbO1atX69xzz9XNN9+sn3766YzH7927Vw8//LBatWqlOXPmeJ53v2ckBFVuQR2EMjIyNGDAAKWnp+uBBx5QWlqaZ2hz8uTJWrRokVavXi3DMIrcC7Rhwwa5/nyH8/TTT+vNN9/UwYMHtX79eqWlpemyyy6T0+nUfffd59kPQNVXqjDkCu4QlJfjW7j5bZe0f2351gQA5WHo0KGaNm2a5/HkyZPVvXt3bdmyxee2fv/9d91yyy2eK4rcvvrqK0JQJRbUQeiee+5RSkqK7rrrLk2dOlU1atTwbJswYYI6duyogoICxcXFKTLyr5sB3PcHORwOvfPOO7r55ps9nxTUrFlTb7zxhiwWi3755Rft2LGjYjuFClWQl62c4xle/yE4lRSGdNJnHsEagqTCUR5Hvm/HHFjPqBCAqiUmJkb/+c9/PI8ff/xxPfTQQ3I4yna9r3tiBPcVRu4Pyq+55hr17t37rOtF+QjaILR161bNmzdPdevW1cSJE73uk5iYKKn4hAjuNJ+UlKRBgwYVOy4uLk4NGzaUJKWmpkqSfvjhB/Xu3VsxMTEKDw9XbGxskUvvUDWt/OhxvTamntd/CF7ewtCpgjEEuVxSygbfj8s9Jv2+x//1AEB5mTlzpmrXri1JmjNnjv71r3+Vua1TZ4fLzMzUCy+84Nn+5ptvFvkwHpVH0M4aN3fuXDmdTg0bNkzVq1f3uo97SsSSgtCYMWNKbN89guRu+8iRI2rfvr1uv/121a9fXykpKZo4caK6d++uTZs2lWlR1i5duig9Pf3MO8LDGlpNg57e6bf22vW6TfHnDfG67eNJffxyjpbx8XLkn3lBNlS8WvZoPXTt/ym6dtMizy/d/L7e+vd4uRRcl8ZGhFXXq2O3lenYR/8+WZ+tnu7nigDAd07n6YeoL774Yg0YMECSlJ6errvvvrvM5/IWgpKSkrR8+XJ16tRJl112mRo3bqx77rlHzzzzTIntxMfHy2KpnOMTg0beK3v1SKWlp3nez3p7LpCio6O1Zs0an48L2iC0ZMkSSVKvXr1K3CclJUVS0SDkcDi0adMmWSwW9ezZ0+txDodD+/fvlyQ1btxYknTVVVfpqquuKrJf165d1apVK3300UcaN26cz31IT0/3jDihdELCbX5tr1Z0vBq3K98h7YNpB1VwIrtcz4GySVWq5nzzrO679jUZfw79uFwuzfxkgn49mhLg6vyvVvX6ZT42Nyef31cAqoSxY8d6vh4/frwOHz5cpnZKCkHue4Juv/127dixQxaLRbfddpsmTZpU4qV3aWlpZaqhIjj/rNnpcHh+z3t7rioK2iC0b98+SVKTJk28bi8oKNDy5cslFQ1C27dvV05OjurXr1/iMObKlSt1/PhxJSQkFFk461R16hQushESUraX+XRtwztraMkLn1VWDWIaMCJUSXVpcaXGXDnDE4IkyTAMTbtzqSZ9OESHMyvvH66yCLWGl/lYa5jLc8kwAASS0+ksMVhER0dr8ODBkqRDhw5p3rx5ZTrHmUKQVLgG0aJFizRgwAA1btxY/fr1K3HG4ZiYmEo7ImT58z55i9Xq+T3v7blAKut75qANQllZWZKknBzvbzDnzZunjIwM1ahRQ02b/nXZi3uihLy8vBLbfvXVVyVJN954Y7FtDodDTqdT+/bt08MPP6zo6Ghdd911ZepDWYb4zO5EgfRg2X6nBcyOnTsVHrQ/iVXXqVNkG1bJ9ecHefVrxek/D6wu1aKrVc26D6XDe30/7sXZj8oe9ajf6wEAX2VlZZV4W8Tll1+u0NBQSdKsWbOUn+/j7DAqXQhy+89//uO5DO90QWjnzp2y2+0+11IRnn3lPR3LzFJMdIznaipvz1VFlTN6+oE7Ga5bt67YtrS0NI0fP15S4YKpJ3/a674/6OjRo15nhFu6dKnee+891alTx+s1pZdcconCwsIUHx+v5ORkLVmyRPXqcWM9UJV4WycoNKLwa/evi9IsuloVxXby/ZioxpI9yu+lAIDfuSfKkqTFixf7fLwvIUiSvv/+e8/lcCefG5VD0AYh91SFkydPLhJoVq9erV69eikjo3D645MXUpX+CkKhoaEaO3as/vjjr+miFixY4JlFbvbs2V4vnXvzzTe1cuVKzZ07V5GRkbr88ss99xMBqPzOtFhqaLXSLbpaVdVtJkWe48MBhhR3frmVAwB+dXIY8fZh+en4GoIkKTs7W9u2FU5C0759e4WFhZWhapSXoA1CEyZMUJ06dXTgwAElJCSoffv2io+PV7du3dSsWTNdeumlkorPGOe+NG7GjBn69ttvFRsbq27duqlRo0YaOHCgjh49qunTp6t///5ez9uqVSudd955uv7667V48WIdP35czz33XPl2FoBfnCkESZJhKcWiq1WYxSJ1HCzZSjPCYxS+PlGNy70sAPAL9yRX6enpOnLkSKmPK0sIcnMvpRIWFqZzzvHlkyaUt6C9MyE2NlY//PCDxo8fr++//1579+5V27ZtNXPmTI0ePVrNmzeXVDQIpaen69ChQ4qKitKtt96qmJgYPfnkk9q0aZPsdrsGDx6shx56SF27di1VDbVq1VKLFi20a9eucukjyk9s254a99/TT418pu2oWkoTgtzc6wytnSflHP0rDAXLPUPhdqnrDdKO76VDWyWnl0mOatSXml8k1W1afBsAVFbvvvuu6tWrp+PHj/t03AsvvFCmECRJ33zzjf744w/l5OSUeO86AiNog5AktWnTRgsXLiz2fGZmpvbu3SuLxaJ27dp5no+OjvasBCwV3tTWr1+/Mp//119/1fbt23XeeeeVuQ0A5c+XEOQW7GEotJqU0FeKv0RK2yzt/O6vbV2HSZHRwbWYLABzeOSRR8p03DPPPKPevXurQYMGPoUgqXDx1pkzZ5bpvChfQR2ESrJ582a5XC61bNlSNpt/1p0ZPny4WrRooU6dOqlWrVrauXOnpk2bppCQEN13331+OQcA/zua4nsIcvMahj6Qzr9ZsgbJb9ewalKTLtLO7yW5JBlSzZhAVwUAFevgwYPq1auXGjdurJUrVwa6HPhJkPyp9s3GjRslFb8/6Gycf/75euedd/Tiiy8qNzdXjRo1Uq9evfSPf/yjxLWMAAReZIxUt4X0207fQpDbqWGo6fnBE4IAAH85ePCgDh48GOgy4Eem/HNdHkHorrvu0l133eW39gBUDItVat9fOrhRatix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/npbcXIIkgqvtGjYsGG59wMASosRIS8IQkjZ8p0+eraXLrxhihL7/d3rPi8ONxTXqZ8G/n1hBVdXcbIOSykbpLzswsd52dKuH6TYjlJEZGBrg39YLFKHq6SfF0gZu/8KQ6UZGTo1BFnDpM5DpMjo8q8b/tW+fXu99dZbSkxMLPJ8VlaWkpOTtX79ev3++++SCgPNueeeq06dOqlatWqSpPj4eL3yyisaPny4Ro4cqb179xYJQZmZmerbt69+/PHHiu0YAJwGQciLDRs2SJI6duwY4EqAwMjPkTZ/WfjG+GQuZ+ElVHt/kqJbS20ul6yhgakR/mOx+h6GSgpBNWMqrm74x2233abp06crLCxMkuR0OvXVV19pxowZ+uKLL+RwOLweFxISogEDBmjs2LHq3bu3JKl79+5KTk7W9u3bPX9D3SFo+fLlFdMhACiloL80LiMjQxMmTFCLFi0UERGhRo0aady4ccrKytKoUaNkGIZefvnlIsecPCL0448/atCgQapXr55sNpt69Oihr7/+OgA9ASpGfo605v3iIagIl5S+VVr3f4VviFH1ucNQ3eaFj093mRwhKHjcd999mjlzpicEbdq0Sd26ddOVV16phQsXlhiCJKmgoEAff/yx+vTpowsuuEA7duyQJEVERBCCAFQJQR2EkpOT1b59e02ZMkXp6elq27at8vPz9dJLL2no0KHaunWrJKlTp06eYzIzM7V7925ZrVYtXrxYF154oVasWOGZNOHHH39UUlKSFixYEIguAeVu8xdS1u+l2/ePg9L2JeVbDypOqcKQixAULG666Sa98MILnsfTpk1TYmKi1q5d63NbK1as0HnnnVdkIgSXy6WJEycSggBUWkEbhDIyMjRgwAClp6frgQceUFpamtatW6f09HRNnjxZixYt0urVq2UYRpF7gTZs2OCZ+ebpp5/Wm2++qYMHD2r9+vVKS0vTZZddJqfTqfvuu8/rDDkILgV52co5nuH1XzDK+l3K+MW3Y9I2S3lZ5VMPKl5JYUgn/bojBFV9cXFxmjFjhufxY489pvvvv195eXllai88PFxz5swpMkW2YRh64IEHFB3NTWMAKqegvUfonnvuUUpKiu666y5NnTq1yLYJEyZozpw52rBhg5o2barIyL/u+nbfH+RwOPTOO+9o0KBBnm01a9bUG2+8oebNm+uXX37Rjh07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", - "text/plain": [ - "
" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Draw the circuit without barriers and reverse bit order\n", - "circuit.draw(output='mpl', plot_barriers=False, reverse_bits=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Backend-specific customizations\n", - "\n", - "Some available customizing options are specific to a backend. The `line_length` kwarg for the `text` backend can be used to set a maximum width for the output. When a diagram is wider than the maximum, it will wrap the diagram below. The `mpl` backend has the `style` kwarg, which is used to customize the output. The `scale` option is used by the `mpl` and `latex` backends to scale the size of the output image with a multiplicative adjustment factor. The `style` kwarg takes in a `dict` with multiple options, providing a high level of flexibility for changing colors, changing rendered text for different types of gates, different line styles, etc. Available options are:\n", - "\n", - "- **textcolor** (str): The color code to use for text. Defaults to `'#000000'`\n", - "- **subtextcolor** (str): The color code to use for subtext. Defaults to `'#000000'`\n", - "- **linecolor** (str): The color code to use for lines. Defaults to `'#000000'`\n", - "- **creglinecolor** (str): The color code to use for classical register lines `'#778899'`\n", - "- **gatetextcolor** (str): The color code to use for gate text `'#000000'`\n", - "- **gatefacecolor** (str): The color code to use for gates. Defaults to `'#ffffff'`\n", - "- **barrierfacecolor** (str): The color code to use for barriers. Defaults to `'#bdbdbd'`\n", - "- **backgroundcolor** (str): The color code to use for the background. Defaults to `'#ffffff'`\n", - "- **fontsize** (int): The font size to use for text. Defaults to 13\n", - "- **subfontsize** (int): The font size to use for subtext. Defaults to 8\n", - "- **displaytext** (dict): A dictionary of the text to use for each element\n", - " type in the output visualization. The default values are:\n", - " \n", - " \n", - " 'id': 'id',\n", - " 'u0': 'U_0',\n", - " 'u1': 'U_1',\n", - " 'u2': 'U_2',\n", - " 'u3': 'U_3',\n", - " 'x': 'X',\n", - " 'y': 'Y',\n", - " 'z': 'Z',\n", - " 'h': 'H',\n", - " 's': 'S',\n", - " 'sdg': 'S^\\\\dagger',\n", - " 't': 'T',\n", - " 'tdg': 'T^\\\\dagger',\n", - " 'rx': 'R_x',\n", - " 'ry': 'R_y',\n", - " 'rz': 'R_z',\n", - " 'reset': '\\\\left|0\\\\right\\\\rangle'\n", - " \n", - " \n", - " You must specify all the necessary values if using this. There is\n", - " no provision for an incomplete dict passed in.\n", - "- **displaycolor** (dict): The color codes to use for each circuit element.\n", - " By default, all values default to the value of `gatefacecolor` and\n", - " the keys are the same as `displaytext`. Also, just like\n", - " `displaytext`, there is no provision for an incomplete dict passed\n", - " in.\n", - "- **latexdrawerstyle** (bool): When set to True, enable LaTeX mode, which will\n", - " draw gates like the `latex` output modes.\n", - "- **usepiformat** (bool): When set to True, use radians for output.\n", - "- **fold** (int): The number of circuit elements at which to fold the circuit.\n", - " Defaults to 20\n", - "- **cregbundle** (bool): If set True, bundle classical registers.\n", - "- **showindex** (bool): If set True, draw an index.\n", - "- **compress** (bool): If set True, draw a compressed circuit.\n", - "- **figwidth** (int): The maximum width (in inches) for the output figure.\n", - "- **dpi** (int): The DPI to use for the output image. Defaults to 150.\n", - "- **creglinestyle** (str): The style of line to use for classical registers.\n", - " Choices are `'solid'`, `'doublet'`, or any valid matplotlib\n", - " `linestyle` kwarg value. Defaults to `doublet`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:34.082174Z", - "start_time": "2019-08-21T09:07:34.067403Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:27.732916Z", - "iopub.status.busy": "2023-08-25T18:25:27.732477Z", - "iopub.status.idle": "2023-08-25T18:25:27.753932Z", - "shell.execute_reply": "2023-08-25T18:25:27.753342Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
            ░ ┌───┐ ░    ┌─┐                           \n",
-       "qa_0: ──────░─┤ H ├─░────┤M├───────────────────────────\n",
-       "      ┌───┐ ░ ├───┤ ░    └╥┘┌─┐                        \n",
-       "qa_1: ┤ X ├─░─┤ H ├─░─────╫─┤M├────────────────────────\n",
-       "      └───┘ ░ ├───┤ ░     ║ └╥┘┌─┐                     \n",
-       "qa_2: ──────░─┤ H ├─░─────╫──╫─┤M├─────────────────────\n",
-       "            ░ ├───┤ ░     ║  ║ └╥┘    ░ ┌─┐            \n",
-       "qb_0: ──────░─┤ H ├─■─────╫──╫──╫──X──░─┤M├────────────\n",
-       "      ┌───┐ ░ ├───┤ │     ║  ║  ║  │  ░ └╥┘┌─┐         \n",
-       "qb_1: ┤ X ├─░─┤ H ├─X─────╫──╫──╫──┼──░──╫─┤M├─────────\n",
-       "      ├───┤ ░ ├───┤ │     ║  ║  ║  │  ░  ║ └╥┘┌─┐      \n",
-       "qb_2: ┤ X ├─░─┤ H ├─X──■──╫──╫──╫──┼──░──╫──╫─┤M├──────\n",
-       "      └───┘ ░ ├───┤    │  ║  ║  ║  │  ░  ║  ║ └╥┘┌─┐   \n",
-       "qb_3: ──────░─┤ H ├────X──╫──╫──╫──■──░──╫──╫──╫─┤M├───\n",
-       "      ┌───┐ ░ ├───┤    │  ║  ║  ║  │  ░  ║  ║  ║ └╥┘┌─┐\n",
-       "qb_4: ┤ X ├─░─┤ H ├────X──╫──╫──╫──X──░──╫──╫──╫──╫─┤M├\n",
-       "      └───┘ ░ └───┘       ║  ║  ║     ░  ║  ║  ║  ║ └╥┘\n",
-       "c0: 3/════════════════════╩══╩══╩════════╬══╬══╬══╬══╬═\n",
-       "                          0  1  2        ║  ║  ║  ║  ║ \n",
-       "c1: 5/═══════════════════════════════════╩══╩══╩══╩══╩═\n",
-       "                                         0  1  2  3  4 
" - ], - "text/plain": [ - " ░ ┌───┐ ░ ┌─┐ \n", - "qa_0: ──────░─┤ H ├─░────┤M├───────────────────────────\n", - " ┌───┐ ░ ├───┤ ░ └╥┘┌─┐ \n", - "qa_1: ┤ X ├─░─┤ H ├─░─────╫─┤M├────────────────────────\n", - " └───┘ ░ ├───┤ ░ ║ └╥┘┌─┐ \n", - "qa_2: ──────░─┤ H ├─░─────╫──╫─┤M├─────────────────────\n", - " ░ ├───┤ ░ ║ ║ └╥┘ ░ ┌─┐ \n", - "qb_0: ──────░─┤ H ├─■─────╫──╫──╫──X──░─┤M├────────────\n", - " ┌───┐ ░ ├───┤ │ ║ ║ ║ │ ░ └╥┘┌─┐ \n", - "qb_1: ┤ X ├─░─┤ H ├─X─────╫──╫──╫──┼──░──╫─┤M├─────────\n", - " ├───┤ ░ ├───┤ │ ║ ║ ║ │ ░ ║ └╥┘┌─┐ \n", - "qb_2: ┤ X ├─░─┤ H ├─X──■──╫──╫──╫──┼──░──╫──╫─┤M├──────\n", - " └───┘ ░ ├───┤ │ ║ ║ ║ │ ░ ║ ║ └╥┘┌─┐ \n", - "qb_3: ──────░─┤ H ├────X──╫──╫──╫──■──░──╫──╫──╫─┤M├───\n", - " ┌───┐ ░ ├───┤ │ ║ ║ ║ │ ░ ║ ║ ║ └╥┘┌─┐\n", - "qb_4: ┤ X ├─░─┤ H ├────X──╫──╫──╫──X──░──╫──╫──╫──╫─┤M├\n", - " └───┘ ░ └───┘ ║ ║ ║ ░ ║ ║ ║ ║ └╥┘\n", - "c0: 3/════════════════════╩══╩══╩════════╬══╬══╬══╬══╬═\n", - " 0 1 2 ║ ║ ║ ║ ║ \n", - "c1: 5/═══════════════════════════════════╩══╩══╩══╩══╩═\n", - " 0 1 2 3 4 " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Set line length to 80 for above circuit\n", - "circuit.draw(output='text')" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:34.550463Z", - "start_time": "2019-08-21T09:07:34.114408Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:27.758544Z", - "iopub.status.busy": "2023-08-25T18:25:27.757115Z", - "iopub.status.idle": "2023-08-25T18:25:28.169485Z", - "shell.execute_reply": "2023-08-25T18:25:28.168569Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Change the background color in mpl\n", - "\n", - "style = {'backgroundcolor': 'lightgreen'}\n", - "\n", - "circuit.draw(output='mpl', style=style)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:34.991487Z", - "start_time": "2019-08-21T09:07:34.585528Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:28.173041Z", - "iopub.status.busy": "2023-08-25T18:25:28.172762Z", - "iopub.status.idle": "2023-08-25T18:25:28.650701Z", - "shell.execute_reply": "2023-08-25T18:25:28.650041Z" - }, - "tags": [ - "nbsphinx-thumbnail" - ] - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Scale the mpl output to 1/2 the normal size\n", - "circuit.draw(output='mpl', scale=0.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## circuit_drawer() as function\n", - "\n", - "If you have an application where you prefer to draw a circuit with a self-contained function instead of as a method of a circuit object, you can directly use the `circuit_drawer()` function, which is part of the public stable interface from `qiskit.tools.visualization`. The function behaves identically to the `circuit.draw()` method, except that it takes in a circuit object as required argument.\n", - "\n", - "
\n", - "Note: In Qiskit Terra <= 0.7, the default behavior for the circuit_drawer() function is to use the latex output backend, and in 0.6.x that includes a fallback to mpl if latex fails for any reason. Starting with release > 0.7, the default changes to the text output.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:57.321520Z", - "start_time": "2019-08-21T09:07:57.318296Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:28.655507Z", - "iopub.status.busy": "2023-08-25T18:25:28.653912Z", - "iopub.status.idle": "2023-08-25T18:25:28.659557Z", - "shell.execute_reply": "2023-08-25T18:25:28.658973Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.tools.visualization import circuit_drawer" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:07:57.752965Z", - "start_time": "2019-08-21T09:07:57.353458Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:28.664270Z", - "iopub.status.busy": "2023-08-25T18:25:28.662797Z", - "iopub.status.idle": "2023-08-25T18:25:29.107986Z", - "shell.execute_reply": "2023-08-25T18:25:29.107037Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA8MAAAKxCAYAAAB36H5IAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAACTQUlEQVR4nOzdeVxU9f7H8feZQbZRUlwCQVzREEXLpSwr166GZmZqpd3yaotm2iZ2b9db3VupafWz7WbXzFbjpl2vadtNKss0V9zLJTe2itIUUIGZ+f1BM4mAMuPAwJzX8/HgAXOW7/l8xwHnPd9zvsdwOp1OAQAAAABgIhZ/FwAAAAAAQHUjDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMJ8ncBgC85nVKh3d9VeCbYKhmGv6tAoHA6nSooKPB3GR4JDw+XwS8BAACoZoRhBJRCuzQ11d9VeGbmSCmE30T4SEFBgerWrevvMjySl5cnm83m7zIAAIDJcJo0AAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAqlRcXJxCQkL8XQYAAEApQf4uAABQ8zRr1kyXXnqpunTpok6dOqlBgwayWq06fvy49uzZow0bNmj9+vVau3at7HZ7he20bt1an3/+ubZt26Zrr71WJ0+erMZeAAAAVIwwDACQJFksFiUnJ2vChAkaMGBAhdtddtlluuWWWyRJBw8e1Ny5czVv3jz9+OOPpbZzBeHY2FjFxsZq1qxZmjRpUpX2AQAAoLJMcZp0bm6uUlJS1KZNG4WGhqpZs2aaPHmy8vPzNXbsWBmGoeeff77UPvv379esWbM0YMAAtW7dWjabTeHh4erUqZNmzJihwsJCP/UGAHyvS5cuSk9P19KlS8sNwg6HQ0VFRWWWx8XF6fHHH9fBgwf14IMPymq1SiodhCVp69at+vvf/161nQAAAPBAwIfh9PR0dezYUbNmzVJOTo7at2+voqIiPfvssxo5cqR27twpSercuXOp/f7xj38oJSVFX331laxWqxITE9WkSRNt2bJFf/7zn3XjjTf6oTeoLhk7Ptec0YY2LJ9d4TZzRhv67+xB1VgV4HtWq1WPPfaY1qxZo44dO7qXf//99/r73/+uQYMGKTo6WlarVcHBwQoLC1P37t01fvx4vf/++3I4HJKkkJAQTZ8+XatXr1b//v3LBOE+ffooNzfXL30EAAAoT0CfJp2bm6vBgwcrJydH999/vx5++GHVq1dPkvTkk09q6tSpCgoKkmEYSkpKKrXvVVddpbvuuksXXnihDMNwL1+/fr0GDRqk9957T1u3bi315hEAapOQkBC98847uvbaa93LNm7cqL/+9a/6+OOP3UH3VCdOnNC6deu0bt06vfTSS2revLnuuece3X333bJarerWrZs++ugjWSwln7UShAEAQE0V0CPDkyZNUkZGhiZOnKjZs2e7g7AkpaSkqFOnTiouLlaLFi0UERFRat+RI0fqoosuKhWEJalr167q37+/JGn79u1V3wkAqAJBQUFKTU11B+GioiL97W9/08UXX6wPP/yw3CBcngMHDujee+/VZZddpj179kiSOwjv3r2bIAwAAGqsgB0Z3rlzp1JTU9WoUSNNnz693G26dOmizZs3q1OnTmXWnTx5UkuXLtUXX3yhffv26dixY+43h64QHBwcXGqfffv2afLkyfrss88UFBSkwYMH65lnnlHDhg193DsAODePPPKIhgwZIknKz8/XkCFDtGLFCq/by83NVVhYWKllNpuN+RUAAECNFbBheOHChXI4HBo1apTq1q1b7jauN26nh+EVK1ZozJgxOnTo0BmP0apVK/fPx44dU+/evRUZGamFCxfq+PHjSklJ0aBBg7Rq1Sr3SAlql+LCAh0/xqgWAkuXLl00depUSVJhYeE5B2HXZFkxMTGSSsK1zWZT06ZN9fTTT2vcuHE+qRsAAMCXAjYMp6WlSZJ69+5d4TYZGRmSSofhb775RldffbWKioo0duxYjRo1Sh06dFCDBg0UFBSkb7/9VgkJCQoKClJCQoJ7v5dfflmZmZlauXKl4uLiJEmxsbG69NJLtXTp0lLX5KH2WLP4Ya1Z/LC/ywB8xmq1asGCBQoKKvnz/49//MMnQfjUybJuvvlmrVy5UhERERo7dqzeeecdffrppz6pHwAAwFcCNgwfOHBAktS8efNy1xcXF2vVqlWSSofhP//5zyosLNSMGTPcIyenWr58uSSpffv2CgkJcS9ftmyZevbs6Q7CktSjRw+1atVK77//vldhuGvXrsrJyfF4PzOz1gnT0Md2+6y9Dr1vV/zFw8td958Z/X1yjLbx8bIXHfdJW8DZrvVNTk5Whw4dJJVMljVjxgyvj1VeEHZdI3zfffdp3rx5kqSpU6eeMQzHx8dz9gwAAPBKVFSU1q9f79W+ARuG8/PzJUnHj5cfMlJTU5Wbm6t69eqpZcuWkiS73a6VK1dKksaMGVNmH4fDobfeekuSdOGFF5Zat2PHDg0fXjY0JSYmaseOHV71IScnR5mZmV7ta1ZBIeE+ba9+VLziOvTzaZuny8rOUvHJgio9BuAyYcIE988PPfSQiouLvWrnTEFYkubPn6+pU6cqPj5e/fr1U7t27fTdd9+V21Z2drZXNQAAAJyLgA3DUVFROnz4sDZu3KgePXqUWpedna0pU6ZIkpKSktwzRufn58tut0squX3I6Z566ilt2rRJUtkwfPjwYdWvX7/MPpGRkRW+AaxMH+AZa52ws29UwzSNbsrIMHzG4XBUGC7j4uL0hz/8QVLJfYQ//vhjr45xtiAsSU6nU//85z/19NNPS5LGjh2rlJSUctuLjo5mZBgAAHjlXDJTwIbhfv36aefOnZo5c6b69++vtm3bSpLWrVunm2++2f2mrXPnzu59IiIiFBMTo8zMTD3++ON68cUXZbVaVVhYqDlz5mjatGkKCgpScXFxmTBcFbwd7jezk8XS1FR/V+GZXbt3KyRgfxNR3fLz8yucNPDSSy91//zGG2/I6XR63H5lgrDL66+/7g7DPXv2rLDN3bt3y2azeVwLAADAuQjYj+JTUlLUsGFDHTp0SImJierYsaPi4+PVvXt3tWrVSn369JFUdibphx56SFLJhFgxMTHq1q2bmjRpooceekjPPfecnE6nDMMoFaIlqUGDBjpy5EiZOn755RdFRkZWSR8BwBNdunRx/7xmzRqP9/ckCEvSzz//7L73cKdOnWS1Wr2oGgAAoGoEbBiOjY3Vl19+qeTkZIWGhmr//v2KjIzU3LlztXz5cu3atUtS2TA8fvx4vfzyy4qPj9cvv/yirKwsDRo0SBs2bFD37t1lt9vVqlUrRURElNovISGh3GuDd+zYUWrWaQDwl1M/xNuwYYNH+3oahE8/Tnh4uPsMHQAAgJogoE/OTEhI0LJly8osz8vL0/79+2WxWNyzqp7qtttu02233VZumxWdVjho0CD95S9/UUZGhvvN4jfffKO9e/dq1qxZ59ALAPCNBg0aSJKKior0008/VXo/b4OwpFKTAJY3rwIAAIC/GE5vLhqr5b755htdcsklateunb799luftHn06FF17NhRjRo10qOPPqoTJ04oJSVFjRs31urVq5kcpprUxmuGZ44U1wzDZ850zXC7du1Uv359BQcH68svv6x0mxMnTtRzzz0nybMgLJUE6aioKB0/flzfffede6b/U+Xl5XHNMAAAqHamfAu+detWSWVPkT4XERERSktL0+TJk3XDDTcoKChIgwYN0jPPPEMQBlAjeDuz/fPPP6969erpxhtv9CgIS9LevXu1d+9er44LAABQlQjDPtS6detyT8sGgNpu+vTpeuaZZ8q97RwAAEBtZMohy6oKwwAQyAjCAAAgkJhyZDgtLc3fJQAAAAAA/MiUI8MAAAAAAHMjDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcIwAAAAAMB0CMMAAAAAANMhDAMAAAAATIcwDAAAAAAwHcPpdDr9XQTgK06nVGj3dxWeCbZKhuHvKhAonE6nCgoKfNberLnv6Gh+gSJs4Zpyxw1lHvtCeHi4DH4JAABANQvydwGALxmGFMKrGiZmGIZsNpvP2gsOCVVwkV3BIaGy2WxlHgMAANRWnCYNAAAAADAdwjAAAAAAwHQIwwAAAAAA0yEMAwAAAABMhzAMAAAAADAdwjAAAAAAwHQIwwAAAAAA0yEMAwAAAABMhzAMAAAAADAdwjAAAAAAwHQIwwAAAAAA0yEMAwAAAABMhzAMAAAAADAdwjAAAAAAwHQIwwAAAAAA0yEMAwAAAABMhzAMAAAAADCdIH8XAN9yOp0qcNj9XYZHwi1WGYbhk7acTqmwdnVfwVbJR90HoN/+DhYU+LsMj4SHh/vs7yAAAKgcwnCAKXDY1SDtf/4uwyOH+/SXzeqbl2KhXZqa6pOmqs3MkVIIv4mAzxQUFKhu3br+LsMjeXl5stls/i4DAABT4TRpAAAAAIDpEIYBAAAAAKZDGAYAAAAAmA5hGAAAAABgOoRhAAAAAIDpEIYBAAAAAKZDGAYAAAAAmA5hGAAAAABgOoRhAAAAAIDpEIYBAAAAAKZDGAYAAAAAmE6QvwsAAKCmaty4sbp06aJmzZopJCRERUVF+vHHH7Vx40YdOHCgUm0kJyfrtttu08iRI3Xy5MkqrhgAAFQWYRgAgFN07NhR48ePV3JysuLi4ircLjc3V2lpaXrppZf02WeflbtNcnKyFi9erJCQEC1ZskTXXnstgRgAgBrCFKdJ5+bmKiUlRW3atFFoaKiaNWumyZMnKz8/X2PHjpVhGHr++edL7bN//37NmjVLAwYMUOvWrWWz2RQeHq5OnTppxowZKiws9FNvAABVoUePHvryyy+1ZcsWjR8//oxBWJIaNWqkESNGKC0tTTt27NCIESNKrT81CEvS4cOHVVxcXGX1AwAAzwT8yHB6eroGDhyonJwc2Ww2tW/fXllZWXr22We1d+9e/fLLL5Kkzp07l9rvH//4h+bPny+bzaamTZsqMTFRP/74o7Zs2aItW7Zo3bp1Wrx4sR96VD3s8xfI8c6/Zb3vHlkGXFVqndPplH3Kg3Lu3Kmg55+V0bKFf4qsQhk7PtfiJ3qr542z1CX5gXK3mTPaUIvOyRrywLJqrg6AL4WGhuqxxx7TvffeK4vl98+I8/PztW7dOm3YsEHffvutjh8/rjp16qhVq1bq0qWLLr74YjVs2FCSlJCQoNTUVI0YMUITJkxQt27dSgXhhQsX6uabb5bdbvdLHwEAQFkBHYZzc3M1ePBg5eTk6P7779fDDz+sevXqSZKefPJJTZ06VUFBQTIMQ0lJSaX2veqqq3TXXXfpwgsvlGEY7uXr16/XoEGD9N5772nr1q3q2LFjtfapulhuHiXHmm9kn/svGV0uktG4kXud470lcm7ZKsufbg3IIAzAPKKjo/Xxxx+X+lu+Y8cOPf/883rjjTeUl5dX4b5BQUG69tprNXHiRF155ZWSpGHDhqlv374KDw9XcHCwJIIwAAA1VUCfJj1p0iRlZGRo4sSJmj17tjsIS1JKSoo6deqk4uJitWjRQhEREaX2HTlypC666KJSQViSunbtqv79+0uStm/fXvWd8BOjTh0FTblfOnFC9qf/z73ceShDjgWvy7ignSzDh/mvQAA4R9HR0Vq5cqU7CJ84cUJTpkxRx44d9c9//vOMQViSiouLtWjRIvXq1UvDhw/XTz/9JEmqX78+QRgAgFogYEeGd+7cqdTUVDVq1EjTp08vd5suXbpo8+bN6tSpU5l1J0+e1NKlS/XFF19o3759OnbsmBwOh6TfQ7DrzY4kZWRkaMaMGVq7dq02b96swsJCOZ3OKuhZ9THi28hywwg53loox/IPZQy4SvYnZ0tOp6xT7pdhtfq7RADwSlhYmD7++GO1adNGkvT9998rOTlZ3377rVftLVq0SHXq1NGbb77pPtX65MmTmjZtGkEYAIAaKmDD8MKFC+VwODRq1CjVrVu33G3CwsIkqUwYXrFihcaMGaNDhw6d8RitWrVy/7xnzx4tXrxY3bp1U3BwsFatWnWOPagZLKNulGP1N7L/a54se7+X87tdstw+TkazWH+XVi2KCwt0/Fiuv8sA4GOPPfaYe0T4+++/1+WXX66srCyv20tOTtarr75a6prjkJAQvfzyy+rXr1+t/3AUAIBAFLBhOC0tTZLUu3fvCrfJyMiQVDoMf/PNN7r66qtVVFSksWPHatSoUerQoYMaNGigoKAgffvtt0pISFBQUJASEhLc+11xxRXKzs6WJD3yyCMBE4aNoCAFTblPxXffI8ey5TI6JMpy3bX+LqvarFn8sNYsftjfZQDwocsuu0z33HOPpJJTo5OTk885CJ86WdaiRYvUrVs3NW/eXH369NEdd9yhl156yRelAwAAHwrYMHzgwAFJUvPmzctdX1xc7A6sp4bhP//5zyosLNSMGTM0derUMvstX75cktS+fXv3Gx9JpUYDfKVr167KycnxaB9ncLA09wXfFmKzSXXqSMXFMrp1leHjvraNbyvDR7eqstYJ09DHdvukLUnq0Pt2xV88vNx1/5nR3yfHaBsfL3vRcZ+0Bfja0DH3yFY3Qtk52YqNjS3zuCZyXdJSkZkzZ7r/Zv/1r3/1+tRoqWwQdl0j3Lt3b/3vf/+TJD3++ONasGCBTpw4UWE78fHxVfL/CAAAgS4qKkrr16/3at+ADcP5+fmSpOPHyw8Zqampys3NVb169dSyZUtJkt1u18qVKyVJY8aMKbOPw+HQW2+9JUm68MILq6LsUnJycpSZmenZTqEhquPDGpxOp+xPPSMVF0lxzeR4+x1ZrrxCRtNonx0jKztLOnHSJ20FhYT7pB2X+lHxiuvQz6dtni4rO0vFJwuq9BiAtxy/Xe/qsNuVmZlZ5nFt06lTJ1122WWSSuZ/eOaZZ7xuq6IgbLfb9emnn2rhwoW68cYbFRkZqREjRuj111+vsC3XmUUAAKD6BGwYjoqK0uHDh7Vx40b16NGj1Lrs7GxNmTJFkpSUlOSeMTo/P9890Ul5n+A/9dRT2rRpk6TqCcNRUVEe7+MMDtZPPqzBsWSpnJu3yDLmFll6XKLiu+6W/alnZJ09s8xM295qGt3UpyPDtU3T6KaMDKPGsvw2UZ7FalVMTEyZxzWRw+GoMFyOHz/e/fPzzz9/1lHkipwpCLs8++yzuvHGGyVJd9111xnDcHR0NCPDAAB4wZvM5BKwYbhfv37auXOnZs6cqf79+6tt27aSpHXr1unmm29Wbm7JpEidO3d27xMREaGYmBhlZmbq8ccf14svviir1arCwkLNmTNH06ZNU1BQkIqLi6slDHsz3J9vL1aDtP/55PjOzEw55i+Q0a6tLCOul2G1yjJ6lByvvibHkqWyDh3ik+Ps2r1LNqtvXooni6WpqT5pqtrs2r1bIQH7m4ja7okX3tLRvHxFR0UrIyOjzOOaKD8/v8KJE5OTkyVJeXl5evPNN71qvzJBWJLWrFmj9PR0de7cWd27d1ejRo3c//ecbvfu3bLZbF7VAwAAvBOwH0OnpKSoYcOGOnTokBITE9WxY0fFx8ere/fuatWqlfr06SOp7EzSDz30kCTp5ZdfVkxMjLp166YmTZrooYce0nPPPSen0ynDMEqF6EDkdDhkn/W05HDIOuU+922ULCOul9E2Xo75C+TM4rQ+ALXH+eef777Oee3atWe9j3B5KhuEXVyTOUolt/MDAAA1R8CG4djYWH355ZdKTk5WaGio9u/fr8jISM2dO1fLly/Xrl27JJUNw+PHj9fLL7+s+Ph4/fLLL8rKytKgQYO0YcMGde/eXXa7Xa1atVJERIQ/ulVtHIvek3PHTlluGS0jLs693LBaZX3gPslhl/2pZ7hdCIBa49QwumHDBo/39zQIn34cwjAAADVLQJ+cmZCQoGXLlpVZnpeXp/3798tisahDhw5l1t9222267bbbym3TDOHPefCgHK+9ISPhAlmGXVdmvdGieZWcLg0AValZs2bun3fu3OnRvt4E4dOPE3fKB4sAAMD/AjoMV2T79u1yOp1q27atwsN9N/vwokWLJEk7duwo9bhFixbq2rWrz45T1Yy4ONVZ/t8zbmO9caSsN46spoqqX2z7Xpr85pk/+DjbegA1y86dO/XPf/5TYWFhHt1OqVWrVl4FYankrgCvvfaaTpw44b5bAQAAqBlMGYa3bt0qqewp0udq+PDh5T6+5ZZbtGDBAp8eCwDgmZUrV3oVSL///ns9+uijeuKJJzwKwlLJ3QtuvfVWj48JAACqHmHYh8xwCjUAmNH06dO1Y8cOLVu2rNJBGAAA1GyEYQAAKuG//z3z5SMAAKB2MWUYPvVWFwAAAAAA8wnYWysBAAAAAFARwjAAAAAAwHQIwwAAAAAA0yEMAwAAAABMhzAMAAAAADAdwjAAAAAAwHQIwwAAAAAA0yEMAwAAAABMhzAMAAAAADAdwjAAAAAAwHQIwwAAAAAA0wnydwHwrXCLVYf79Pd3GR4Jt1h91lawVZo50mfNVYtg33UfgKTw8HDl5eX5rL1Zc9/R0fwCRdjCNeWOG8o89oXw8HCftAMAACqPMBxgDMOQzWref1bDkELM230A+u3voM3ms/aCQ0IVXGRXcEiobDZbmccAAKB24jRpAAAAAIDpEIYBAAAAAKZDGAYAAAAAmA5hGAAAAABgOoRhAAAAAIDpEIYBAAAAAKZDGAYAAAAAmA5hGAAAAABgOoRhAAAAAIDpEIYBAAAAAKZDGAYAAAAAmA5hGAAAAABgOoRhAAAAAIDpEIYBAAAAAKZDGAYAAAAAmA5hGAAAAABgOoRhAAAAAIDpEIYBAAAAAKYT5O8CAF9yOqVCu7+r8EywVTIMf1cBIFA4nU4VFBT4uwyPhIeHy+APIQCgmhGGEVAK7dLUVH9X4ZmZI6UQfhMB+EhBQYHq1q3r7zI8kpeXJ5vN5u8yAAAmw2nSAAAAAADTIQwDAAAAAEyHMAwAAAAAMB3CMAAAAADAdAjDAAAAAADTIQwDAAAAAEyHMAwAAAAAMB3CMAAAAADAdAjDAAAAAADTIQwDAAAAAEyHMAwAAM7IMAyFhYUpNDRUhmF4vP/o0aMVEhJSBZUBAOA9wjAAACilcePGmjBhghYsWKCtW7eqqKhIBQUFOn78uAoLC7V582bNnz9fd9xxhxo0aHDGth555BG98cYbWrJkCYEYAFCjEIYBAIAkqVu3bnrjjTd06NAhvfDCC7rlllvUoUMHWa1W9zZBQUFKSkrSmDFj9NJLLykzM1Pz589X586dy7T3yCOP6OGHH5YkDRgwQAMHDqyurgAAcFamCMO5ublKSUlRmzZtFBoaqmbNmmny5MnKz8/X2LFjZRiGnn/++VL77N+/X7NmzdKAAQPUunVr2Ww2hYeHq1OnTpoxY4YKCwv91BtUh4wdn2vOaEMbls+ucJs5ow39d/agaqwKAKqGzWbT888/r7Vr15Y5pbmoqEibN29WWlqa0tLStHXrVhUXF7vXh4WFacyYMdqwYYNmz56tsLAwSaWDsCRNmjRJS5YsqbY+AQBwNkH+LqCqpaena+DAgcrJyZHNZlP79u2VlZWlZ599Vnv37tUvv/wiSWU+0f7HP/6h+fPny2azqWnTpkpMTNSPP/6oLVu2aMuWLVq3bp0WL17shx4BAOA73bp1U2pqqlq2bOle9vPPP+uVV17RokWLtGXLFp08ebLUPqGhoercubNGjBihMWPGqH79+rJYLLr//vs1ePBgpaWl6c4773RvP2nSJD333HPV1icAACojoEeGc3NzNXjwYOXk5Oj+++9Xdna2Nm7cqJycHM2cOVPLly/XunXrZBiGkpKSSu171VVXacOGDTp27Jh27dqltWvXav/+/Vq3bp3OP/98vffee9q6daufegYAwLnr1auX0tLS3EE4Pz9f99xzj2JiYjR16lStW7euTBCWpBMnTmjNmjW677773NueOHFCktS2bVuCMACgVgjoMDxp0iRlZGRo4sSJmj17turVq+del5KSok6dOqm4uFgtWrRQREREqX1Hjhypiy66qMysmV27dlX//v0lSdu3b6/6TgAAUAW6deum999/X3Xr1pUkrVq1SklJSZozZ065AbgiBQUFevLJJ9W5c2dlZWWVWjdjxgyCMACgxgrY06R37typ1NRUNWrUSNOnTy93my5dumjz5s3q1KlTmXUnT57U0qVL9cUXX2jfvn06duyYHA6HpN9DcHBwsHv7RYsWaeHChVq/fr1++uknxcXFadiwYfrzn//sfqOB2qe4sEDHj+X6uwwA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jHQp51up83VSULBPywMAAH7SvHlzdenSRRdccIHCwsLkdDp19OhRbdmyRRs2bNDPP/981jbat2+vxYsXa/To0dqwYUM1VI3KIgxXID09XZKUlJTk30IAVKljP0qbFknFJ6WN/5YuGuF5IM5Il779tOTn9MXShcMlK39dAQColS644AJNmDBBN9xwgxo3bnzGbXfv3q358+frlVde0U8//VRmffv27fXZZ5+pSZMm+t///qdevXppy5YtVVU6PMR4fQUIw4A5hEZIYfVLfi4sKAnEebmV3//UICxJ9WMki9WXFQLwN6dTktP1wJ+VAKhK7dq10yeffKKdO3fq7rvvPmsQlqT4+HhNnz5dGRkZevnll1W/fn33ulODsCTt2bNHBw4cqKry4QXGLiqwefNmSVKnTp38XAn8IWPH51r8RG/1vHGWuiQ/UO42c0YbatE5WUMeWFbN1cGX6oRKFw2XNr4rHfvh90BcmRHi04Nwi+5S68slw6jSkgFUk+KTUvb2kt/1U21aJMV2lhq1kgyGFYBaz2Kx6L777tM//vEPhYaGupcXFBToiy++0Pr167V582YdOXJEhmEoKipKF110kbp3764ePXrIYrEoODhYt912m5KTk3X77bdr3759pYLwunXr1L9/f/3666/+6ibKYYo/4bm5uUpJSVGbNm0UGhqqZs2aafLkycrPz9fYsWNlGIaef/75UvucOjK8evVqDR06VI0bN1Z4eLguvfRSffLJJ37oCYCq4ArE9c4veVyZEWKCMBDYfjkoffWy9F2alP9L6XU/75c2L5HWviWdzPNHdQB8JSwsTO+//75mzZrlDsLff/+9Jk+erJiYGF199dX629/+psWLF2vFihX69NNP9eabb+q+++5Tz5491aZNG82cOdMdcps2baply5Zp7dq1BOFaIODDcHp6ujp27KhZs2YpJydH7du3V1FRkZ599lmNHDlSO3fulCR17tzZvU9eXp727t0rq9WqFStWqGfPnvr666/dE2mtXr1aAwcO1NKlS/3RJQBVwJNATBAGAtvhQ9KmxSUjw2dy7AdpQ2rJ3wsAtU9oaKiWLVumq6++WpLkcDj01FNPKTExUc8++6yOHDly1jb27dunBx98UImJifrggw/cy202mySCcE0X0GE4NzdXgwcPVk5Oju6//35lZ2dr48aNysnJ0cyZM7V8+XKt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p9Po0gyxc+dOBQQEKCUlxevXtnn9ivCo5ORk7d27t9u24cOHG1QNAADA1R04cEBlZWX68Y9/rNdee0379u1TRUWFQkNDVVFRYXR5XtHU1KS0tDTdfvvteuCBB4wux+t27typJUuWKCsrS6GhoVq3bp1KS0v19ttvKzc31+jyrrmYmBg99thjGjt2rIKDg/Xuu+/qBz/4gQICAnT//fcbXZ5XnT59WnfddZdmz56tjz/+2OvXJwz7uICAAI0cOdLoMgAAAFyyevVqZWVl6emnn5Ykpaam6vDhw1q5cqVpwvDcuXM1d+5cSdIjjzxicDXet23btm6vV61aperqalVWVpoiDBcVFXV7PWbMGG3evFm7du0yVRh2OBy64447dN9996mlpcWQMMxt0j6uvr5eCQkJSkhI0Jw5c7Rnzx6jSwIAALii3bt3q7i4uNu24uJiffbZZ6qvrzeoKhjJ4XCosbFRYWFhRpfidU6nU/v379fu3btVWFhodDletXz5clksFkN/IURn2IdNnjxZGzZsUFpamhobG7VmzRrl5eWpurpas2bNMro8AADgQz7/skHnzjf12N7e0dH150fH63u87mSRdMOoOFmtV++12O32Hne1db622+1KSEgYyDDc1trapk9Pnumx3dXxS9KI66MUGTH02hd7jfzfqbNq+bq127bexnul78GQ4CAlxcX0+7orVqxQQ0OD7r33XndL94jzjU0680VDj+39+Qwkx49QUFBg39c6f17x8fFqbW2Vw+HQE088oWXLlg18EAPgcDj0yWen9M2Zy/0Zf3TkUA2/LqrPa9XU1OiFF15QbW2tLBaLJ8p3C2HYh3XeXtMpLy9P9fX1euaZZwjDAACgXyyyaOOf3lRbW3uv+5svtGj9pqorvp46MV03jjYmyHpCYKBN7/3vB/ro+Ile9/c1/sjwMN2/ZME1r/Naajj/lV7d+lav+7453t623V42s99h+Le//a1WrFihrVu3GvaLkE6BQYH6r6q31dj091739/UZGDsmUTcmx7t0rfDwcNXV1am5uVl79uzRT37yE8XFxemee+4Z2CAGwGq16sixz7T34OFe9/c1/qBAm5bdPb/P6/ztb3/THXfcoQ0bNhg+3ZPbpP1MTk6OPv30U6PLAAAAPmbYdZEqKcx269zh10WpOH+KS8fGxsbq9OnT3badOXOma59RLBaL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", - "text/plain": [ - "
" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "circuit_drawer(circuit, output='mpl', plot_barriers=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:08:30.149127Z", - "start_time": "2019-08-21T09:08:30.140718Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:29.111707Z", - "iopub.status.busy": "2023-08-25T18:25:29.111437Z", - "iopub.status.idle": "2023-08-25T18:25:29.228676Z", - "shell.execute_reply": "2023-08-25T18:25:29.228045Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:29 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "444d0ea94a774f55a250f5d894a160ac": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "707a5283aaac4d47bf9e48b0d39382b0": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_ee2513f813ad430495c78238406e21d5", - "placeholder": "​", - "style": "IPY_MODEL_444d0ea94a774f55a250f5d894a160ac", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "ee2513f813ad430495c78238406e21d5": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/tutorials/circuits_advanced/04_transpiler_passes_and_passmanager.ipynb b/docs/tutorials/circuits_advanced/04_transpiler_passes_and_passmanager.ipynb deleted file mode 100644 index 306a24de2037..000000000000 --- a/docs/tutorials/circuits_advanced/04_transpiler_passes_and_passmanager.ipynb +++ /dev/null @@ -1,6957 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Transpiler Passes and Pass Manager" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Introduction" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A central component of Qiskit Terra is the transpiler, which is designed for modularity and extensibility. The goal is to be able to easily write new circuit transformations (known as transpiler **passes**), and combine them with other existing passes. Which passes are chained together and in which order has a major effect on the final outcome. This pipeline is determined by a **pass manager**, which schedules the passes and also allows passes to communicate with each other by providing a shared space. In this way, the transpiler opens up the door for research into aggressive optimization of quantum circuits.\n", - "\n", - "In this notebook, we look at the built-in passes, how to use the pass manager, and develop a simple custom transpiler pass. In order to do the latter, we first need to introduce the internal representation of quantum circuits in Qiskit, in the form of a Directed Acyclic Graph, or **DAG**. Then, we illustrate a simple swap mapper pass, which transforms an input circuit to be compatible with a limited-connectivity quantum device.\n", - "\n", - "***Before you start***: You may need to install the `pydot` library and the `graphviz` library for the DAG plotting routines. If you are using Anaconda Python, you can install both with the `conda` command. If you use your system's native Python interpreter, install `pydot` using the `pip` command, and install `graphviz` using your system's native package manager (e.g. `yum`, `apt`, `dnf`, `brew`, etc.)." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:56.857930Z", - "start_time": "2019-12-10T21:47:54.444353Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:30.918072Z", - "iopub.status.busy": "2023-08-25T18:25:30.917826Z", - "iopub.status.idle": "2023-08-25T18:25:31.520463Z", - "shell.execute_reply": "2023-08-25T18:25:31.519613Z" - } - }, - "outputs": [], - "source": [ - "from qiskit import QuantumCircuit\n", - "from qiskit.compiler import transpile\n", - "from qiskit.transpiler import PassManager" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-21T09:12:12.822442Z", - "start_time": "2019-08-21T09:12:12.819902Z" - } - }, - "source": [ - "## PassManager object\n", - "\n", - "Lets you specify the set of passes you want." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:57.268332Z", - "start_time": "2019-12-10T21:47:56.860709Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:31.526622Z", - "iopub.status.busy": "2023-08-25T18:25:31.525089Z", - "iopub.status.idle": "2023-08-25T18:25:32.402259Z", - "shell.execute_reply": "2023-08-25T18:25:32.401532Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "circ = QuantumCircuit(3)\n", - "circ.ccx(0, 1, 2)\n", - "circ.draw(output='mpl')" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:57.533035Z", - "start_time": "2019-12-10T21:47:57.270693Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:32.407275Z", - "iopub.status.busy": "2023-08-25T18:25:32.405984Z", - "iopub.status.idle": "2023-08-25T18:25:32.807173Z", - "shell.execute_reply": "2023-08-25T18:25:32.806412Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAzQAAADuCAYAAADvP0KjAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAA1rElEQVR4nO3deXxV9b3v/9fOPJAACUMiCQRCmOcgg6KIBYcqDm1BK9Va9arnysGeeoTb2nPUnqtWD4/+/KltxXNora0DxeFQwQEURETAIDMhEIYAmSAhEJKQkGHv+8eSIZCQvXf2Xmuvlffz8cgDkjV94Pv9rqzPXt/B5fF4PIiIiIiIiNhQmNUBiIiIiIiI+EsJjYiIiIiI2JYSGhERERERsS0lNCIiIiIiYltKaERERERExLaU0IiIiIiIiG0poREREREREdtSQiMiIiIiIralhEZERERERGxLCY2IiIiIiNiWEhoREREREbEtJTQiIiIiImJbSmhERERERMS2lNCIiIiIiIhtKaERERERERHbUkIjIiIiIiK2pYRGRERERERsSwmNiIiIiIjYlhIaERERERGxLSU0IiIiIiJiW0poRERERETEtpTQiIiIiIiIbSmhERERERER21JCIyIiIiIitqWERkREREREbEsJjYiIiIiI2JYSGhERERERsS0lNCIiIiIiYltKaERERERExLaU0IiIiIiIiG0poREREREREdtSQiMiIiIiIralhEZERERERGxLCY2IiIiIiNiWEhoREREREbEtJTQiIiIiImJbSmhERERERMS2IqwOQERERETkQh6PBxqarA7DN5HhuFwuq6PocJTQiIiIiEjoaWii8YnFVkfhk4hnZkCUHq/Npi5nIiIiIiJiW0poRERERETEtpTQiIiIiIiIbSmhERERERER21JCIyIiIiIitqWERkREREREbEsJjYiIiIiI2JYSGhERERFxjDd2rCZq/l28sWN1i9sLKsuImn8X93/8qsmRSbAooREREREREdtSQiMiIiIiIralhEZERERERGxLCY2IiIiIiNiWEhoREREREbGtCKsDMEN5eTkvvPAC77//PoWFhXTv3p0f/OAHPPvss8yZM4c//elPvPzyy8yePdvqUEUkCBqbYNthyC2C2gaIioD0JBjXDzrFWB2diJihvArW74Oyk9DkgYQYGN0HsnqCy2V1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Fbhc0CshCYAdZYf45YTbyCndx6DkXnSKMhZieWjUtGaJy9R3/oN/zr6BW7MutyRu8Z8SGhERERFxjC1HC86+nQHoHB3Pq1tWkBybwC39x1oXmASNEhoRERERcYybMsdwU+aYs9+vu/v/AjDyz4+zYuavWz3uszv/LeixSXAooRERERERx9v6s/+0OgQJEs1yJiIiIiIitqWERkREREREbEsJjYiIiIiI2JbG0IiIiIhI6IkMJ+KZGVZH4ZvIcKsj6JCU0IiIiIhIyHG5XBClR1Vpm7qciYiIiIiIbSmhERERERER21JCIyIiIiIitqWERkREREREbEsJjYiIiIiI2JYSGhERERERsS0lNCIiIiIiYltKaERERERExLaU0IiIiIiIiG0poREREREREdtSQiMiIiIiIralhEZERERERGxLCY2IiIiIiNiWEhoREREREbEtJTQiIiIiImJbSmhERERERMS2lNCIiIiIiIhtRVgdgIiIiFzM4/FAQ5PVYfgmMhyXy2V1FOIQagPiLSU0IiIioaihicYnFlsdhU8inpkBUXq0kABRGxAvqcuZiIiIiIjYlhIaERERERGxLSU0IiIiIiJiW0poRERERETEtpTQiIiIiHQQTW7weKyOQqzi8ZwrfyfVA03DICIiIuJAHg8UlMOuYiisgEMVUF13bvvJWvjj55CWBANTIasnaMZhZ2lyw84i2HcUDh+DouNwutHYdrIOnv4A0pMhPQmGpUFqF0vD9ZsSGhEREREHaWiCb/bD2j1QfKL1/TzA7lLj6/Nc6J4AVw6ACZkQE2lWtBIMVbXwVT6s22skrq05fsr42nYYlm2FzB5wZRaM6gNhNkpuldCIiIiIOMTBcnhrHRw56fuxZVXwP9/C6jz48QQYkBL4+CS4PB74tgDe3win6n0/ft9R4+urPfDjiUaSawcaQyMiIuIgb+xYTdT8u3hjx+oWtxdUlhE1/y7u//hVkyOTYHJ74KOt8OJy/5KZ8x2vgT98Du/mGF2W7KajtoG6Blj4Jfzta/+SmfPtL4MXlhlveOxAb2hEREREbMztgXfWG93MAumrPXDiFNw7CSLCA3tuCaya0/DqSjhcEbhzNjTBog3GuKtpwwJ33mDQGxoRERERm/J44L2cwCczZ+wohL9+bSRNEppON8CCVYFNZs63bCt8kReccweKEhoRERERm9p0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", - "text/plain": [ - "
" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.transpiler.passes import Unroller\n", - "pass_ = Unroller(['u1', 'u2', 'u3', 'cx'])\n", - "pm = PassManager(pass_)\n", - "new_circ = pm.run(circ)\n", - "new_circ.draw(output='mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All of Qiskit's transpiler passes are accessible from ``qiskit.transpiler.passes``." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:57.539422Z", - "start_time": "2019-12-10T21:47:57.535048Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:32.812347Z", - "iopub.status.busy": "2023-08-25T18:25:32.811101Z", - "iopub.status.idle": "2023-08-25T18:25:32.827192Z", - "shell.execute_reply": "2023-08-25T18:25:32.826554Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['ALAPSchedule',\n", - " 'ALAPScheduleAnalysis',\n", - " 'ASAPSchedule',\n", - " 'ASAPScheduleAnalysis',\n", - " 'AlignMeasures',\n", - " 'ApplyLayout',\n", - " 'BIPMapping',\n", - " 'BarrierBeforeFinalMeasurements',\n", - " 'BasicSwap',\n", - " 'BasisTranslator',\n", - " 'CSPLayout',\n", - " 'CXCancellation',\n", - " 'CXDirection',\n", - " 'CheckCXDirection',\n", - " 'CheckGateDirection',\n", - " 'CheckMap',\n", - " 'Collect1qRuns',\n", - " 'Collect2qBlocks',\n", - " 'CollectCliffords',\n", - " 'CollectLinearFunctions',\n", - " 'CollectMultiQBlocks',\n", - " 'CommutationAnalysis',\n", - " 'CommutativeCancellation',\n", - " 'CommutativeInverseCancellation',\n", - " 'Commuting2qGateRouter',\n", - " 'ConsolidateBlocks',\n", - " 'ConstrainedReschedule',\n", - " 'ContainsInstruction',\n", - " 'ConvertConditionsToIfOps',\n", - " 'CountOps',\n", - " 'CountOpsLongestPath',\n", - " 'CrosstalkAdaptiveSchedule',\n", - " 'DAGFixedPoint',\n", - " 'DAGLongestPath',\n", - " 'Decompose',\n", - " 'DenseLayout',\n", - " 'Depth',\n", - " 'DynamicalDecoupling',\n", - " 'EchoRZXWeylDecomposition',\n", - " 'EnlargeWithAncilla',\n", - " 'Error',\n", - " 'FixedPoint',\n", - " 'FullAncillaAllocation',\n", - " 'GateDirection',\n", - " 'GatesInBasis',\n", - " 'HighLevelSynthesis',\n", - " 'HoareOptimizer',\n", - " 'InstructionDurationCheck',\n", - " 'InverseCancellation',\n", - " 'Layout2qDistance',\n", - " 'LayoutTransformation',\n", - " 'LinearFunctionsSynthesis',\n", - " 'LinearFunctionsToPermutations',\n", - " 'LookaheadSwap',\n", - " 'MergeAdjacentBarriers',\n", - " 'MinimumPoint',\n", - " 'NoiseAdaptiveLayout',\n", - " 'NumTensorFactors',\n", - " 'Optimize1qGates',\n", - " 'Optimize1qGatesDecomposition',\n", - " 'Optimize1qGatesSimpleCommutation',\n", - " 'OptimizeCliffords',\n", - " 'OptimizeSwapBeforeMeasure',\n", - " 'PadDelay',\n", - " 'PadDynamicalDecoupling',\n", - " 'PulseGates',\n", - " 'RZXCalibrationBuilder',\n", - " 'RZXCalibrationBuilderNoEcho',\n", - " 'RemoveBarriers',\n", - " 'RemoveDiagonalGatesBeforeMeasure',\n", - " 'RemoveFinalMeasurements',\n", - " 'RemoveResetInZeroState',\n", - " 'ResetAfterMeasureSimplification',\n", - " 'ResourceEstimation',\n", - " 'SabreLayout',\n", - " 'SabreSwap',\n", - " 'SetIOLatency',\n", - " 'SetLayout',\n", - " 'Size',\n", - " 'SolovayKitaev',\n", - " 'SolovayKitaevSynthesis',\n", - " 'StochasticSwap',\n", - " 'TemplateOptimization',\n", - " 'TimeUnitConversion',\n", - " 'TranslateParameterizedGates',\n", - " 'TrivialLayout',\n", - " 'UnitarySynthesis',\n", - " 'Unroll3qOrMore',\n", - " 'UnrollCustomDefinitions',\n", - " 'UnrollForLoops',\n", - " 'Unroller',\n", - " 'VF2Layout',\n", - " 'VF2PostLayout',\n", - " 'ValidatePulseGates',\n", - " 'Width']" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.transpiler import passes\n", - "[pass_ for pass_ in dir(passes) if pass_[0].isupper()]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Different Variants of the Same Pass\n", - "\n", - "There can be passes that do the same job, but in different ways. For example, the ``TrivialLayout``, ``DenseLayout`` and ``NoiseAdaptiveLayout`` all choose a layout (binding of virtual qubits to physical qubits), but use different algorithms and objectives. Similarly, the ``BasicSwap``, ``LookaheadSwap`` and ``StochasticSwap`` all insert swaps to make the circuit compatible with the coupling map. The modularity of the transpiler allows plug-and-play replacements for each pass.\n", - "\n", - "Below, we show the swapper passes all applied to the same circuit, to transform it to match a linear chain topology. You can see differences in performance, where the ``StochasticSwap`` is clearly the best. However, this can vary depending on the input circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:57.681468Z", - "start_time": "2019-12-10T21:47:57.541513Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:32.831761Z", - "iopub.status.busy": "2023-08-25T18:25:32.830596Z", - "iopub.status.idle": "2023-08-25T18:25:32.951486Z", - "shell.execute_reply": "2023-08-25T18:25:32.950648Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.transpiler import CouplingMap, Layout\n", - "from qiskit.transpiler.passes import BasicSwap, LookaheadSwap, StochasticSwap\n", - "\n", - "coupling = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6]]\n", - "\n", - "circuit = QuantumCircuit(7)\n", - "circuit.h(3)\n", - "circuit.cx(0, 6)\n", - "circuit.cx(6, 0)\n", - "circuit.cx(0, 1)\n", - "circuit.cx(3, 1)\n", - "circuit.cx(3, 0)\n", - "\n", - "coupling_map = CouplingMap(couplinglist=coupling)\n", - "\n", - "bs = BasicSwap(coupling_map=coupling_map)\n", - "pass_manager = PassManager(bs)\n", - "basic_circ = pass_manager.run(circuit)\n", - "\n", - "ls = LookaheadSwap(coupling_map=coupling_map)\n", - "pass_manager = PassManager(ls)\n", - "lookahead_circ = pass_manager.run(circuit)\n", - "\n", - "ss = StochasticSwap(coupling_map=coupling_map)\n", - "pass_manager = PassManager(ss)\n", - "stochastic_circ = pass_manager.run(circuit)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:57.902461Z", - "start_time": "2019-12-10T21:47:57.682997Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:32.956196Z", - "iopub.status.busy": "2023-08-25T18:25:32.955645Z", - "iopub.status.idle": "2023-08-25T18:25:33.283035Z", - "shell.execute_reply": "2023-08-25T18:25:33.282166Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "circuit.draw(output='mpl')" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:58.238179Z", - "start_time": "2019-12-10T21:47:57.904473Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:33.286999Z", - "iopub.status.busy": "2023-08-25T18:25:33.286654Z", - "iopub.status.idle": "2023-08-25T18:25:33.723644Z", - "shell.execute_reply": "2023-08-25T18:25:33.722762Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "basic_circ.draw(output='mpl')" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:58.643611Z", - "start_time": "2019-12-10T21:47:58.241545Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:33.727109Z", - "iopub.status.busy": "2023-08-25T18:25:33.726836Z", - "iopub.status.idle": "2023-08-25T18:25:34.157478Z", - "shell.execute_reply": "2023-08-25T18:25:34.155557Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "lookahead_circ.draw(output='mpl')" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:58.935337Z", - "start_time": "2019-12-10T21:47:58.646318Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:34.162468Z", - "iopub.status.busy": "2023-08-25T18:25:34.161164Z", - "iopub.status.idle": "2023-08-25T18:25:34.537478Z", - "shell.execute_reply": "2023-08-25T18:25:34.536630Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "stochastic_circ.draw(output='mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Preset Pass Managers\n", - "\n", - "Qiskit comes with several pre-defined pass managers, corresponding to various levels of optimization achieved through different pipelines of passes. Currently ``optimization_level`` 0 through 3 are supported; the higher the number, the more optimized it is, at the expense of more time. Choosing a good pass manager may take trial and error, as it depends heavily on the circuit being transpiled and the backend being targeted.\n", - "\n", - "Here we illustrate the different levels by looking at a state synthesis circuit. We initialize four qubits to an arbitrary state, and then try to optimize the circuit that achieves this.\n", - "\n", - "- ``optimization_level=0``: just maps the circuit to the backend, with no explicit optimization (except whatever optimizations the mapper does).\n", - "\n", - "- ``optimization_level=1``: maps the circuit, but also does light-weight optimizations by collapsing adjacent gates.\n", - "\n", - "- ``optimization_level=2``: medium-weight optimization, including a noise-adaptive layout and a gate-cancellation procedure based on gate commutation relationships.\n", - "\n", - "- ``optimization_level=3``: heavy-weight optimization, which in addition to previous steps, does resynthesis of two-qubit blocks of gates in the circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:58.956270Z", - "start_time": "2019-12-10T21:47:58.937115Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:34.541000Z", - "iopub.status.busy": "2023-08-25T18:25:34.540619Z", - "iopub.status.idle": "2023-08-25T18:25:34.835452Z", - "shell.execute_reply": "2023-08-25T18:25:34.834556Z" - } - }, - "outputs": [], - "source": [ - "import math\n", - "from qiskit.providers.fake_provider import FakeTokyo\n", - "\n", - "backend = FakeTokyo() # mimics the tokyo device in terms of coupling map and basis gates" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:47:59.632459Z", - "start_time": "2019-12-10T21:47:58.959187Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:34.839582Z", - "iopub.status.busy": "2023-08-25T18:25:34.839114Z", - "iopub.status.idle": "2023-08-25T18:25:34.964569Z", - "shell.execute_reply": "2023-08-25T18:25:34.963877Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "
     »\n",
-       "q_0: »\n",
-       "     »\n",
-       "q_1: »\n",
-       "     »\n",
-       "q_2: »\n",
-       "     »\n",
-       "q_3: »\n",
-       "     »\n",
-       "q_4: »\n",
-       "     »\n",
-       "q_5: »\n",
-       "     »\n",
-       "q_6: »\n",
-       "     »\n",
-       "q_7: »\n",
-       "     »\n",
-       "q_8: »\n",
-       "     »\n",
-       "q_9: »\n",
-       "     »\n",
-       "«     ┌────────────────────────────────────────────────────────────────────────────┐\n",
-       "«q_0: ┤0                                                                           ├\n",
-       "«     │                                                                            │\n",
-       "«q_1: ┤1                                                                           ├\n",
-       "«     │  Initialize(0.5j,0.35355,0,0,0,0,0,0,0.35355,0.35355j,0,0,0,0,0.5,0.35355) │\n",
-       "«q_2: ┤2                                                                           ├\n",
-       "«     │                                                                            │\n",
-       "«q_3: ┤3                                                                           ├\n",
-       "«     └────────────────────────────────────────────────────────────────────────────┘\n",
-       "«q_4: ──────────────────────────────────────────────────────────────────────────────\n",
-       "«                                                                                   \n",
-       "«q_5: ──────────────────────────────────────────────────────────────────────────────\n",
-       "«                                                                                   \n",
-       "«q_6: ──────────────────────────────────────────────────────────────────────────────\n",
-       "«                                                                                   \n",
-       "«q_7: ──────────────────────────────────────────────────────────────────────────────\n",
-       "«                                                                                   \n",
-       "«q_8: ──────────────────────────────────────────────────────────────────────────────\n",
-       "«                                                                                   \n",
-       "«q_9: ──────────────────────────────────────────────────────────────────────────────\n",
-       "«                                                                                   
" - ], - "text/plain": [ - " »\n", - "q_0: »\n", - " »\n", - "q_1: »\n", - " »\n", - "q_2: »\n", - " »\n", - "q_3: »\n", - " »\n", - "q_4: »\n", - " »\n", - "q_5: »\n", - " »\n", - "q_6: »\n", - " »\n", - "q_7: »\n", - " »\n", - "q_8: »\n", - " »\n", - "q_9: »\n", - " »\n", - "« ┌────────────────────────────────────────────────────────────────────────────┐\n", - "«q_0: ┤0 ├\n", - "« │ │\n", - "«q_1: ┤1 ├\n", - "« │ Initialize(0.5j,0.35355,0,0,0,0,0,0,0.35355,0.35355j,0,0,0,0,0.5,0.35355) │\n", - "«q_2: ┤2 ├\n", - "« │ │\n", - "«q_3: ┤3 ├\n", - "« └────────────────────────────────────────────────────────────────────────────┘\n", - "«q_4: ──────────────────────────────────────────────────────────────────────────────\n", - "« \n", - "«q_5: ──────────────────────────────────────────────────────────────────────────────\n", - "« \n", - "«q_6: ──────────────────────────────────────────────────────────────────────────────\n", - "« \n", - "«q_7: ──────────────────────────────────────────────────────────────────────────────\n", - "« \n", - "«q_8: ──────────────────────────────────────────────────────────────────────────────\n", - "« \n", - "«q_9: ──────────────────────────────────────────────────────────────────────────────\n", - "« " - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc = QuantumCircuit(10)\n", - "\n", - "random_state = [\n", - " 1 / math.sqrt(4) * complex(0, 1),\n", - " 1 / math.sqrt(8) * complex(1, 0),\n", - " 0,\n", - " 0,\n", - " 0,\n", - " 0,\n", - " 0,\n", - " 0,\n", - " 1 / math.sqrt(8) * complex(1, 0),\n", - " 1 / math.sqrt(8) * complex(0, 1),\n", - " 0,\n", - " 0,\n", - " 0,\n", - " 0,\n", - " 1 / math.sqrt(4) * complex(1, 0),\n", - " 1 / math.sqrt(8) * complex(1, 0)]\n", - "\n", - "qc.initialize(random_state, range(4))\n", - "qc.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now map this to the 20-qubit Tokyo device, with different optimization levels:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:00.000884Z", - "start_time": "2019-12-10T21:47:59.634920Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:34.969668Z", - "iopub.status.busy": "2023-08-25T18:25:34.968324Z", - "iopub.status.idle": "2023-08-25T18:25:35.265698Z", - "shell.execute_reply": "2023-08-25T18:25:35.264985Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "gates = OrderedDict([('cx', 70), ('u3', 15), ('u1', 14), ('reset', 4)])\n", - "depth = 86\n" - ] - } - ], - "source": [ - "optimized_0 = transpile(qc, backend=backend, seed_transpiler=11, optimization_level=0)\n", - "print('gates = ', optimized_0.count_ops())\n", - "print('depth = ', optimized_0.depth())" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:00.474954Z", - "start_time": "2019-12-10T21:48:00.003129Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:35.270709Z", - "iopub.status.busy": "2023-08-25T18:25:35.269492Z", - "iopub.status.idle": "2023-08-25T18:25:35.360004Z", - "shell.execute_reply": "2023-08-25T18:25:35.359180Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "gates = OrderedDict([('cx', 22), ('u3', 15), ('u1', 6), ('reset', 4)])\n", - "depth = 41\n" - ] - } - ], - "source": [ - "optimized_1 = transpile(qc, backend=backend, seed_transpiler=11, optimization_level=1)\n", - "print('gates = ', optimized_1.count_ops())\n", - "print('depth = ', optimized_1.depth())" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:01.649048Z", - "start_time": "2019-12-10T21:48:00.477272Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:35.365687Z", - "iopub.status.busy": "2023-08-25T18:25:35.364283Z", - "iopub.status.idle": "2023-08-25T18:25:35.448775Z", - "shell.execute_reply": "2023-08-25T18:25:35.448055Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "gates = OrderedDict([('cx', 20), ('u3', 15), ('u1', 6), ('reset', 4)])\n", - "depth = 39\n" - ] - } - ], - "source": [ - "optimized_2 = transpile(qc, backend=backend, seed_transpiler=11, optimization_level=2)\n", - "print('gates = ', optimized_2.count_ops())\n", - "print('depth = ', optimized_2.depth())" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:03.166110Z", - "start_time": "2019-12-10T21:48:01.651535Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:35.453871Z", - "iopub.status.busy": "2023-08-25T18:25:35.452621Z", - "iopub.status.idle": "2023-08-25T18:25:35.560848Z", - "shell.execute_reply": "2023-08-25T18:25:35.559972Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "gates = OrderedDict([('cx', 20), ('u3', 15), ('u1', 6), ('reset', 4)])\n", - "depth = 39\n" - ] - } - ], - "source": [ - "optimized_3 = transpile(qc, backend=backend, seed_transpiler=11, optimization_level=3)\n", - "print('gates = ', optimized_3.count_ops())\n", - "print('depth = ', optimized_3.depth())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Introducing the DAG" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In Qiskit, we represent circuits internally using a Directed Acyclic Graph (DAG). The advantage of this representation over a pure list of gates (i.e., *netlist*) is that the flow of information between operations are explicit, making it easier for passes to make transformation decisions without changing the semantics of the circuit.\n", - "\n", - "Let's start by building a simple circuit, and examining its DAG." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:03.375950Z", - "start_time": "2019-12-10T21:48:03.169405Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:35.566793Z", - "iopub.status.busy": "2023-08-25T18:25:35.565393Z", - "iopub.status.idle": "2023-08-25T18:25:35.848730Z", - "shell.execute_reply": "2023-08-25T18:25:35.847981Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit\n", - "from qiskit.dagcircuit import DAGCircuit\n", - "q = QuantumRegister(3, 'q')\n", - "c = ClassicalRegister(3, 'c')\n", - "circ = QuantumCircuit(q, c)\n", - "circ.h(q[0])\n", - "circ.cx(q[0], q[1])\n", - "circ.measure(q[0], c[0])\n", - "circ.rz(0.5, q[1]).c_if(c, 2)\n", - "circ.draw(output='mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the DAG, there are three kinds of graph nodes: qubit/clbit input nodes (green), operation nodes (blue), and output nodes (red). Each edge indicates data flow (or dependency) between two nodes. " - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:03.525267Z", - "start_time": "2019-12-10T21:48:03.378085Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:35.853022Z", - "iopub.status.busy": "2023-08-25T18:25:35.852563Z", - "iopub.status.idle": "2023-08-25T18:25:37.484757Z", - "shell.execute_reply": "2023-08-25T18:25:37.483965Z" - }, - "scrolled": true - }, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Could not save to JPEG for display", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/JpegImagePlugin.py:639\u001b[0m, in \u001b[0;36m_save\u001b[0;34m(im, fp, filename)\u001b[0m\n\u001b[1;32m 638\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 639\u001b[0m rawmode \u001b[38;5;241m=\u001b[39m \u001b[43mRAWMODE\u001b[49m\u001b[43m[\u001b[49m\u001b[43mim\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmode\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 640\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", - "\u001b[0;31mKeyError\u001b[0m: 'RGBA'", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[0;31mOSError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:643\u001b[0m, in \u001b[0;36mImage._repr_image\u001b[0;34m(self, image_format, **kwargs)\u001b[0m\n\u001b[1;32m 642\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 643\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msave\u001b[49m\u001b[43m(\u001b[49m\u001b[43mb\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mimage_format\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 644\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:2413\u001b[0m, in \u001b[0;36mImage.save\u001b[0;34m(self, fp, format, **params)\u001b[0m\n\u001b[1;32m 2412\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 2413\u001b[0m \u001b[43msave_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2414\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/JpegImagePlugin.py:642\u001b[0m, in \u001b[0;36m_save\u001b[0;34m(im, fp, filename)\u001b[0m\n\u001b[1;32m 641\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcannot write mode \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mim\u001b[38;5;241m.\u001b[39mmode\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m as JPEG\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m--> 642\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m(msg) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n\u001b[1;32m 644\u001b[0m info \u001b[38;5;241m=\u001b[39m im\u001b[38;5;241m.\u001b[39mencoderinfo\n", - "\u001b[0;31mOSError\u001b[0m: cannot write mode RGBA as JPEG", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/IPython/core/formatters.py:344\u001b[0m, in \u001b[0;36mBaseFormatter.__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 342\u001b[0m method \u001b[38;5;241m=\u001b[39m get_real_method(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint_method)\n\u001b[1;32m 343\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m method \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 344\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmethod\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 345\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 346\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:661\u001b[0m, in \u001b[0;36mImage._repr_jpeg_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 656\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_repr_jpeg_\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 657\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"iPython display hook support for JPEG format.\u001b[39;00m\n\u001b[1;32m 658\u001b[0m \n\u001b[1;32m 659\u001b[0m \u001b[38;5;124;03m :returns: JPEG version of the image as bytes\u001b[39;00m\n\u001b[1;32m 660\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 661\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_repr_image\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mJPEG\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:646\u001b[0m, in \u001b[0;36mImage._repr_image\u001b[0;34m(self, image_format, **kwargs)\u001b[0m\n\u001b[1;32m 644\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 645\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCould not save to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mimage_format\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m for display\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m--> 646\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(msg) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n\u001b[1;32m 647\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m b\u001b[38;5;241m.\u001b[39mgetvalue()\n", - "\u001b[0;31mValueError\u001b[0m: Could not save to JPEG for display" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.converters import circuit_to_dag\n", - "from qiskit.tools.visualization import dag_drawer\n", - "dag = circuit_to_dag(circ)\n", - "dag_drawer(dag)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Therefore, writing a transpiler pass means using Qiskit's DAGCircuit API to analyze or transform the circuit. Let's see some examples of this." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**a. Get all op nodes in the DAG:**" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:03.532050Z", - "start_time": "2019-12-10T21:48:03.527373Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:37.488355Z", - "iopub.status.busy": "2023-08-25T18:25:37.488077Z", - "iopub.status.idle": "2023-08-25T18:25:37.493016Z", - "shell.execute_reply": "2023-08-25T18:25:37.492403Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[DAGOpNode(op=Instruction(name='h', num_qubits=1, num_clbits=0, params=[]), qargs=(Qubit(QuantumRegister(3, 'q'), 0),), cargs=()),\n", - " DAGOpNode(op=Instruction(name='cx', num_qubits=2, num_clbits=0, params=[]), qargs=(Qubit(QuantumRegister(3, 'q'), 0), Qubit(QuantumRegister(3, 'q'), 1)), cargs=()),\n", - " DAGOpNode(op=Instruction(name='measure', num_qubits=1, num_clbits=1, params=[]), qargs=(Qubit(QuantumRegister(3, 'q'), 0),), cargs=(Clbit(ClassicalRegister(3, 'c'), 0),)),\n", - " DAGOpNode(op=Instruction(name='rz', num_qubits=1, num_clbits=0, params=[0.5]), qargs=(Qubit(QuantumRegister(3, 'q'), 1),), cargs=())]" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dag.op_nodes()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each node is an instance of the ``DAGOpNode`` class. Let's examine the information stored in the fourth op node." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:03.540054Z", - "start_time": "2019-12-10T21:48:03.534378Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:37.496135Z", - "iopub.status.busy": "2023-08-25T18:25:37.495635Z", - "iopub.status.idle": "2023-08-25T18:25:37.506647Z", - "shell.execute_reply": "2023-08-25T18:25:37.506048Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "node name: rz\n", - "node op: Instruction(name='rz', num_qubits=1, num_clbits=0, params=[0.5])\n", - "node qargs: (Qubit(QuantumRegister(3, 'q'), 1),)\n", - "node cargs: ()\n", - "node condition: (ClassicalRegister(3, 'c'), 2)\n" - ] - } - ], - "source": [ - "node = dag.op_nodes()[3]\n", - "print(\"node name: \", node.name)\n", - "print(\"node op: \", node.op)\n", - "print(\"node qargs: \", node.qargs)\n", - "print(\"node cargs: \", node.cargs)\n", - "print(\"node condition: \", node.op.condition)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**b. Add an operation to the back:**" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:03.660392Z", - "start_time": "2019-12-10T21:48:03.542892Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:37.509619Z", - "iopub.status.busy": "2023-08-25T18:25:37.509390Z", - "iopub.status.idle": "2023-08-25T18:25:37.728942Z", - "shell.execute_reply": "2023-08-25T18:25:37.727978Z" - }, - "tags": [ - "nbsphinx-thumbnail" - ] - }, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Could not save to JPEG for display", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/JpegImagePlugin.py:639\u001b[0m, in \u001b[0;36m_save\u001b[0;34m(im, fp, filename)\u001b[0m\n\u001b[1;32m 638\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 639\u001b[0m rawmode \u001b[38;5;241m=\u001b[39m \u001b[43mRAWMODE\u001b[49m\u001b[43m[\u001b[49m\u001b[43mim\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmode\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 640\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", - "\u001b[0;31mKeyError\u001b[0m: 'RGBA'", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[0;31mOSError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:643\u001b[0m, in \u001b[0;36mImage._repr_image\u001b[0;34m(self, image_format, **kwargs)\u001b[0m\n\u001b[1;32m 642\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 643\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msave\u001b[49m\u001b[43m(\u001b[49m\u001b[43mb\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mimage_format\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 644\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:2413\u001b[0m, in \u001b[0;36mImage.save\u001b[0;34m(self, fp, format, **params)\u001b[0m\n\u001b[1;32m 2412\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 2413\u001b[0m \u001b[43msave_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2414\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/JpegImagePlugin.py:642\u001b[0m, in \u001b[0;36m_save\u001b[0;34m(im, fp, filename)\u001b[0m\n\u001b[1;32m 641\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcannot write mode \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mim\u001b[38;5;241m.\u001b[39mmode\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m as JPEG\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m--> 642\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m(msg) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n\u001b[1;32m 644\u001b[0m info \u001b[38;5;241m=\u001b[39m im\u001b[38;5;241m.\u001b[39mencoderinfo\n", - "\u001b[0;31mOSError\u001b[0m: cannot write mode RGBA as JPEG", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/IPython/core/formatters.py:344\u001b[0m, in \u001b[0;36mBaseFormatter.__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 342\u001b[0m method \u001b[38;5;241m=\u001b[39m get_real_method(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint_method)\n\u001b[1;32m 343\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m method \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 344\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmethod\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 345\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 346\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:661\u001b[0m, in \u001b[0;36mImage._repr_jpeg_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 656\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_repr_jpeg_\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 657\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"iPython display hook support for JPEG format.\u001b[39;00m\n\u001b[1;32m 658\u001b[0m \n\u001b[1;32m 659\u001b[0m \u001b[38;5;124;03m :returns: JPEG version of the image as bytes\u001b[39;00m\n\u001b[1;32m 660\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 661\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_repr_image\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mJPEG\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:646\u001b[0m, in \u001b[0;36mImage._repr_image\u001b[0;34m(self, image_format, **kwargs)\u001b[0m\n\u001b[1;32m 644\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 645\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCould not save to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mimage_format\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m for display\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m--> 646\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(msg) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n\u001b[1;32m 647\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m b\u001b[38;5;241m.\u001b[39mgetvalue()\n", - "\u001b[0;31mValueError\u001b[0m: Could not save to JPEG for display" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.circuit.library import HGate\n", - "dag.apply_operation_back(HGate(), qargs=[q[0]])\n", - "dag_drawer(dag)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**c. Add an operation to the front:**" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:03.773434Z", - "start_time": "2019-12-10T21:48:03.662725Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:37.734406Z", - "iopub.status.busy": "2023-08-25T18:25:37.733153Z", - "iopub.status.idle": "2023-08-25T18:25:38.089923Z", - "shell.execute_reply": "2023-08-25T18:25:38.088943Z" - } - }, - "outputs": [ - { - "ename": "ValueError", - "evalue": "Could not save to JPEG for display", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/JpegImagePlugin.py:639\u001b[0m, in \u001b[0;36m_save\u001b[0;34m(im, fp, filename)\u001b[0m\n\u001b[1;32m 638\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 639\u001b[0m rawmode \u001b[38;5;241m=\u001b[39m \u001b[43mRAWMODE\u001b[49m\u001b[43m[\u001b[49m\u001b[43mim\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmode\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 640\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", - "\u001b[0;31mKeyError\u001b[0m: 'RGBA'", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[0;31mOSError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:643\u001b[0m, in \u001b[0;36mImage._repr_image\u001b[0;34m(self, image_format, **kwargs)\u001b[0m\n\u001b[1;32m 642\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 643\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msave\u001b[49m\u001b[43m(\u001b[49m\u001b[43mb\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mimage_format\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 644\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:2413\u001b[0m, in \u001b[0;36mImage.save\u001b[0;34m(self, fp, format, **params)\u001b[0m\n\u001b[1;32m 2412\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 2413\u001b[0m \u001b[43msave_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2414\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/JpegImagePlugin.py:642\u001b[0m, in \u001b[0;36m_save\u001b[0;34m(im, fp, filename)\u001b[0m\n\u001b[1;32m 641\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcannot write mode \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mim\u001b[38;5;241m.\u001b[39mmode\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m as JPEG\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m--> 642\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m(msg) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n\u001b[1;32m 644\u001b[0m info \u001b[38;5;241m=\u001b[39m im\u001b[38;5;241m.\u001b[39mencoderinfo\n", - "\u001b[0;31mOSError\u001b[0m: cannot write mode RGBA as JPEG", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/IPython/core/formatters.py:344\u001b[0m, in \u001b[0;36mBaseFormatter.__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 342\u001b[0m method \u001b[38;5;241m=\u001b[39m get_real_method(obj, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint_method)\n\u001b[1;32m 343\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m method \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 344\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmethod\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 345\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 346\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:661\u001b[0m, in \u001b[0;36mImage._repr_jpeg_\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 656\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_repr_jpeg_\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 657\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"iPython display hook support for JPEG format.\u001b[39;00m\n\u001b[1;32m 658\u001b[0m \n\u001b[1;32m 659\u001b[0m \u001b[38;5;124;03m :returns: JPEG version of the image as bytes\u001b[39;00m\n\u001b[1;32m 660\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 661\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_repr_image\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mJPEG\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/work/1/s/.tox/tutorials/lib/python3.8/site-packages/PIL/Image.py:646\u001b[0m, in \u001b[0;36mImage._repr_image\u001b[0;34m(self, image_format, **kwargs)\u001b[0m\n\u001b[1;32m 644\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 645\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCould not save to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mimage_format\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m for display\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m--> 646\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(msg) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01me\u001b[39;00m\n\u001b[1;32m 647\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m b\u001b[38;5;241m.\u001b[39mgetvalue()\n", - "\u001b[0;31mValueError\u001b[0m: Could not save to JPEG for display" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.circuit.library import CCXGate\n", - "dag.apply_operation_front(CCXGate(), qargs=[q[0], q[1], q[2]], cargs=[])\n", - "dag_drawer(dag)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**d. Substitute a node with a subcircuit:**" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:03.905653Z", - "start_time": "2019-12-10T21:48:03.776373Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:38.093916Z", - "iopub.status.busy": "2023-08-25T18:25:38.093527Z", - "iopub.status.idle": "2023-08-25T18:25:38.207711Z", - "shell.execute_reply": "2023-08-25T18:25:38.206862Z" - } - }, - "outputs": [ - { - "data": { - "image/jpeg": "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", - "image/png": "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", - "text/plain": [ - "" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.circuit.library import CHGate, U2Gate, CXGate\n", - "mini_dag = DAGCircuit()\n", - "p = QuantumRegister(2, \"p\")\n", - "mini_dag.add_qreg(p)\n", - "mini_dag.apply_operation_back(CHGate(), qargs=[p[1], p[0]])\n", - "mini_dag.apply_operation_back(U2Gate(0.1, 0.2), qargs=[p[1]])\n", - "\n", - "# substitute the cx node with the above mini-dag\n", - "cx_node = dag.op_nodes(op=CXGate).pop()\n", - "dag.substitute_node_with_dag(node=cx_node, input_dag=mini_dag, wires=[p[0], p[1]])\n", - "dag_drawer(dag)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, after all transformations are complete, we can convert back to a regular QuantumCircuit object.\n", - "This is what the transpiler does! It takes a circuit, operates on it in DAG form, and outputs a transformed circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.154317Z", - "start_time": "2019-12-10T21:48:03.916725Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:38.213190Z", - "iopub.status.busy": "2023-08-25T18:25:38.211932Z", - "iopub.status.idle": "2023-08-25T18:25:38.468812Z", - "shell.execute_reply": "2023-08-25T18:25:38.468062Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit.converters import dag_to_circuit\n", - "circuit = dag_to_circuit(dag)\n", - "circuit.draw(output='mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Implementing a BasicMapper Pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we are familiar with the DAG, let's use it to write a transpiler pass. Here we will implement a basic pass for mapping an arbitrary circuit to a device with limited qubit connectivity. We call this the BasicMapper. This pass is included in Qiskit Terra as well.\n", - "\n", - "The first thing to do when writing a transpiler pass is to decide whether the pass class derives from a ``TransformationPass`` or ``AnalysisPass``. Transformation passes modify the circuit, while analysis passes only collect information about a circuit (to be used by other passes). Then, the ``run(dag)`` method is implemented, which does the main task. Finally, the pass is registered inside the ``qiskit.transpiler.passes`` module.\n", - "\n", - "This pass functions as follows: it traverses the DAG layer-by-layer (each layer is a group of operations acting on independent qubits, so in theory all operations in a layer can be done independently). For each operation, if it does not already meet the coupling map constraints, the pass identifies a swap path and inserts swaps to bring the two qubits close to each other.\n", - "\n", - "Follow the comments in the code for more details." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.178919Z", - "start_time": "2019-12-10T21:48:04.159510Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:38.474133Z", - "iopub.status.busy": "2023-08-25T18:25:38.472904Z", - "iopub.status.idle": "2023-08-25T18:25:38.504720Z", - "shell.execute_reply": "2023-08-25T18:25:38.503967Z" - } - }, - "outputs": [], - "source": [ - "from copy import copy\n", - "\n", - "from qiskit.transpiler.basepasses import TransformationPass\n", - "from qiskit.transpiler import Layout\n", - "from qiskit.circuit.library import SwapGate\n", - "\n", - "\n", - "class BasicSwap(TransformationPass):\n", - " \"\"\"Maps (with minimum effort) a DAGCircuit onto a `coupling_map` adding swap gates.\"\"\"\n", - "\n", - " def __init__(self,\n", - " coupling_map,\n", - " initial_layout=None):\n", - " \"\"\"Maps a DAGCircuit onto a `coupling_map` using swap gates.\n", - " \n", - " Args:\n", - " coupling_map (CouplingMap): Directed graph represented a coupling map.\n", - " initial_layout (Layout): initial layout of qubits in mapping\n", - " \"\"\"\n", - " super().__init__()\n", - " self.coupling_map = coupling_map\n", - " self.initial_layout = initial_layout\n", - "\n", - " def run(self, dag):\n", - " \"\"\"Runs the BasicSwap pass on `dag`.\n", - " \n", - " Args:\n", - " dag (DAGCircuit): DAG to map.\n", - "\n", - " Returns:\n", - " DAGCircuit: A mapped DAG.\n", - "\n", - " Raises:\n", - " TranspilerError: if the coupling map or the layout are not\n", - " compatible with the DAG.\n", - " \"\"\"\n", - " new_dag = DAGCircuit()\n", - " for qreg in dag.qregs.values():\n", - " new_dag.add_qreg(qreg)\n", - " for creg in dag.cregs.values():\n", - " new_dag.add_creg(creg)\n", - " \n", - "\n", - " if self.initial_layout is None:\n", - " if self.property_set[\"layout\"]:\n", - " self.initial_layout = self.property_set[\"layout\"]\n", - " else:\n", - " self.initial_layout = Layout.generate_trivial_layout(*dag.qregs.values())\n", - "\n", - " if len(dag.qubits) != len(self.initial_layout):\n", - " raise TranspilerError('The layout does not match the amount of qubits in the DAG')\n", - "\n", - " if len(self.coupling_map.physical_qubits) != len(self.initial_layout):\n", - " raise TranspilerError(\n", - " \"Mappers require to have the layout to be the same size as the coupling map\")\n", - " \n", - " canonical_register = dag.qregs['q']\n", - " trivial_layout = Layout.generate_trivial_layout(canonical_register)\n", - " current_layout = trivial_layout.copy()\n", - "\n", - " for layer in dag.serial_layers():\n", - " subdag = layer['graph']\n", - "\n", - " for gate in subdag.two_qubit_ops():\n", - " physical_q0 = current_layout[gate.qargs[0]]\n", - " physical_q1 = current_layout[gate.qargs[1]]\n", - " if self.coupling_map.distance(physical_q0, physical_q1) != 1:\n", - " # Insert a new layer with the SWAP(s).\n", - " swap_layer = DAGCircuit()\n", - " swap_layer.add_qreg(canonical_register)\n", - "\n", - " path = self.coupling_map.shortest_undirected_path(physical_q0, physical_q1)\n", - " for swap in range(len(path) - 2):\n", - " connected_wire_1 = path[swap]\n", - " connected_wire_2 = path[swap + 1]\n", - "\n", - " qubit_1 = current_layout[connected_wire_1]\n", - " qubit_2 = current_layout[connected_wire_2]\n", - "\n", - " # create the swap operation\n", - " swap_layer.apply_operation_back(SwapGate(),\n", - " qargs=[qubit_1, qubit_2],\n", - " cargs=[])\n", - "\n", - " # layer insertion\n", - " order = current_layout.reorder_bits(new_dag.qubits)\n", - " new_dag.compose(swap_layer, qubits=order)\n", - "\n", - " # update current_layout\n", - " for swap in range(len(path) - 2):\n", - " current_layout.swap(path[swap], path[swap + 1])\n", - "\n", - " order = current_layout.reorder_bits(new_dag.qubits)\n", - " new_dag.compose(subdag, qubits=order)\n", - "\n", - " return new_dag" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's test this pass on a small example circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.189596Z", - "start_time": "2019-12-10T21:48:04.181850Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:38.510202Z", - "iopub.status.busy": "2023-08-25T18:25:38.508885Z", - "iopub.status.idle": "2023-08-25T18:25:38.522211Z", - "shell.execute_reply": "2023-08-25T18:25:38.521580Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "q = QuantumRegister(7, 'q')\n", - "in_circ = QuantumCircuit(q)\n", - "in_circ.h(q[0])\n", - "in_circ.cx(q[0], q[4])\n", - "in_circ.cx(q[2], q[3])\n", - "in_circ.cx(q[6], q[1])\n", - "in_circ.cx(q[5], q[0])\n", - "in_circ.rz(0.1, q[2])\n", - "in_circ.cx(q[5], q[0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we construct a pass manager that contains our new pass. We pass the example circuit above to this pass manager, and obtain a new, transformed circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.207681Z", - "start_time": "2019-12-10T21:48:04.191604Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:38.526842Z", - "iopub.status.busy": "2023-08-25T18:25:38.525666Z", - "iopub.status.idle": "2023-08-25T18:25:38.548666Z", - "shell.execute_reply": "2023-08-25T18:25:38.547961Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.transpiler import PassManager\n", - "from qiskit.transpiler import CouplingMap\n", - "from qiskit import BasicAer\n", - "pm = PassManager()\n", - "coupling = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6]]\n", - "coupling_map = CouplingMap(couplinglist=coupling)\n", - "\n", - "pm.append([BasicSwap(coupling_map)])\n", - "\n", - "out_circ = pm.run(in_circ)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.457320Z", - "start_time": "2019-12-10T21:48:04.210267Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:38.553696Z", - "iopub.status.busy": "2023-08-25T18:25:38.552485Z", - "iopub.status.idle": "2023-08-25T18:25:38.882683Z", - "shell.execute_reply": "2023-08-25T18:25:38.881873Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "in_circ.draw(output='mpl')" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.807143Z", - "start_time": "2019-12-10T21:48:04.459740Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:38.888024Z", - "iopub.status.busy": "2023-08-25T18:25:38.886710Z", - "iopub.status.idle": "2023-08-25T18:25:39.363778Z", - "shell.execute_reply": "2023-08-25T18:25:39.363070Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "out_circ.draw(output='mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that this pass only inserts the swaps necessary to make every two-qubit interaction conform to the device coupling map. It does not, for example, care about the direction of interactions, or the native gate set supported by the device. This is a design philosophy of Qiskit's transpiler: every pass performs a small, well-defined action, and the aggressive circuit optimization is achieved by the pass manager through combining multiple passes." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Transpiler Logging \n", - "\n", - "Due to the complexity of the internal operations that the transpiler is performing it's likely that you'll end up in a situation where you'd like to debug an issue or just understand more of what is happening inside the transpiler when you call it. To facilitate this the transpiler emits log messages as part of its normal operation. This logging uses the Python standard library `logging` module to emit the log messages. Python's standard logging was used because it allows Qiskit-Terra's logging to integrate in a standard way with other applications and libraries.\n", - "\n", - "For a more thorough introduction to Python logging refer to the [official documentation](https://docs.python.org/3/library/logging.html) and the tutorials and cookbook linked off of there." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - " Note: Most of the logging module functions used in this section adjust global settings. If you run commands in this section it might effect the output from other cells if they are run in a different order.\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Configuring Python Standard Library Logging\n", - "\n", - "By default Python Standard Logging only prints log messages at the `WARNING`, `ERROR`, or `CRITICAL` log levels.\n", - "Since none of the logs emitted by the transpiler use these log levels (they're all informative) you need to configure logging.\n", - "\n", - "The simplest way to do this is to just run:" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.813322Z", - "start_time": "2019-12-10T21:48:04.809390Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:39.369280Z", - "iopub.status.busy": "2023-08-25T18:25:39.368001Z", - "iopub.status.idle": "2023-08-25T18:25:39.373508Z", - "shell.execute_reply": "2023-08-25T18:25:39.372877Z" - } - }, - "outputs": [], - "source": [ - "import logging\n", - "\n", - "logging.basicConfig(level='DEBUG')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `basicConfig()` function (see the docs here: https://docs.python.org/3/library/logging.html#logging.basicConfig) configures a root handler and formatter. We also specify the [log level](https://docs.python.org/3/library/logging.html#levels) to display with the `level` kwarg. Setting it to a level will also include and higher levels. For example, if you set it to `'INFO'`, in addition to the `INFO` level, this will also include the `WARNING`, `ERROR`, and `CRITICAL` log levels.\n", - "\n", - "Now the python environment in this notebook is configured to emit log messages to stderr when you run the transpiler. For example:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - " Note: basicConfig() will only work when called the first time it's called. It detects if a root handler and formatter have already been setup (either by using an earlier basicConfig() call or otherwise) and does nothing if they have. Further adjustments will have to by interacting with the handler directly.\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.870900Z", - "start_time": "2019-12-10T21:48:04.815673Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:39.378269Z", - "iopub.status.busy": "2023-08-25T18:25:39.377087Z", - "iopub.status.idle": "2023-08-25T18:25:40.228239Z", - "shell.execute_reply": "2023-08-25T18:25:40.227595Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager', group='qiskit.transpiler.init')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='dense', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='noise_adaptive', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='trivial', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='basic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='lookahead', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='none', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='stochastic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='synthesis', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='translator', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='unroller', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager', group='qiskit.transpiler.optimization')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='alap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='asap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager', group='qiskit.transpiler.init')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='dense', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='noise_adaptive', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='trivial', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='basic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='lookahead', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='none', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='stochastic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='synthesis', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='translator', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='unroller', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager', group='qiskit.transpiler.optimization')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='alap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='asap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ContainsInstruction - 0.02527 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnitarySynthesis - 0.12302 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: HighLevelSynthesis - 0.05245 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Unroll3qOrMore - 0.02646 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: SetLayout - 0.01431 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: TrivialLayout - 0.07081 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckMap - 0.05507 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FullAncillaAllocation - 0.12159 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: EnlargeWithAncilla - 0.11563 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ApplyLayout - 0.42582 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckMap - 0.04363 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnitarySynthesis - 0.11373 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: HighLevelSynthesis - 0.03862 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnrollCustomDefinitions - 0.41223 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Begin BasisTranslator from source basis {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to target basis {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Begining basis search from {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate u generated using rule \n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate r generated using rule \n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate h generated using rule \n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate p generated using rule \n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate rx generated using rule \n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate x generated using rule \n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Transformation path:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:x/1 => []\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:rx/1 => [Parameter(theta)]\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:p/1 => [Parameter(theta)]\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:h/1 => []\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:r/1 => [Parameter(theta), Parameter(phi)]\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:u/1 => [Parameter(theta), Parameter(phi), Parameter(lam)]\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation path search completed in 0.698s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: x/1 [] =>\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updating transform for mapped instr ('x', 1) x, [] from \n", - " ┌───┐\n", - "q76: ┤ x ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updated transform for mapped instr ('x', 1) x, [] to\n", - " ┌───────────┐\n", - "q76: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: rx/1 [Parameter(theta)] =>\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: p/1 [Parameter(theta)] =>\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: h/1 [] =>\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updating transform for mapped instr ('h', 1) h, [] from \n", - " ┌───┐\n", - "q73: ┤ h ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updated transform for mapped instr ('h', 1) h, [] to\n", - " ┌─────────┐\n", - "q73: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: r/1 [Parameter(theta), Parameter(phi)] =>\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: u/1 [Parameter(theta), Parameter(phi), Parameter(lam)] =>\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation paths composed in 0.022s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation instructions replaced in 0.000s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: BasisTranslator - 722.23139 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckGateDirection - 0.06580 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GateDirection - 0.18215 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.06127 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.02027 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.03624 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01264 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Optimize1qGatesDecomposition - 1.08647 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CXCancellation - 0.06032 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GatesInBasis - 0.03552 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.03934 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01335 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.02670 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01216 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Optimize1qGatesDecomposition - 0.27943 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CXCancellation - 0.04458 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GatesInBasis - 0.03028 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.03648 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01192 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.02432 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01192 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ContainsInstruction - 0.03123 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.compiler.transpiler:Total Transpile Time - 842.20171 (ms)\n" - ] - } - ], - "source": [ - "from qiskit.providers.fake_provider import FakeTenerife\n", - "\n", - "\n", - "log_circ = QuantumCircuit(2, 2)\n", - "log_circ.h(0)\n", - "log_circ.h(1)\n", - "log_circ.h(1)\n", - "log_circ.x(1)\n", - "log_circ.cx(0, 1)\n", - "log_circ.measure([0,1], [0,1])\n", - "\n", - "backend = FakeTenerife()\n", - "\n", - "transpile(log_circ, backend);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can clearly see here when calling `transpile()` it now prints 2 types of log messages. The first is at the `INFO` log level and come from the pass manager. These indicate each pass that was executed and how long that took. The second are at the `DEBUG` level and come from the StochasticSwap pass and describes the internal operation of that pass. It's useful for debugging issues in the pass's operation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Adjusting the log level for the transpiler\n", - "\n", - "The qiskit transpiler uses a single namespace ``qiskit.transpiler``, as used by ``logging.getLogger('qiskit.transpiler')``. This makes it very easy to adjust the log level for just the transpiler. For example if you only wish to see log messages at the INFO level or above you can run:" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:04.917836Z", - "start_time": "2019-12-10T21:48:04.872823Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:40.249888Z", - "iopub.status.busy": "2023-08-25T18:25:40.249337Z", - "iopub.status.idle": "2023-08-25T18:25:40.354414Z", - "shell.execute_reply": "2023-08-25T18:25:40.353951Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager', group='qiskit.transpiler.init')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='dense', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='noise_adaptive', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='trivial', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='basic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='lookahead', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='none', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='stochastic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='synthesis', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='translator', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='unroller', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager', group='qiskit.transpiler.optimization')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='alap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='asap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager', group='qiskit.transpiler.init')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='dense', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='noise_adaptive', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='trivial', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='basic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='lookahead', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='none', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='stochastic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='synthesis', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='translator', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='unroller', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager', group='qiskit.transpiler.optimization')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='alap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='asap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ContainsInstruction - 0.02122 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnitarySynthesis - 0.07987 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: HighLevelSynthesis - 0.04601 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Unroll3qOrMore - 0.02432 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: SetLayout - 0.01264 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: TrivialLayout - 0.06342 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckMap - 0.04697 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FullAncillaAllocation - 0.11587 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: EnlargeWithAncilla - 0.10896 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ApplyLayout - 0.38004 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckMap - 0.04196 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnitarySynthesis - 0.10443 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: HighLevelSynthesis - 0.03910 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnrollCustomDefinitions - 0.23103 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Begin BasisTranslator from source basis {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to target basis {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation path search completed in 0.000s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation paths composed in 0.001s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation instructions replaced in 0.000s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: BasisTranslator - 3.29542 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckGateDirection - 0.05412 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GateDirection - 0.18239 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.05221 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01526 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.04578 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01669 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Optimize1qGatesDecomposition - 1.12319 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CXCancellation - 0.05960 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GatesInBasis - 0.03552 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.04125 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01359 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.02694 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01287 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Optimize1qGatesDecomposition - 0.30375 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CXCancellation - 0.06223 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GatesInBasis - 0.03552 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.04220 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01216 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.02623 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01192 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ContainsInstruction - 0.03195 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.compiler.transpiler:Total Transpile Time - 100.73042 (ms)\n" - ] - } - ], - "source": [ - "logging.getLogger('qiskit.transpiler').setLevel('INFO')\n", - "transpile(log_circ, backend);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Setting up logging to deal with parallel execution\n", - "\n", - "When running the transpiler with multiple circuits by default these circuits are transpiled in parallel. If you want to do this with logging enabled and be able to understand the output some additional steps are required.\n", - "\n", - "If you were just to enable logging as above and then pass `transpile()` multiple circuits you'll get results that are difficult to decipher. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:05.069815Z", - "start_time": "2019-12-10T21:48:04.920183Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:40.450222Z", - "iopub.status.busy": "2023-08-25T18:25:40.449218Z", - "iopub.status.idle": "2023-08-25T18:25:40.914454Z", - "shell.execute_reply": "2023-08-25T18:25:40.913807Z" - }, - "scrolled": false - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager', group='qiskit.transpiler.init')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='dense', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='noise_adaptive', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='trivial', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='basic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='lookahead', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='none', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='stochastic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='synthesis', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='translator', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='unroller', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager', group='qiskit.transpiler.optimization')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='alap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='asap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager', group='qiskit.transpiler.init')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='dense', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='noise_adaptive', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='trivial', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='basic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='lookahead', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='none', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='stochastic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='synthesis', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='translator', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='unroller', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager', group='qiskit.transpiler.optimization')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='alap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='asap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:stevedore.extension:found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ContainsInstruction - 0.02575 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnitarySynthesis - 0.07677 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: HighLevelSynthesis - 0.03552 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Unroll3qOrMore - 0.02050 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: SetLayout - 0.01073 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: TrivialLayout - 0.05722 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckMap - 0.03743 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FullAncillaAllocation - 0.10443 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: EnlargeWithAncilla - 0.08988 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ApplyLayout - 0.34761 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckMap - 0.03505 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnitarySynthesis - 0.07844 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: HighLevelSynthesis - 0.03338 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnrollCustomDefinitions - 0.06008 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Begin BasisTranslator from source basis {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to target basis {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Begining basis search from {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate u generated using rule \n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate r generated using rule \n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate h generated using rule \n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate p generated using rule \n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate rx generated using rule \n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate x generated using rule \n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Transformation path:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:x/1 => []\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:rx/1 => [Parameter(theta)]\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:p/1 => [Parameter(theta)]\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:h/1 => []\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:r/1 => [Parameter(theta), Parameter(phi)]\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:u/1 => [Parameter(theta), Parameter(phi), Parameter(lam)]\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation path search completed in 0.016s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: x/1 [] =>\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updating transform for mapped instr ('x', 1) x, [] from \n", - " ┌───┐\n", - "q92: ┤ x ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updated transform for mapped instr ('x', 1) x, [] to\n", - " ┌───────────┐\n", - "q92: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: rx/1 [Parameter(theta)] =>\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: p/1 [Parameter(theta)] =>\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: h/1 [] =>\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updating transform for mapped instr ('h', 1) h, [] from \n", - " ┌───┐\n", - "q89: ┤ h ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updated transform for mapped instr ('h', 1) h, [] to\n", - " ┌─────────┐\n", - "q89: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: r/1 [Parameter(theta), Parameter(phi)] =>\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: u/1 [Parameter(theta), Parameter(phi), Parameter(lam)] =>\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation paths composed in 0.012s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation instructions replaced in 0.000s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: BasisTranslator - 31.26550 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckGateDirection - 0.06294 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GateDirection - 0.18287 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.05627 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01717 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.03195 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01359 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Optimize1qGatesDecomposition - 1.01113 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CXCancellation - 0.04864 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GatesInBasis - 0.03672 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.04220 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01216 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.02766 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01168 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Optimize1qGatesDecomposition - 0.27442 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CXCancellation - 0.04554 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GatesInBasis - 0.03147 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.03982 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01121 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.02646 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01192 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ContainsInstruction - 0.03099 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ContainsInstruction - 0.01454 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnitarySynthesis - 0.05937 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: HighLevelSynthesis - 0.03552 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Unroll3qOrMore - 0.01955 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: SetLayout - 0.01001 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: TrivialLayout - 0.05031 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckMap - 0.03839 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FullAncillaAllocation - 0.09966 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: EnlargeWithAncilla - 0.08869 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ApplyLayout - 0.33665 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckMap - 0.03624 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnitarySynthesis - 0.08774 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: HighLevelSynthesis - 0.03362 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: UnrollCustomDefinitions - 0.06127 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Begin BasisTranslator from source basis {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to target basis {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Begining basis search from {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate u generated using rule \n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate r generated using rule \n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate h generated using rule \n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate p generated using rule \n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate rx generated using rule \n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Gate x generated using rule \n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Transformation path:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:x/1 => []\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:rx/1 => [Parameter(theta)]\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:p/1 => [Parameter(theta)]\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:h/1 => []\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:r/1 => [Parameter(theta), Parameter(phi)]\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:u/1 => [Parameter(theta), Parameter(phi), Parameter(lam)]\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation path search completed in 0.017s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: x/1 [] =>\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updating transform for mapped instr ('x', 1) x, [] from \n", - " ┌───┐\n", - "q96: ┤ x ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updated transform for mapped instr ('x', 1) x, [] to\n", - " ┌───────────┐\n", - "q96: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: rx/1 [Parameter(theta)] =>\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: p/1 [Parameter(theta)] =>\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: h/1 [] =>\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updating transform for mapped instr ('h', 1) h, [] from \n", - " ┌───┐\n", - "q93: ┤ h ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Updated transform for mapped instr ('h', 1) h, [] to\n", - " ┌─────────┐\n", - "q93: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: r/1 [Parameter(theta), Parameter(phi)] =>\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "DEBUG:qiskit.transpiler.passes.basis.basis_translator:Composing transform step: u/1 [Parameter(theta), Parameter(phi), Parameter(lam)] =>\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation paths composed in 0.012s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation instructions replaced in 0.000s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: BasisTranslator - 31.84676 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CheckGateDirection - 0.06795 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GateDirection - 0.18001 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.05984 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01764 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.03290 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01502 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Optimize1qGatesDecomposition - 1.02520 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CXCancellation - 0.05102 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GatesInBasis - 0.03767 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.04101 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01311 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.02646 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01216 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Optimize1qGatesDecomposition - 0.27895 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: CXCancellation - 0.04435 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: GatesInBasis - 0.03076 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Depth - 0.03934 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01144 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: Size - 0.02575 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: FixedPoint - 0.01097 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.passmanager.passrunner:Pass: ContainsInstruction - 0.03099 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:qiskit.compiler.transpiler:Total Transpile Time - 459.21159 (ms)\n" - ] - } - ], - "source": [ - "# Change log level back to DEBUG\n", - "logging.getLogger('qiskit.transpiler').setLevel('DEBUG')\n", - "# Transpile multiple circuits\n", - "circuits = [log_circ, log_circ]\n", - "transpile(circuits, backend);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see here we get log messages from both circuits being transpiled together. There is no way to know which pass is part of which circuit's transpilation. Luckily Python logging provides tools to deal with this. The simplest one is to just change the [log formatter](https://docs.python.org/3/library/logging.html#logging.Formatter) so that includes additional information so we can associate a log message with the process it came from." - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:05.077804Z", - "start_time": "2019-12-10T21:48:05.073526Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:40.965311Z", - "iopub.status.busy": "2023-08-25T18:25:40.964819Z", - "iopub.status.idle": "2023-08-25T18:25:40.970620Z", - "shell.execute_reply": "2023-08-25T18:25:40.969947Z" - } - }, - "outputs": [], - "source": [ - "formatter = logging.Formatter('%(name)s - %(processName)-10s - %(levelname)s: %(message)s')\n", - "handler = logging.getLogger().handlers[0]\n", - "handler.setFormatter(formatter)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then rerun the `transpile()` call and see the new log formatter." - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:05.205597Z", - "start_time": "2019-12-10T21:48:05.081153Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:40.973810Z", - "iopub.status.busy": "2023-08-25T18:25:40.973461Z", - "iopub.status.idle": "2023-08-25T18:25:41.578367Z", - "shell.execute_reply": "2023-08-25T18:25:41.577893Z" - }, - "scrolled": false - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager', group='qiskit.transpiler.init')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='dense', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='noise_adaptive', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='trivial', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='basic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='lookahead', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='none', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='stochastic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='synthesis', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='translator', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='unroller', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager', group='qiskit.transpiler.optimization')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='alap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='asap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager', group='qiskit.transpiler.init')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='dense', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='noise_adaptive', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='trivial', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager', group='qiskit.transpiler.layout')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='basic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='lookahead', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='none', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='sabre', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='stochastic', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager', group='qiskit.transpiler.routing')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='synthesis', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='translator', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='unroller', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager', group='qiskit.transpiler.translation')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager', group='qiskit.transpiler.optimization')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='alap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='asap', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='default', value='qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager', group='qiskit.transpiler.scheduling')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.ag', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.bm', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.greedy', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.layers', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='clifford.lnn', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='linear_function.pmh', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.acg', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.basic', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.default', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "stevedore.extension - MainProcess - DEBUG: found extension EntryPoint(name='permutation.kms', value='qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation', group='qiskit.synthesis')\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: ContainsInstruction - 0.02432 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: UnitarySynthesis - 0.09656 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: HighLevelSynthesis - 0.05364 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Unroll3qOrMore - 0.02170 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: SetLayout - 0.01454 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: TrivialLayout - 0.06270 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CheckMap - 0.05245 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FullAncillaAllocation - 0.12374 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: EnlargeWithAncilla - 0.11754 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: ApplyLayout - 0.36788 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CheckMap - 0.04435 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: UnitarySynthesis - 0.09680 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: HighLevelSynthesis - 0.04268 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: UnrollCustomDefinitions - 0.06890 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - INFO: Begin BasisTranslator from source basis {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to target basis {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Begining basis search from {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate u generated using rule \n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate r generated using rule \n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate h generated using rule \n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate p generated using rule \n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate rx generated using rule \n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate x generated using rule \n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Transformation path:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: x/1 => []\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: rx/1 => [Parameter(theta)]\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: p/1 => [Parameter(theta)]\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: h/1 => []\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: r/1 => [Parameter(theta), Parameter(phi)]\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: u/1 => [Parameter(theta), Parameter(phi), Parameter(lam)]\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - INFO: Basis translation path search completed in 0.026s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: x/1 [] =>\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Updating transform for mapped instr ('x', 1) x, [] from \n", - " ┌───┐\n", - "q104: ┤ x ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Updated transform for mapped instr ('x', 1) x, [] to\n", - " ┌───────────┐\n", - "q104: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: rx/1 [Parameter(theta)] =>\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: p/1 [Parameter(theta)] =>\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: h/1 [] =>\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Updating transform for mapped instr ('h', 1) h, [] from \n", - " ┌───┐\n", - "q101: ┤ h ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Updated transform for mapped instr ('h', 1) h, [] to\n", - " ┌─────────┐\n", - "q101: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: r/1 [Parameter(theta), Parameter(phi)] =>\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: u/1 [Parameter(theta), Parameter(phi), Parameter(lam)] =>\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - INFO: Basis translation paths composed in 0.019s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - INFO: Basis translation instructions replaced in 0.000s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: BasisTranslator - 47.47343 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CheckGateDirection - 0.07129 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: GateDirection - 0.19956 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Depth - 0.06771 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01836 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Size - 0.04053 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01669 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Optimize1qGatesDecomposition - 1.08838 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CXCancellation - 0.05770 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: GatesInBasis - 0.04220 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Depth - 0.04721 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01526 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Size - 0.03314 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01526 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Optimize1qGatesDecomposition - 0.31447 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CXCancellation - 0.05436 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: GatesInBasis - 0.03982 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Depth - 0.04649 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01407 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Size - 0.03171 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01407 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: ContainsInstruction - 0.03719 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: ContainsInstruction - 0.01597 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: UnitarySynthesis - 0.06342 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: HighLevelSynthesis - 0.03839 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Unroll3qOrMore - 0.02265 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: SetLayout - 0.01025 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: TrivialLayout - 0.05293 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CheckMap - 0.04387 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FullAncillaAllocation - 0.11039 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: EnlargeWithAncilla - 0.10037 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: ApplyLayout - 0.34881 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CheckMap - 0.04029 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: UnitarySynthesis - 0.08535 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: HighLevelSynthesis - 0.03815 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: UnrollCustomDefinitions - 0.06866 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - INFO: Begin BasisTranslator from source basis {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to target basis {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Begining basis search from {('h', 1), ('measure', 1), ('cx', 2), ('x', 1)} to {'reset', 'u2', 'measure', 'snapshot', 'id', 'u1', 'delay', 'u3', 'barrier', 'cx'}.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate u generated using rule \n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate r generated using rule \n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate h generated using rule \n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate p generated using rule \n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate rx generated using rule \n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Gate x generated using rule \n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n", - " with total cost of 1.0.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Transformation path:\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: x/1 => []\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: rx/1 => [Parameter(theta)]\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: p/1 => [Parameter(theta)]\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: h/1 => []\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: r/1 => [Parameter(theta), Parameter(phi)]\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: u/1 => [Parameter(theta), Parameter(phi), Parameter(lam)]\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - INFO: Basis translation path search completed in 0.020s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: x/1 [] =>\n", - " ┌───────────┐\n", - "q: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Updating transform for mapped instr ('x', 1) x, [] from \n", - " ┌───┐\n", - "q108: ┤ x ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Updated transform for mapped instr ('x', 1) x, [] to\n", - " ┌───────────┐\n", - "q108: ┤ U3(π,0,π) ├\n", - " └───────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: rx/1 [Parameter(theta)] =>\n", - " ┌────────────┐\n", - "q: ┤ R(theta,0) ├\n", - " └────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: p/1 [Parameter(theta)] =>\n", - " ┌──────────────┐\n", - "q: ┤ U(0,0,theta) ├\n", - " └──────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: h/1 [] =>\n", - " ┌─────────┐\n", - "q: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Updating transform for mapped instr ('h', 1) h, [] from \n", - " ┌───┐\n", - "q105: ┤ h ├\n", - " └───┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Updated transform for mapped instr ('h', 1) h, [] to\n", - " ┌─────────┐\n", - "q105: ┤ U2(0,π) ├\n", - " └─────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: r/1 [Parameter(theta), Parameter(phi)] =>\n", - " ┌───────────────────────────────────┐\n", - "q: ┤ U3(theta,phi - π/2,π/2 - 1.0*phi) ├\n", - " └───────────────────────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - DEBUG: Composing transform step: u/1 [Parameter(theta), Parameter(phi), Parameter(lam)] =>\n", - " ┌───────────────────┐\n", - "q: ┤ U3(theta,phi,lam) ├\n", - " └───────────────────┘\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - INFO: Basis translation paths composed in 0.027s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.transpiler.passes.basis.basis_translator - MainProcess - INFO: Basis translation instructions replaced in 0.000s.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: BasisTranslator - 49.36886 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CheckGateDirection - 0.07153 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: GateDirection - 0.18787 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Depth - 0.06032 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01717 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Size - 0.03386 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01168 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Optimize1qGatesDecomposition - 1.03378 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CXCancellation - 0.05388 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: GatesInBasis - 0.03767 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Depth - 0.04673 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01335 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Size - 0.02694 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01144 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Optimize1qGatesDecomposition - 0.28086 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: CXCancellation - 0.04649 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: GatesInBasis - 0.03099 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Depth - 0.03934 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01144 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: Size - 0.02599 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: FixedPoint - 0.01121 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.passmanager.passrunner - MainProcess - INFO: Pass: ContainsInstruction - 0.03076 (ms)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit.compiler.transpiler - MainProcess - INFO: Total Transpile Time - 585.16884 (ms)\n" - ] - } - ], - "source": [ - "transpile(circuits, backend);" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now the format for the log messages has been changed and it includes a process name for each of the transpilation processes so it's at least clear which log messages go together.\n", - "\n", - "There are many different options for how you can configure, this example is pretty limited. Refer to the documentation for more examples and options to build more sophisticated use cases that suit your specific use case or preferences." - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": { - "ExecuteTime": { - "end_time": "2019-12-10T21:48:10.920429Z", - "start_time": "2019-12-10T21:48:10.912305Z" - }, - "execution": { - "iopub.execute_input": "2023-08-25T18:25:41.587119Z", - "iopub.status.busy": "2023-08-25T18:25:41.586886Z", - "iopub.status.idle": "2023-08-25T18:25:41.772067Z", - "shell.execute_reply": "2023-08-25T18:25:41.771231Z" - }, - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:41 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "celltoolbar": "Tags", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" - } - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "2a78c65199f8464cad8e826f476ab169": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_e8a0db65f2d94080898ff75d01662e2b", - "placeholder": "​", - "style": "IPY_MODEL_e8b43570a48c4466aff6c1b286c6383e", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "e8a0db65f2d94080898ff75d01662e2b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "e8b43570a48c4466aff6c1b286c6383e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/tutorials/circuits_advanced/05_pulse_gates.ipynb b/docs/tutorials/circuits_advanced/05_pulse_gates.ipynb deleted file mode 100644 index 83ce068c7b14..000000000000 --- a/docs/tutorials/circuits_advanced/05_pulse_gates.ipynb +++ /dev/null @@ -1,529 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Pulse gates\n", - "\n", - "Most quantum algorithms can be described with circuit operations alone. When we need more control over the low-level implementation of our program, we can use _pulse gates_. Pulse gates remove the constraint of executing circuits with basis gates only, and also allow you to override the default implementation of any basis gate.\n", - "\n", - "Pulse gates allow you to map a logical circuit gate (e.g., `X`) to a Qiskit Pulse program, called a `Schedule`. This mapping is referred to as a _calibration_. A high fidelity calibration is one which faithfully implements the logical operation it is mapped from (e.g., whether the `X` gate calibration drives $|0\\rangle$ to $|1\\rangle$, etc.).\n", - "\n", - "A schedule specifies the exact time dynamics of the input signals across all input _channels_ to the device. There are usually multiple channels per qubit, such as drive and measure. This interface is more powerful, and requires a deeper understanding of the underlying device physics.\n", - "\n", - "It's important to note that Pulse programs operate on physical qubits. A drive pulse on qubit $a$ will not enact the same logical operation on the state of qubit $b$ -- in other words, gate calibrations are not interchangeable across qubits. This is in contrast to the circuit level, where an `X` gate is defined independent of its qubit operand.\n", - "\n", - "This page shows you how to add a calibration to your circuit.\n", - "\n", - "**Note:** Not all providers support pulse gates." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Build your circuit\n", - "\n", - "Let's start with a very simple example, a Bell state circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:44.058723Z", - "iopub.status.busy": "2023-08-25T18:25:44.058441Z", - "iopub.status.idle": "2023-08-25T18:25:45.601003Z", - "shell.execute_reply": "2023-08-25T18:25:45.600288Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXEAAADuCAYAAADPwDeGAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAdm0lEQVR4nO3de3SU5b328W/OJxKSAJpAAgEBBQIBAmwTqRoKchaworZU0IqKxQqtJSjuqnRZEKGu97VuKgjVasuhKrUWEKhFhVJAAgRBzmhsEjJsA4EwCRGSyf5jmpSQCWQmM5m5J9dnLVYyz+F+fgPDNfdzP6eA6urqakRExEiB3i5ARERcpxAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMFuztAqS+6mq4WOXtKpwTGgQBAd6uwn9UV1dTXl7u7TKcEhkZSYA+BM1OIe6DLlbB7NXersI5C+6FMH2a3Ka8vJxWrVp5uwynWK1WoqKivF1Gi6PhFBERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXaSHatGlD586d6dKlCwkJCU6vP23aNJKTkz1QmTSF7nYh4qeSkpKYPHkyN998M+np6bRv377O/LNnz7Jnzx527drFypUr2bdvX4NtPf3008ybN48TJ06QlZVFfn6+p8uXRlJPXMTPDB48mDVr1pCXl8evfvUrxo4dWy/AAWJjYxkyZAizZ88mNzeXbdu2ce+999ZbribAAW644QZGjhzp8fcgjef3IV5cXEx2djZdu3YlPDyc5ORkZsyYQVlZGQ899BABAQG8+uqr3i5TPMhmgwMFsGI7vP4JvLkVNu6Hcxe8XZl7tWrVit/+9rds3bqVCRMmEBQUVDuvpKSEv//976xYsYI//vGPrFu3joKCgjrrZ2ZmsmrVKjZt2kTHjh2BugEOkJ2dzdKlS5vnDUmj+PVwSm5uLiNH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v2/nHzn76dj+eh774Z0+twsoIt6lEMd+UBOgdevWHmnf1THxy3vhxSXnmL94hbtLExEf4eqYuEIciImxH6X21L1KzlsvNHlMvPxChZuqERF/ohAHevXqxZo1a9i+fbtH2ndlrEtj4SIti6tj4jo7Bdi7dy/9+/cnJCSE3Nxcevbs6e2SNBYuIo2ii32Afv36cc8993Dp0iVGjhzJp59+Wmf+qVOnmD9/PmVlzXOaoK7OFJHGUk/830pLSxk3blztLSQ7dOhA+/btKSoqorCwkOrqakpKSoiNjfV4LeqFi0hjqSf+bzExMXz00UcsX76c22+/nfLycvbt20dgYCDDhw9n+fLlREdHN0straIiCA8LVS9cRK5JPXEfVfHtRcJCQxTiInJVCnEREYNpOEVExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQM9n8wwEoOGPYewwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit import QuantumCircuit\n", - "\n", - "circ = QuantumCircuit(2, 2)\n", - "circ.h(0)\n", - "circ.cx(0, 1)\n", - "circ.measure(0, 0)\n", - "circ.measure(1, 1)\n", - "\n", - "circ.draw('mpl')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Build your calibrations\n", - "\n", - "Now that we have our circuit, let's define a calibration for the Hadamard gate on qubit 0.\n", - "\n", - "In practice, the pulse shape and its parameters would be optimized through a series of Rabi experiments (see the [Qiskit Textbook](https://learn.qiskit.org/course/quantum-hardware-pulses/calibrating-qubits-using-qiskit-pulse) for a walk through). For this demonstration, our Hadamard will be a Gaussian pulse. We will _play_ our pulse on the _drive_ channel of qubit 0.\n", - "\n", - "Don't worry too much about the details of building the calibration itself; you can learn all about this on the following page: [building pulse schedules](06_building_pulse_schedules.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:45.604362Z", - "iopub.status.busy": "2023-08-25T18:25:45.603808Z", - "iopub.status.idle": "2023-08-25T18:25:46.475986Z", - "shell.execute_reply": "2023-08-25T18:25:46.475062Z" - } - }, - "outputs": [], - "source": [ - "from qiskit import pulse\n", - "from qiskit.pulse.library import Gaussian\n", - "from qiskit.providers.fake_provider import FakeValencia\n", - "\n", - "backend = FakeValencia()\n", - "\n", - "with pulse.build(backend, name='hadamard') as h_q0:\n", - " pulse.play(Gaussian(duration=128, amp=0.1, sigma=16), pulse.drive_channel(0))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's draw the new schedule to see what we've built." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:46.480233Z", - "iopub.status.busy": "2023-08-25T18:25:46.479652Z", - "iopub.status.idle": "2023-08-25T18:25:46.635696Z", - "shell.execute_reply": "2023-08-25T18:25:46.635007Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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MxERERERUU3GkStEb7nQ0FCMHDmSiRUiIiIiIqJy4sgVIiIiIiIiIiI1cEFbFRQVFeHhw4cwNjausB1ViIiIiIiIiKhiCSGQkZEBW1tbaGhU3WQdJldU8PDhQ9jZ2VV3GERERERERESkgvv376NevXpVdj4mV1Qg3e3k5q1opTufEBEREREREVH1y8jIQDMnxyp/dmdyRQXSqUDGxsYwMTGp5miIiIiIiIiIqCRVvaQHdwsiIiIiIiIiIlIDkytERERERERERGpgcoWIiIiIiIiISA1MrhARERERERERqYHJFSIiIiIiIiIiNTC5QkRERERERESkBiZXiIiIiIiIiIjUwOQKEREREREREZEamFwhIiIiIiIiIlIDkytERERERERERGpgcoWIiIiIiIiISA1MrhARERERERERqYHJFSIiIiIiIiIiNTC5QkRERERERESkBiZXiIiIiIiIiIjUwOQKEREREREREZEamFwhIiIiIiIiIlIDkytERERERERERGpgcoWIiIiIiIiISA1MrhARERERERERqYHJFSIiIiIiIiIiNTC5QkRERERERESkBiZXiIiIiIiIiIjUwOQKEREREREREZEamFwhIiIiIiIiIlIDkytERERERERERGpgcoWIiIiIiIiISA1a1R0AERERkbqux2tW6fnesS+s0vMRERHR643JFSIiInotVHWCRB3qxMrEDBER0ZuHyRUiIiKqMjUpgVJZSroHTLwQERHVTEyuEBERUYViAqX8mHghIiKqmbigLRERERERERGRGjhyhYiIiMqMo1OqHke1EBERvb6YXCEiIiKlmECpOZh4ISIiql6cFkREREREREREpAaOXCEiInqLcXTKm4+jWoiIiCofR64QEREREREREamBI1eIiIjeYByZQiUp7f3BkS1ERESq4cgVIiIiIiIiIiI1cOQKERFRDcfRKVRZuF4LERGRajhyhYiIiIiIiIhIDRy5QkREVANwdAq9bjiqhYiI6F8cuUJEREREREREpAaOXCEiInpNcHQKvSk4qoWIiN42HLlCRERERERERKQGjlwhIiKqQhydQm87jmohIqI3EUeuEBERERERERGpgSNXiIiIKhhHpxCVD0e1EBFRTcWRK0REREREREREamByhYiIiIiIiIhIDZwWREREVA6c+kNUtThliIiIXmccuUJEREREREREpAaOXCEiIlKCI1OIao7SPq8c2UJERJWNI1eIiIiIiIiIiNTAkStERPTW4ugUorcD12shIqLKxpErRERERERERERq4MgVIiJ6o3F0ChGVhKNaiIioInDkChERERERERGRGphcISIiIiIiIiJSA6cFERFRjcepP0RUGThliIiIVMWRK0REREREREREauDIFSIiqhE4OoWIXicc1UJERC/jyBUiIiIiIiIiIjVw5AoREb02ODqFiN4EHNVCRPT24cgVIiIiIiIiIiI1cOQKERFVGY5MIaK3XWn/DnJkCxFRzcSRK0REREREREREauDIFSIiqlAcnUJEVH5cr4WIqGbiyBUiIiIiIiIiIjVw5AoREZUZR6cQEVU9jmohInp9MblCRERKMYFCRFRzMPFCRFS9OC2IiIiIiIiIiEgNHLlCRPQW4+gUIqI3H0e1EBFVPo5cISIiIiIiIiJSA0euEBG94Tg6hYiIisNRLUREFYPJFSKiGo7JEyIiqgyl/f+FyRcion8xuUJEVAMwgUJERK8bjnohIvoX11whIiIiIiIiIlJDjR65IpFI5H7X0tKCqakpbGxs0LZtW/Tp0wd+fn7Q0ir+MnNycrBw4ULs3LkT9+7dg4WFBXx9fbFgwQLUrVu3si+BiEiGo1OIiOhNwVEtRPS2kQghRHUHUV7S5EpAQAAAoKioCGlpabh9+zaio6MhhEDjxo2xbds2tG/fXqH/8+fP4eXlhfDwcNjY2MDd3R1xcXE4f/48rKysEB4ejkaNGiE9PR2mpqZ4kPAQJiYmVXqNRPRmYQKFiIioeEy8EJG60tPTUa+uLdLS0qr0+f2NSK4ou4TY2Fh89dVX2L17NwwMDPDXX3/B2dlZrs3s2bPxn//8B25ubvjtt99gZGQEAFi+fDmmTZsGDw8PnD59mskVIioTJlCIiIgqHhMvRKQKJlfKoaTkitTo0aOxbt06tGnTBpGRkbLyvLw81K5dG2lpaYiMjESbNm3k+rVu3RpXr17FxYsX0aRJEyZXiEgOEyhERESvDyZeiEiqupIrNXrNFVUsW7YMO3fuxN9//40///wTXbp0AQD89ddfSEtLg4ODg0JiBQAGDRqEq1ev4vDhw5g6dWpVh01EVYAJEiIiojeDOv9PZ2KGiCrCG59cMTU1Ra9evbBnzx6EhobKkitXrlwBALi4uCjtJy2/evVq1QRKRJWCCRQiIiIqCRffJaKK8MYnVwDA2dkZe/bswc2bN2Vl9+7dAwDUq1dPaR9peXx8fOUHSESlYpKEiIiIqlp5v38wKUP09nkrkiu1atUCAKSkpMjKMjMzAQAGBgZK+xgaGgIAMjIyKjk6ojcLkyBERET0tqus70NM2hC9vt6K5Ip0wVvpArjlFRUVJUu6ENUU91PNqzsEIiIiIqoA/9yt2vPZmaWU3ojoNZOVlVUt530rkitPnz4FAFhYWMjKpNsuZ2dnK+0jfUGMjY1lZb18e1ZWiERERERERERUQ70VyZW///4bANC8eXNZWf369QEADx48UNpHWm5vby8r++X4rxy5QkRERERERPSaysrKqpaBEW98ciUtLQ2//vorAMDLy0tW3rp1awBAZGSk0n7S8latWsnKWrZsWaX7ZBMRERERERGR6tLT06vlvBrVctYqNG3aNGRlZaFdu3Zwc3OTlXfu3BmmpqaIjY3F5cuXFfrt2bMHANCnT5+qCpWIiIiIiIiIaqA3Nrnyzz//4P3338e6detgaGiIdevWydXr6Ohg4sSJAIAJEybILXqzfPlyXL16FR4eHmjbtm2Vxk1ERERERERENcsbMS1oxIgRAICioiKkp6fj9u3buHXrFoQQaNKkCbZv346WLVsq9Js9ezZ+//13nD17Fk2aNIG7uzvi4+MREREBKysrrF+/voqvhIiIiIiIiIhqGomQ7lNcA726tbKWlhZMTExga2uLtm3bws/PD3379oWmZvH7zOfk5GDhwoXYvn077t+/DwsLC/j6+mLBggWoV68egBdztkxNTfEg4SHXXCEiIiIiIiJ6TaWnp6NeXVukpaVV6fN7jU6uVBUmV4iIiIiIiIhef9WVXHlj11whIiIiIiIiIqoKTK4QEREREREREanhtU2uHD9+HM7OztDT04NEIkFqamp1h0REREQEADh48CCcW7dCQkJCdYdCREREr4HXcreg5ORkDBkyBO+88w5Wr14NXV1dGBoaVndYREREVEESHjzA5s2bce7cOSQmPgEA2NrawrVdOwwaNBhNmzat5giJiIiIVPdaLmh7/Phx9OrVCydOnED37t2rOxwuaEtERFSB/ggLw4wZ06GlpYVevXvDsakjJBoSxN2Nw8mTv+PRo0c4euwX2NraVneoxSosLERBQQF0dHQUdi8kIiKi6lNdC9q+liNX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YWJiUvXBEREREREREVGp0tPTUa+uLdLS0qr0+b1S1lwhIiIiIiIiInpbMLlCRERERERERKQGJleIiIiIiIiIiNRQaQva1jRceoaIiIiIiIiIyoMjV4iIiIiIiIiI1MDkChERERERERGRGphcISIiIiIiIiJSA5MrRERERERERERqYHKFiIiIiIiIiEgNTK4QEREREREREamByRUiIiIiIiIiIjVoVXcANYEQAgCQkZFRzZEQERERERERUXGkz+3S5/iqwuSKCqQvTjMnx2qOhIiIiIiIiIhKk5ycDFNT0yo7n0RUdTqnBioqKsLDhw9hbGwMiURS3eGoLD09HXZ2drh//z5MTEyqOxyiKsX3P73t+Bmgtxnf//Q24/uf3nZpaWmoX78+UlJSYGZmVmXn5cgVFWhoaKBevXrVHUa5mZiY8B9Wemvx/U9vO34G6G3G9z+9zfj+p7edhkbVLjHLBW2JiIiIiIiIiNTA5AoRERERERERkRqYXHmD6erqYt68edDV1a3uUIiqHN//9LbjZ4DeZnz/09uM739621XXZ4AL2hIRERERERERqYEjV4iIiIiIiIiI1MDkChERERERERGRGphcISIiIiIiIiJSA5Mrb6CcnBzMnTsXTZs2hZ6eHmxtbfHxxx8jISGhukMjUlt2djYOHDiAUaNGwdHREXp6ejA0NETr1q3xzTffIDMzU6GPRCIp9cfb27saroao7Dw9PUt8Lx8/frzUY3Tv3l3W/sGDB1UQNVHFuXDhAoYMGQJbW1toa2vDzMwM7u7u2LBhA15dSjA6OhorVqzA0KFD4eDgIHvfx8XFVU/wRCq4dOkSFi1ahAEDBqBevXqy921pNm7ciPbt28PIyAgWFhbo3bs3zp49W2z73NxcLF68GC4uLjAyMoKuri4aNmyITz75BP/8809FXhKRysr6/j906BACAgLQsmVL1KpVC9ra2qhduzZ69+6NI0eOqHzeBQsWyM61devWcsXOBW3fMM+fP4eXlxfCw8NhY2MDd3d3xMXF4fz587CyskJ4eDgaNWpU3WESldvatWvxySefAACaNWuGFi1aID09HWfPnkVGRgacnJwQFhaG2rVry/qMGDGi2OMdPXoUT58+xdy5czF//vzKDp9IbZ6enggLC8PAgQNhZGSkUD9t2jS0bNmy2P4bN27EyJEjIZFIIITA/fv3Ua9e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", - "text/plain": [ - "
" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "h_q0.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Link your calibration to your circuit\n", - "\n", - "All that remains is to complete the registration. The circuit method `add_calibration` needs information about the gate and a reference to the schedule to complete the mapping:\n", - "\n", - " QuantumCircuit.add_calibration(gate, qubits, schedule, parameters)\n", - "\n", - "The `gate` can either be a `circuit.Gate` object or the name of the gate. Usually, you'll need a different schedule for each unique set of `qubits` and `parameters`. Since the Hadamard gate doesn't have any parameters, we don't have to supply any." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:46.639164Z", - "iopub.status.busy": "2023-08-25T18:25:46.638668Z", - "iopub.status.idle": "2023-08-25T18:25:46.642564Z", - "shell.execute_reply": "2023-08-25T18:25:46.641961Z" - } - }, - "outputs": [], - "source": [ - "circ.add_calibration('h', [0], h_q0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Lastly, note that the transpiler will respect your calibrations. Use it as you normally would (our example is too simple for the transpiler to optimize, so the output is the same)." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:46.647204Z", - "iopub.status.busy": "2023-08-25T18:25:46.645289Z", - "iopub.status.idle": "2023-08-25T18:25:47.132553Z", - "shell.execute_reply": "2023-08-25T18:25:47.131802Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "['id', 'rz', 'sx', 'x', 'cx', 'reset']\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAdoAAADuCAYAAACeeMagAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAme0lEQVR4nO3dd3hUdb7H8fekFxLpRQIEAgiBEELvIVyQgLCICqioqCzqqoANWFkfJesqIOVKuQgrWFYUuFdRERQsKCDKUgNKWQghQGKChpYCgZS5f4wZiKkzmZOZCZ/X8/CYmTnlO7vJfOb82jGZzWYzIiIiYggPZxcgIiJSnSloRUREDKSgFRERMZCCVkRExEAKWhEREQMpaEVERAykoBURETGQglZERMRACloREREDKWhFREQMpKAVERExkIJWRETEQApaERERAyloRUREDKSgFRERMZCCVkRExEAKWhEREQMpaEVERAykoBURETGQglZERMRACloREREDKWhFREQMpKAVERExkIJWRETEQApaERERAyloRUREDKSgFRERMZCCVkRExEAKWhEREQMpaEVERAykoBURETGQglZERMRACloREREDKWhFREQMpKAVERExkIJWRETEQApaERERA3k5uwARsxmu5ju7Ctfk4wkmk7OrkJKYzWYuXbrk7DJsEhAQgEm/UFVOQStOdzUfpq1xdhWuafYY8NVfqUu6dOkSNWrUcHYZNsnKyiIwMNDZZdxw1HQsIiJiIAWtiIiIgRS0IiIiBlLQyg0t+dB3LLjPxJ4Nc0vdZsF9Jj6dO6wKqxKR6kRBKyIiYiAFrYiIiIEUtCIiIgbSDD0RIO/qJS5npju7DBGphhS0IsCOj15ix0cvObsMEamGFLQiQPuYR2jVfVSJr308a1AVVyMi1YmCVgSo2bAVTdsPdHYZcoNr2LAhAQEBmM1mMjIyOHv2bIX39fLy4rnnnmPBggVcvnzZwCrFVhoM5Ua+/PJLYmNjqVOnDgEBAURERDBz5kyuXr3q7NJExA6RkZHMmjWLb775hvPnz5Oamsrx48dJTEwkPT2dlJQU1q1bx/Tp02nSpEmpx/Hy8uL9999n5syZfPrpp/j7+1fhu5DyKGjdxLx58xg8eDCbNm0iKCiIW265hSNHjjB9+nRiYmL0DVbEjYwZM4bt27cTHx/PtGnTGDBgADVr1iy23c0338zw4cN55ZVXOHHiBGvXrqVPnz5FtikM2dGjRwPQt29fIiMjq+JtSAWp6dgN7Ny5kylTpmAymXj77bcZN24cAMePHyc2NpYffviB559/ntdff925hYrY6Uoe7E2CY2mWn329IKw+dG4Oft7Ors5xmjZtyvLlyxk0qHi/f0pKCvHx8Vy4cAGTyUSDBg3o1KkTtWrVAsDT05ORI0cycuRIli5dypQpU8jJySkSsjk5Odx+++3s2LGjSt+XlE1B6wZefvllzGYzDz/8sDVkAcLCwlixYgXR0dG88cYbTJ8+nfr16zuxUhHb5BfAxgOw7Sjk5BZ9be9JWLcPereG2yLB083b30aNGsWKFSsICgqyPnfgwAGWLFnCp59+SlpaWon7tWzZknvuuYdHH32Uxo0bA/DYY48xZMgQjh49ag3twpDdtGmT8W9GbOI2v7rp6elMnTqVli1b4ufnR5MmTZg8eTLZ2dmMHz8ek8nE4sWLnV2mldlsdshxMjMz+fLLLwGYMGFCsdf79etH69atuXr1KuvWrXPIOUWqQn4BvLMNvjpYPGQLXcmDzYdgxRbIy6/a+hxp/PjxrF692hqyp0+fZujQoURGRrJs2bJSQxYgISGBl19+mdDQUB5//HGysrIAaNasmULWTbhF0MbHxxMREcGcOXNIS0sjPDyc3NxcFi5cyJgxYzh8+DAAHTt2NKyG/v37YzKZSEpKKnfbAwcOEBUVRUJCQqXPu2/fPq5evYqvry9dunQpcZvCPhs1F9kuJLw/k1ea6Xzbc6VuM3mlmRHPra/Cqm4M6+Php+SKbXvoF/hkr6HlGGbUqFH885//xMPD8nH73nvv0b59e7744gubjpOXl8cbb7xBVFQU6enXFlcxm808/fTTClkX5vJBm56ezvDhw0lLS+PZZ58lNTWVvXv3kpaWxuzZs9mwYQO7du3CZDLRoUMHZ5cLwNtvv83+/fuJiYkhMTGxUsc6evQoYPn26uVVckt/WFhYkW1FXF32FUtzsS1+TIBMNxvz16RJE5YvX24N2Xnz5vHAAw+QkZFh1/G8vLx45ZVXqFu3rvU5k8nEE088gY+Pj0NqFsdz+aCdNGkSycnJPPnkk8ydO7dI/8bUqVOJjIwkLy+P0NBQgoODnVjpNfPmzeP+++8nOTmZmJiYCl0Fl+b8+fMA1gERJSl8rXBbEVe3M9H2puD8Athx3Jh6jLJ8+XLr59LKlSt57rnSW07K88fRxTk5OdZWs/bt2/Piiy9WvmAxhEsH7eHDh1mzZg1169Zl5syZJW7TuXNngGLD2U+cOMGf/vQngoKCqFWrFg888IBNk79Lk5ycTFJSUpn/Tp06xYwZMxgwYACnTp0iJiaGU6dO2XW+nJwcgDK/rfr6+gJoio+4jcO/VO1+zjB69GhuvfVWwNIn+8QTT9h9rJJC9vbbb+eOO+6wzqOfNm0arVq1qnzh4nAuPep41apVFBQUMHbsWGrUqFHiNoUTs68P2szMTGJiYqhduzarVq3i8uXLTJ06lWHDhrF9+3ZrM449+vbta/M+SUlJ3HfffWzdutXmff38/ADKXJTiypUrAHZPUu/SpUuZgzGM5untz8h/HHPa+V1Z61atyM+tfl+gBjy5ntpNOtq8394Dhwl52DWWxCwoKCjz9YkTJ1p//stf/lKp5uKSQrawT3bmzJm89NJLeHl58dhjj/Hss8+WeqxWrVpV6vPvRtawYUN2795t174uHbSbN28GICYmptRtkpMtoymuD9p//vOfpKSksHXrVpo2bQpASEgIvXr1Yt26ddx+++121xQREVHhvpCzZ89am43btm1r1/kq0ixckeblsqSlpZGSkmLXvo7g5RvgtHO7ul9SfyHvyiVnl+Fwl7IuUNuO/S5nXXDq72pFdejQwTpI8eeff2bDhg12Hae8kAVYuHAh06ZNw8/Pj4ceeogXXnih1Nat1NRUu+qQynHpoD158iRgGQhUkry8PLZv3w4UDdr169fTp08fa8gC9OzZkxYtWvDZZ59VKmjXrVtHaGhoudslJycTHR0NWFaBWbJkiV3na926NWD53yIvL6/EAVHHjx8vsq2tGjZsaNd+juLpreXiSnNzo5ur5RVt9pnD0La/zftlnTlknUvqbAUFBaUG191332392d6//YqELMC5c+dYvXo1Dz74ILVq1SI2NpaPP/64xGM2atRIV7R2qsznpEsHbXZ2NlB63+OaNWtIT08nKCiI5s2bW58/dOgQo0YVvxNLu3btOHTokDHFXiclJcU64viuu+5i5cqVeHp62nWsqKgofHx8uHLlCrt376ZHjx7Ftvn+++8B6N69u13nsLc5xFGu5MG0NY45Vvrpn/ngbx0ZMeULmkUY08SYfOg7Pno1hkGPvE14vwcB+O1kPB+80Ik7p39LSNtoh53r6LFj+Lr0X6l90jPhlXVg62zzt2aNp8Eb4w2pyVbZ2dmldml17drV+vMnn3xi87ErGrLXn+PBBx8ELF1BpQXtsWPHCAwMtLkeqRyX/mpT+A1i797iE+hSU1OZMmUKYGmmMZlM1tfOnz9f4rqhtWvX5ty5c8YUe524uDgSEhIYOXIkq1atKnVaTkUEBQVZJ6W/+eabxV7funUrR48excfHhxEjRth9nupi2/vPcHPr3oaFbGnqNetIWOfb2fbBsw5brKQ6qxsE7UJs26dNI2hwkzH1OFqnTp0Ay+eUrc21toYswJ49e6w/Fw4QFdfh0kE7cKDltmWzZ88uMkd0165dxMTEWCdtG7lQRaHo6GjuvPPOCn0bXLBgAXFxcaxZs6ZSIVvohRdesK5z/O6771qfP378OOPHW77dP/roozf88oupx37k1M9fETXkGaecPyr2KX49sYek+M+dcn53c08PqF/BGXl1g2BsL2PrcZT69etTu7alB3r//v027WtPyIKlq6pwVoW940HEOC4dtFOnTqVOnTqcPn2adu3aERERQatWrejWrRstWrRgwIABQPGpPbVq1eLChQvFjnfu3DnrH4Ct4uLi+PDDD6lXr1652/r7+/Piiy/i7e2Y1dB79OjBrFmzMJvNPPjgg4SGhhIVFUWbNm1ISEige/fuzJo1yyHncmcHvl6Cf1BdQiOHOuX8N9/Sl+B6ofy0ealTzu9uAn1h0iBo1aDs7cLqw+RBEORXNXVVlpeXFwkJCaSkpNg0rc9kMtkVsoUSExM5ffq0Bjy5IJfu/QkJCWHbtm1MmTKFLVu2kJSURHh4OMuWLWPChAnWFZH+GLRt27YtsS/20KFD9OvXr0pqd7TCxTnmzZvHrl27OHPmDK1bt2bs2LE8++yz1rm0N6qC/DyO7/mE5h2H4elV9AtOft5V9m18nf/88AEX0o7i4elNzYatCO/7IJG3PglA1vlf2Pv5PE4f/IbM9JPk5V7mpnotaNt3HJ1uew4Pj/L72E0mE00jBnNoywqu5mTh41dy/51cU8MPnhgIp8/C9mNw7Aycy7L03Xp7wsRB0LSOs6u0zS+//GLXfFaz2czu3bsZPXq0XWsXd+vWzeZzStVw6aAFS2iuX198ndmsrCySkpLw8PCgffv2RV4bNmwY06dPJzk5mZAQS0fQv//9b44fP86cOXOqpG4jDB48mMGDBzu7DJf064k95OZk0SCs6IdNft5VPpk9mOTD39E04lba9L4PL28/0k//RMLutdagTT91gOO71xLWeSQ3NQijID+Xk/s3sn3NX7n4ayL/NX5Zhepo1LInP29exi//+Z7QyFiHv8/qqkkduPv3QH1pLVy8DAE+7heylTVnzhzy8/M5ePCg1i6uRlw+aEtz8OBBzGYzrVu3JiCg6DzMRx55hEWLFjFixAji4uLIyclh6tSpdOvWTQOGqqmzKZYWjJr1w4o8v2/j6yQf/o4uw5+n95hXi7xmvm7BgZC20Tw4P7HIoLqo2KfY9Mb9HPxuOT3umEFgrUbl1nFTA8v5z6UcVNCKXebPn+/sEsTBXLqPtiw//fQTULzZGCA4OJjNmzfTqFEj7r77bv785z/Tq1cv1q9frzlk1dTlzN8A8K1RtA/+Pz+8j29gLbqPLL4OrOm63wUvH39ryObnXSUn6xyXM9NpFjEYs7mAMycqNgXKv4blEuxSxq92vQ8RqX7c9oq2rKAFyx1tSmpylurJxO9Xon+YWnMh7Rj1mnXEy6fskTQF+Xns/mwWh7//FxfOJBQ7zpXsit2wwWydGWoqczsRuXFU26CVG4t/sGU0eE62ffOkt77/DPu/XETrHmPo+qe/EXBTfTw8vfk1aS/bV0/DbC57XdtCOVmW8wcElz86XURuDG4btIXrIIsA1AmxDIi7kFb05gQ1G7bm3C9HyMu9gpd36SOzj3z/Ho3b9GPIk6uLPH/hTIJNdVz8ffvCekRE1GEp1UK90Ch8/INJS9hR5Pk2vcZyJfs8uz75R7F9rl/ByeThWWxFp9ycbPZ98d821ZGasAMPTy8ate5t034iUn257RWtyPU8PDwJ63IHiXs+KXL12jF2Mon7PmPnp//gTOIumkbciqe3H+dSDnI+9T/c8fzXALTsdhc/b17G54vG0LT9QC5dPMPBLW/hV6Pi80vMZjMnD2ykWYdYzaEVESsFrVQbHQb+hcPb3uHEvvW06nYnAJ5ePoyc9iV7v5jHf374gB/+bzqe3n7UbNCK8H4PWfftN3Y+Pn5BHPv3/5K491Nq1G5C+5hHaNCiKx/PGlih86cc2Upm+klixv2PIe9PRNyTyawV0MXJHHn3nk9mx5J7JZtRL25zzAFtsP6/R5J57jR3/31Xkfm4lTF7DNXy7j1lKVyw4iZ/iLvD2dWUrqy797iqrKws3b3HCdRHK9VK37HzSE34kZM/fVml5/01aR/H935K33vnOSxkRaR6uMG+K0t1VyekHZP+lVfl560fGsXk9yo2BUhEbiy6ohURETGQglZERMRACloREREDqY9WnM7H0zK6VorzKf82uOIkAQEBZGVlOex4c5atJiP7EsGBAUx59O5ijx3hj3c6k6qhoBWnM5luvCks4v5MJpNDp8r4+Prhk5uPj68fgYGBxR6L+1LTsYiIiIEUtCIiIgZS0IqIiBhIQSsiImIgBa2IiIiBFLQiIiIGUtCKiIgYSEErIiJiIAWtiIiIgRS0IiIiBlLQioiIGEhBKyIiYiAFrYiIiIEUtCIiIgZS0IqIiBhIQSsiImIgBa2IiIiBvJxdgIiIuB+z2cylS5ecXYZNAgICMJlMVX5eBa2IiNjs0qVL1KhRw9ll2CQrK4vAwMAqP6+ajkVERAykoBURETGQglZERMRACloREREDKWhFxGXkF4DZ7OwqRBxLo45FxCnMZkj8DY78AqfPQfI5yLpy7fWMy7B0MzSpDbc0grD64ISZGSKVpqAVkSp1NQ92JsL3RyHtYunbmYEjqZZ/Xx2EBsHQuzV0DwNffXKJG9Gvq4hUmcRfYdUO+C3T9n3PZMDa3bDlCNzTA1o2cHx9IkZQ0IqI4QrMsCEeNh+yXKlWxtksWPw1RLeBEVHgoZEm4uIUtCJiqPwC+OBH2JPk2ONuOQIXLsEDvcFTYSsuTL+eImIYsxnW/NvxIVto/ylLiGuksgDUq1cPf39/Z5dRjK5o3URSUhLffPMNu3btYteuXfz000/k5uYybtw43nnnHWeXJ1KinYmWf0bakwQt6kPvVsaeR4xRq1YtunfvTufOnYmKiqJ27dp4eXmRk5PD8ePH2bNnD3v27GH//v0UFBSUepz69evz7bffkpKSwogRI7h8+XIVvouyKWjdxOuvv86CBQucXYZIhV24BB/vsX2/Z2Ih2N8yvWf+xorts24vtGkEddxrjfsbWu/evXn88ce566678PHxKXGbQYMGWX9OTExk6dKlvPXWW5w9e7bIdoUhGx4eTnh4OEuWLOGhhx4ytH5bqOnYTdStW5ehQ4fy0ksv8dlnn/H44487uySRMn28B3Jybd8v2B9qBlj+W1FX8iwjksX1hYWFsWXLFr7//nvuvffeUkP2j1q0aMFrr71GcnIy06ZNw9PTEygasgAnT57k73//u2H120NXtG7ihRdeKPJ4x44dTqpEpHzns+HA6ao958EUSM+EukFVe16puIkTJzJr1iwCAgKsz/3222+sWrWKHTt2sGfPHk6dOkV+fj41atSgffv2dO7cmcGDBxMbGwuAn58fs2bNYuTIkTzzzDO8+eabRUI2JiaGEydOOOX9lcatrmjT09OZOnUqLVu2xM/PjyZNmjB58mSys7MZP348JpOJxYsXO7tMK7NGaMgN6odjzhmgtP1Y1Z9Tyufh4cHSpUtZuHChNWQTExO5//77rZ/jq1at4ujRo+Tk5JCbm8v58+fZtm0br7/+OkOGDKFly5YsXryY/Px8ALp3787WrVtdPmTBjYI2Pj6eiIgI5syZQ1paGuHh4eTm5rJw4ULGjBnD4cOHAejYsaNhNfTv3x+TyURSUlK52x44cICoqCgSEhIMq0fEVe1y0mfdrhMageyKlixZwqOPPmp9vGjRIjp06MDKlSu5cuVKGXtec/z4cSZOnEifPn04dszyjaqw+Tg1NdVlQxbcJGjT09MZPnw4aWlpPPvss6SmprJ3717S0tKYPXs2GzZsYNeuXZhMJjp06ODscgF4++232b9/PzExMSQmGjzsUsSFXLxsGQjlDFk5lmZrcR0TJ060hmxubi733HMPkyZNIjvbvv+jEhMTrVe1hby8vLh4sYz1PJ3MLYJ20qRJJCcn8+STTzJ37lyCgq51wkydOpXIyEjy8vIIDQ0lODjYiZVeM2/ePO6//36Sk5OJiYmp0FWwSHWQfLb8bYx0+pxzzy/XhIWFMWvWLOvj+++/n9WrV9t9vMKBT23atAEgJycHsMyfXbRoUeWKNZDLB+3hw4dZs2YNdevWZebMmSVu07lzZwAiIyOtzxUGc7du3fD19cXkwNt+JCcnk5SUVOa/U6dOMWPGDAYMGMCpU6eIiYnh1KlTDqtBxFWlOvnCwtnnl2uWL19u7ZNdvHgxa9assftYJY0ujo6O5tw5yzere++9l+HDh1e+aAO4/KjjVatWUVBQwNixY6lRo+RJcoUrgVwftAkJCXz00Ud07doVHx8ftm/f7rCa+vbta/M+SUlJ3HfffWzdutVhdThKly5dSEtLc3YZUk2ED3qO8IFPlfha4RzZsgT7XfvvjJGlb1faPNsFC9/gz1+8UrFiXcjIh54isEYwqWmphISEFHvsaspaPAKgZ8+e9O/fH7A09/71r3+1+1wlhWxhn+ykSZNYuXIlAM8//zyfffZZqcdp1aoVHnYujt2wYUN277ZvDpnLB+3mzZsBiImJKXWb5ORkoGjQ9uvXj9TUVABmzJjh0KCNiIio8Nyvs2fPWpuN27Zt67AaHCktLY2UlBRnlyHVRJOM0i8pC+fIVoSHR8W3vV5m5kW3/H0u+L3fsSA/n5SUlGKP3c31c/3j4uLs7pMtK2QB3n//faZMmUJkZCQ9e/YkKiqKffv2lXiswkyoai4ftCdPngSgWbNmJb6el5dnDdHrg9beby0VsW7dOkJDQ8vdLjk5mejoaADGjBnDkiVLDKupMho2bOjsEqQa8fcr/WMlowKr4gX7WUK2oAAyckrfrrRj+ft60bhx4/JP5GI8fh9B6+HpSePGjYs9djUFBQWlBlfNmjUZNWoUYLnYsLfJuLyQLbRkyRKWLVsGwIQJE0pd0KdRo0aVuqK1l8sHbeG3oNLWrVyzZg3p6ekEBQXRvHnzqiytTCkpKdYRx3fddRcrV660DkV3NfY2h4iU5GAKvPldya9VZEnFGSMtV7IZOTDjY9vPP/fl54h89znbd3SyV//nfTKysmnUsBHJycnFHrua7OzsUrvzCsfGgKX7r6JTeK5X0ZAFy1Xt4sWL8fb2LrNr79ixYwQGBtpcS2W5/GCowm8Re/fuLfZaamoqU6ZMAaBDhw4OHfBUWXFxcSQkJDBy5EhWrVqFl5fLf6cRcYgmtW/s88u1Aapg3yp2toQsWEL/559/BixddNevPOUKXD5oBw4cCMDs2bM5evSo9fldu3YRExNDeno6YOxCFYWio6O58847K/SNaMGCBcTFxbFmzRqFrNxQgv2hlpM+54L8oFbVX7DIH0RFRVl/3rPHtjtL2BqyhQpb5jw9PYmIiLCxYmO5fNBOnTqVOnXqcPr0adq1a0dERAStWrWiW7dutGjRggEDBgBF+2eNEhcXx4cffki9evXK3dbf358XX3wRb29vh5x7+/bt1K1b1/pv/vz5AKxevbrI844c9CVir64tnHTe5uBCDVs3rNq1rzUr2DKt0d6QBTh9+tri2tef3xW4/KVWSEgI27ZtY8qUKWzZsoWkpCTCw8NZtmwZEyZMICwsDKiaoHWm3NzcYreGArhy5UqR/o/cXDtulyLiYD1bwlcHq3Y5RBPQS/ekdQlPP/009erVw8/Pz6b7wg4bNszutYtXrVrFzp07uXz5Mj/99JNddRvF5YMWLG3u69evL/Z8VlYWSUlJeHh40L59eydUVnX69++vmxSI26gVCB2bwr6TVXfOdiG6c4+rsDfo3nrrLerUqcMTTzxh89rFCQkJLru2vFsEbWkOHjyI2WymdevWJXZ+f/jhhwAcOnSoyOPQ0FC6dOlSdYWK3IBu7wRHUuHyVePP5esFd+pPulqYM2cOS5cuJTMz09mlOIxbB23ht6bSmo0L53H98fG4ceN45513DK1N5EZ3UwDc0Rne/9G2/Qrnx1Zkzm2h2ztrEFR1Up1CFqp50KqpVcS5ujSHxN/gRxta9Coy1/Z6XZtDjzDb9hGpSi4/6rgs5QWtiDiXyQSjulrC0AhRzeDuHhppLK7Nra9oC9dBFhHX5eEB9/S0rPb09SHHjEQ2ATFtYVhHy/FFXJlbB62IuAcPE9zW0TIyeNWPcCbD/mPVC4J7e0Lz8qezi7gEBa2IVJnQuvDcUNh9Ar4/CinnK75vo5rQp7WlGdpHn1ziRvTrKiJVytvTsqBFjzA4edYyBSj5HJw+axlpbMbSNBzkb1m3OKQ23NLQcgWrvlhxRwpaEXEKk8lyhRta99pzZjMUmC1NzQpVqS4UtCLiMkwm8FTASjWj8XoiIiIGUtCKiIgYSEErIiJiIAWtiIiIgTQYSkREbBYQEEBWVpbDjjdn2Woysi8RHBjAlEfvLvbYEUq6y1tVUNCKiIjNTCYTgYGOu2WSj68fPrn5+Pj6ERgYWOyxO1PTsYiIiIEUtCIiIgZS0IqIiBhIQSsiImIgBa2IiIiBFLQiIiIGUtCKiIgYSEErIiJiIAWtiIiIgRS0IiIiBlLQioiIGEhBKyIiYiAFrYiIiIEUtCIiIgZS0IqIiBhIQSsiImIgBa2IiIiBFLQiIiIGUtCKiIgYSEErIiJiIAWtiIiIgRS0IiIiBlLQSrnmzJlDz549qVWrFjVr1qRPnz5s3LjR2WWJSDk+//xzOnbsiK+vL6GhocyfP9/ZJVWprVu3MmLECJo1a4bJZOIf//iHU+pQ0Eq5Nm/ezMMPP8y3337Lzp076dWrF8OGDWP79u3OLk1ESrF7925GjBjBkCFDiI+PZ8aMGUyfPp2lS5c6u7Qqk5WVRXh4OK+99hoNGzZ0Wh1eTjuzuI0vvviiyOPXXnuNjRs3snbtWnr37u2kqkSkLPPnz6dr167MnDkTgLZt23Lw4EFmzZrFY4895uTqqsbQoUMZOnQoANOmTXNaHQpasVlBQQEZGRkEBgY6uxQRt3MxM5sz6eeLPZ+Xn2/979ETycUeX69Z4wb4+niXeZ7t27czfvz4Is/FxsYyd+5ckpOTCQkJqczbsFuB2czxpBTMf3jelvdfK7gG9erUrIJqHUNBKzZ79dVXuXDhAo888oizSxFxO74+3nz0xRYuZmaX+Pqlyzm89b+fl/q4VWgILUcPKfc8qampxZpLCx+npqY6LWg9TCaOJJ5i++6fS3y9vPfv7eXJpIfuNLxOR1IfrdhkyZIlvPrqq3z44YdO+0MVcWd+vj6Mvi3Grn39/Xy5a2g0HiaTg6uqWrH9ulHfzivSoTE9qFfbvn2dRUErFTZ37lymTJnCunXrGDhwoLPLEXFbYc1upk+XCJv3u/3WPtwUVLEum0aNGpGWllbkuTNnzlhfcyZvby/GDBuAh4dtXxhaNw+hR1S4QVUZR0ErFfLiiy8SFxfH559/rpAVcYDB0V2pX6dWhbePbBtGZNuwCm/fu3dvNm3aVOS5jRs30qxZM5dojWrcsC4De3eu8Pb+fr7cNSQakxtezStopVxPPfUUc+bM4b333uOWW24hLS2NtLQ0Ll686OzSRNyWt5cXY4bH4OlR/sdwcI1ARtzax6bjP/300+zcuZO//e1vHDlyhHfffZdFixbx17/+1d6SHS66R0ea3tygQtuOvLUPwRW8mi+UlZVFfHw88fHxXL16lbS0NOLj40lISLCnXLuZzGbzHwd/iRRR2jfIcePG8c4771RtMSLVzLc/7mPT1l1lbjN+zFBahdp+FbphwwamT5/OkSNHaNiwIZMnT+aZZ56xt1RDpJ+/yMK3P+Jqbl6p23QMb8ndwwfYfOzvvvuOmJji/eHR0dF89913Nh/PXgpacahzFzPJz893u8EKIs5SUFDAsg8+42TKmRJf79W5PX8a2KuKq6pa/44/zMebtpX42k1BgTz18F34+/lWcVWOo6ZjcahNW3Yyf/n/sX1PyUP3RaQoDw8PRt8Wg4938dmW9WrXZEh0NydUVbW6RbahTVjTEl8bNbS/W4csKGjFgX5NP8+Bw8cxm82EhjhvuTMRd1OnVjDDBvQs8pyHh4kxw2LwLiGAqxuTycSdsf0I8C8aqL07t6dlaGMnVeU4Clo3k5+fz3vvvcett95KvXr18PX1pWnTpsTGxrJ8+XLyf19NxRm++WEvZiC8VSiNG9R1Wh0i7qjrH67q/qtXZ0Ia1XNiRVUrqEYAdwzuZ31cv05NYqvJ1byC1o1kZGQwaNAgHnjgAb766it8fHyIjIykoKCAL7/8kgkTJpCZmemU2gqvZgH+q3cnp9Qg4s5MJhN3DulHoL8fTRrVp3/Pjs4uqcq1v6U5ndq3/v1qfkC1uZrXYCg3MmrUKOuKTP/617+KjKY7c+YMK1asYPLkyTavQbzo3bVkZl2uVG2Xc3LIzcvHy8uTAD+/Sh1L5EaWm5eHh4dHhab9VEdms5ncvDx8vMtey7mqBdXwZ+K4O+zaV0HrJvbs2UOXLl3w8vJi3759tG/f3mHHfvV/3icjq+R1V0VExDKXefoTY+3at3pcl98APvnkEwBuu+02h4YsWL6pVYauZkWkuqvM56SC1k0cOnQIgJ49e5azpe3sbQ4B+PXsBf57+f8C8Jf7RmgQlIjIHyho3URGRgYAN910k8OPXZk+2ss5OZgBLy9P3v1wU7nbi4i4o8r00Spo3URwcDCAIesLZ2ZdrnQfbV5evvp5RURKoKB1E+3atWPt2rX8+OOPDj+2vX0P6psVkRtFZfpoNerYTezbt49OnTrh7e1NfHw84eHOvSdjYd+sGZj44B3qmxURKcWNOVHLDUVFRTF69Ghyc3MZMmQIW7ZsKfL6mTNnmDlzJtnZVdN8u1mrQImIVIiuaN1IRkYGI0aMsN7eqXHjxtx8882kpqaSkpKC2Wzm/Pnz1KxZ09A6dDUrIlJxuqJ1I8HBwXz99desWLGC/v37c+nSJfbv34+HhweDBw9mxYoVBAUFGV7H1dxcGjesp6tZEZEK0BWt2MVsNnPlai5+vj7OLkVExKUpaEVERAykpmMREREDKWhFREQMpKAVERExkIJWRETEQApaERERAyloRUREDKSgFRERMZCCVkRExEAKWhEREQMpaEVERAykoBURETGQglZERMRACloREREDKWhFREQMpKAVERExkIJWRETEQApaERERAyloRUREDKSgFRERMZCCVkRExEAKWhEREQMpaEVERAykoBURETGQglZERMRACloREREDKWhFREQMpKAVERExkIJWRETEQApaERERAyloRUREDKSgFRERMZCCVkRExEAKWhEREQMpaEVERAykoBURETGQglZERMRACloREREDKWhFREQMpKAVEREx0P8DdPWgtaOrfZwAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit import transpile\n", - "from qiskit.providers.fake_provider import FakeHanoi\n", - "\n", - "backend = FakeHanoi()\n", - "\n", - "circ = transpile(circ, backend)\n", - "\n", - "print(backend.configuration().basis_gates)\n", - "circ.draw('mpl', idle_wires=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that `h` is not a basis gate for the mock backend `FakeHanoi`. Since we have added a calibration for it, the transpiler will treat our gate as a basis gate; _but only on the qubits for which it was defined_. A Hadamard applied to a different qubit would be unrolled to the basis gates.\n", - "\n", - "That's it!\n", - "\n", - "## Custom gates\n", - "\n", - "We'll briefly show the same process for nonstandard, completely custom gates. This demonstration includes a gate with parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:47.136297Z", - "iopub.status.busy": "2023-08-25T18:25:47.135737Z", - "iopub.status.idle": "2023-08-25T18:25:47.303976Z", - "shell.execute_reply": "2023-08-25T18:25:47.303212Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit import QuantumCircuit\n", - "from qiskit.circuit import Gate\n", - "\n", - "circ = QuantumCircuit(1, 1)\n", - "custom_gate = Gate('my_custom_gate', 1, [3.14, 1])\n", - "# 3.14 is an arbitrary parameter for demonstration\n", - "circ.append(custom_gate, [0])\n", - "circ.measure(0, 0)\n", - "\n", - "circ.draw('mpl')" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:47.307411Z", - "iopub.status.busy": "2023-08-25T18:25:47.307150Z", - "iopub.status.idle": "2023-08-25T18:25:47.312146Z", - "shell.execute_reply": "2023-08-25T18:25:47.311509Z" - } - }, - "outputs": [], - "source": [ - "with pulse.build(backend, name='custom') as my_schedule:\n", - " pulse.play(Gaussian(duration=64, amp=0.2, sigma=8), pulse.drive_channel(0))\n", - "\n", - "circ.add_calibration('my_custom_gate', [0], my_schedule, [3.14, 1])\n", - "# Alternatively: circ.add_calibration(custom_gate, [0], my_schedule)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we use the `Gate` instance variable `custom_gate` to add the calibration, the parameters are derived from that instance. Remember that the order of parameters is meaningful." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:47.315255Z", - "iopub.status.busy": "2023-08-25T18:25:47.314810Z", - "iopub.status.idle": "2023-08-25T18:25:47.548426Z", - "shell.execute_reply": "2023-08-25T18:25:47.547564Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "circ = transpile(circ, backend)\n", - "circ.draw('mpl', idle_wires=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Normally, if we tried to transpile our `circ`, we would get an error. There was no functional definition provided for `\"my_custom_gate\"`, so the transpiler can't unroll it to the basis gate set of the target device. We can show this by trying to add `\"my_custom_gate\"` to another qubit which hasn't been calibrated." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:47.552195Z", - "iopub.status.busy": "2023-08-25T18:25:47.551564Z", - "iopub.status.idle": "2023-08-25T18:25:47.581769Z", - "shell.execute_reply": "2023-08-25T18:25:47.580890Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\"Cannot unroll the circuit to the given basis, ['id', 'rz', 'sx', 'x', 'cx', 'reset']. Instruction my_custom_gate not found in equivalence library and no rule found to expand.\"\n" - ] - } - ], - "source": [ - "circ = QuantumCircuit(2, 2)\n", - "circ.append(custom_gate, [1])\n", - "\n", - "\n", - "from qiskit import QiskitError\n", - "try:\n", - " circ = transpile(circ, backend)\n", - "except QiskitError as e:\n", - " print(e)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:47.585881Z", - "iopub.status.busy": "2023-08-25T18:25:47.585428Z", - "iopub.status.idle": "2023-08-25T18:25:47.725832Z", - "shell.execute_reply": "2023-08-25T18:25:47.724998Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:47 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "322a886f2b1b43f0bb60ddff7ea72e33": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4786f90d3c284a3f9d9c8cbdf164899c": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_322a886f2b1b43f0bb60ddff7ea72e33", - "placeholder": "​", - "style": "IPY_MODEL_af1e70f287cf45b8b8426ccdd66d5351", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "af1e70f287cf45b8b8426ccdd66d5351": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/circuits_advanced/06_building_pulse_schedules.ipynb b/docs/tutorials/circuits_advanced/06_building_pulse_schedules.ipynb deleted file mode 100644 index 20ba9aa7d2e6..000000000000 --- a/docs/tutorials/circuits_advanced/06_building_pulse_schedules.ipynb +++ /dev/null @@ -1,923 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Building Pulse Schedules\n", - "\n", - "Pulse gates define a low-level, exact representation for a circuit gate. A single operation can be implemented with a pulse program, which is comprised of multiple low-level instructions. To learn more about pulse gates, refer back to the documentation [here](05_pulse_gates.ipynb). This page details how to create pulse programs.\n", - "\n", - "Note: For IBM devices, pulse programs are used as subroutines to describe gates. Previously, some devices accepted full programs in this format, but this is being sunset in December 2021. Other providers may still accept full programs in this format. Regardless of how the program is used, the syntax for building the program is the same. Read on to learn how!\n", - "\n", - "Pulse programs, which are called `Schedule`s, describe instruction sequences for the control electronics. We build `Schedule`s using the Pulse Builder. It's easy to initialize a schedule:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:49.766612Z", - "iopub.status.busy": "2023-08-25T18:25:49.765953Z", - "iopub.status.idle": "2023-08-25T18:25:50.229794Z", - "shell.execute_reply": "2023-08-25T18:25:50.229089Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "ScheduleBlock(, name=\"my_example\", transform=AlignLeft())" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit import pulse\n", - "\n", - "with pulse.build(name='my_example') as my_program:\n", - " # Add instructions here\n", - " pass\n", - "\n", - "my_program" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can see that there are no instructions yet. The next section of this page will explain each of the instructions you might add to a schedule, and the last section will describe various _alignment contexts_, which determine how instructions are placed in time relative to one another." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# `Schedule` Instructions\n", - "\n", - " - [delay(duration, channel)](#delay)\n", - " - [play(pulse, channel)](#play)\n", - " - [set_frequency(frequency, channel)](#set_frequency)\n", - " - [shift_phase(phase, channel)](#shift_phase)\n", - " - [acquire(duration, channel, mem_slot, reg_slot)](#acquire)\n", - "\n", - "Each instruction type has its own set of operands. As you can see above, they each include at least one `Channel` to specify where the instruction will be applied.\n", - "\n", - "**Channels** are labels for signal lines from the control hardware to the quantum chip.\n", - "\n", - " - `DriveChannel`s are typically used for _driving_ single qubit rotations,\n", - " - `ControlChannel`s are typically used for multi-qubit gates or additional drive lines for tunable qubits, \n", - " - `MeasureChannel`s are specific to transmitting pulses which stimulate readout, and\n", - " - `AcquireChannel`s are used to trigger digitizers which collect readout signals.\n", - " \n", - "`DriveChannel`s, `ControlChannel`s, and `MeasureChannel`s are all `PulseChannel`s; this means that they support _transmitting_ pulses, whereas the `AcquireChannel` is a receive channel only and cannot play waveforms.\n", - "\n", - "For the following examples, we will create one `DriveChannel` instance for each `Instruction` that accepts a `PulseChannel`. Channels take one integer `index` argument. Except for `ControlChannel`s, the index maps trivially to the qubit label." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:50.235098Z", - "iopub.status.busy": "2023-08-25T18:25:50.233791Z", - "iopub.status.idle": "2023-08-25T18:25:50.238998Z", - "shell.execute_reply": "2023-08-25T18:25:50.238396Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.pulse import DriveChannel\n", - "\n", - "channel = DriveChannel(0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The pulse `Schedule` is independent of the backend it runs on. However, we can build our program in a context that is aware of the target backend by supplying it to `pulse.build`. When possible you should supply a backend. By using the channel accessors `pulse._channel()` we can make sure we are only using available device resources." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:50.243572Z", - "iopub.status.busy": "2023-08-25T18:25:50.242406Z", - "iopub.status.idle": "2023-08-25T18:25:50.455696Z", - "shell.execute_reply": "2023-08-25T18:25:50.455031Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "5\n" - ] - } - ], - "source": [ - "from qiskit.providers.fake_provider import FakeValencia\n", - "\n", - "backend = FakeValencia()\n", - "\n", - "with pulse.build(backend=backend, name='backend_aware') as backend_aware_program:\n", - " channel = pulse.drive_channel(0)\n", - " print(pulse.num_qubits())\n", - " # Raises an error as backend only has 5 qubits\n", - " #pulse.drive_channel(100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `delay`\n", - "\n", - "One of the simplest instructions we can build is `delay`. This is a blocking instruction that tells the control electronics to output no signal on the given channel for the duration specified. It is useful for controlling the timing of other instructions.\n", - "\n", - "The duration here and elsewhere is in terms of the backend's cycle time (1 / sample rate), `dt`. It must take an integer value.\n", - "\n", - "To add a `delay` instruction, we pass a duration and a channel, where `channel` can be any kind of channel, including `AcquireChannel`. We use `pulse.build` to begin a Pulse Builder context. This automatically schedules our delay into the schedule `delay_5dt`." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:50.460776Z", - "iopub.status.busy": "2023-08-25T18:25:50.459477Z", - "iopub.status.idle": "2023-08-25T18:25:50.464812Z", - "shell.execute_reply": "2023-08-25T18:25:50.464068Z" - } - }, - "outputs": [], - "source": [ - "with pulse.build(backend) as delay_5dt:\n", - " pulse.delay(5, channel)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That's all there is to it. Any instruction added after this delay on the same channel will execute five timesteps later than it would have without this delay.\n", - "\n", - "## `play`\n", - "\n", - "The `play` instruction is responsible for executing _pulses_. It's straightforward to add a play instruction:\n", - "\n", - "```\n", - "with pulse.build() as sched:\n", - " pulse.play(pulse, channel)\n", - "```\n", - "\n", - "Let's clarify what the `pulse` argument is and explore a few different ways to build one.\n", - "\n", - "### Pulses\n", - "\n", - "A `Pulse` specifies an arbitrary pulse _envelope_. The modulation frequency and phase of the output waveform are controlled by the `set_frequency` and `shift_phase` instructions, which we will cover next.\n", - "\n", - "The image below may provide some intuition for why they are specified separately. Think of the pulses which describe their envelopes as input to an arbitrary waveform generator (AWG), a common lab instrument -- this is depicted in the left image. Notice the limited sample rate discritizes the signal. The signal produced by the AWG may be mixed with a continuous sine wave generator. The frequency of its output is controlled by instructions to the sine wave generator; see the middle image. Finally, the signal sent to the qubit is demonstrated by the right side of the image below.\n", - "\n", - "**Note**: The hardware may be implemented in other ways, but if we keep the instructions separate, we avoid losing explicit information, such as the value of the modulation frequency.\n", - "\n", - "![alt text](pulse_modulation.png \"Pulse modulation image\")\n", - "\n", - "There are many methods available to us for building up pulses. Our `library` within Qiskit Pulse contains helpful methods for building `Pulse`s. Let's take for example a simple Gaussian pulse -- a pulse with its envelope described by a sampled Gaussian function. We arbitrarily choose an amplitude of 1, standard deviation $\\sigma$ of 10, and 128 sample points.\n", - "\n", - "**Note**: The amplitude norm is arbitrarily limited to `1.0`. Each backend system may also impose further constraints -- for instance, a minimum pulse size of 64. These additional constraints, if available, would be provided through the `BackendConfiguration` which is described [here](08_gathering_system_information.ipynb#Configuration)." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:50.468209Z", - "iopub.status.busy": "2023-08-25T18:25:50.467549Z", - "iopub.status.idle": "2023-08-25T18:25:50.471269Z", - "shell.execute_reply": "2023-08-25T18:25:50.470595Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.pulse import library\n", - "\n", - "amp = 1\n", - "sigma = 10\n", - "num_samples = 128" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Parametric pulses\n", - "Let's build our Gaussian pulse using the `Gaussian` parametric pulse. A parametric pulse sends the name of the function and its parameters to the backend, rather than every individual sample. Using parametric pulses makes the jobs you send to the backend much smaller. IBM Quantum backends limit the maximum job size that they accept, so parametric pulses may allow you to run larger programs.\n", - "\n", - "Other parametric pulses in the `library` include `GaussianSquare`, `Drag`, and `Constant`.\n", - "\n", - "\n", - "**Note**: The backend is responsible for deciding exactly how to sample the parametric pulses. It is possible to draw parametric pulses, but the samples displayed are not guaranteed to be the same as those executed on the backend." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:50.474488Z", - "iopub.status.busy": "2023-08-25T18:25:50.474052Z", - "iopub.status.idle": "2023-08-25T18:25:51.428053Z", - "shell.execute_reply": "2023-08-25T18:25:51.426196Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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IiIiIiIiIoMwuUBEREREREREBmFygYiIiIiIiIgMwuQCERERERERERmEyQUiIiIiIiIiMgiTC0RERERERERkECYXiIiIiIiIiMggTC4QERERERERkUGYXCAiIiIiIiIigzC5QEREREREREQGYXKBiIiIiIiIiAzC5AIRERERERERGYTJBSIiIiIiIiIyCJMLRERERERERGQQJheIiIiIiIiIyCBMLhARERERERGRQZhcICIiIiIiIiKDMLlARERERERERAZhcoGIiIiIiIiIDMLkAhEREREREREZhMkFIiIiIiIiIjIIkwtEREREREREZBAmF4iIiIiIiIjIICaVHQARERGVr4cpEshFyfeztRCwNCv7eIiIiOjVw+QCERHRS06uAGLuGessF6VILABASiYg0VHmYK2Ai30pGyYiIqJXDpMLREREL4GneUBisvbRjEKUPoFQJAHoajYtywjZudpLzU0FXB2YeCAiInqdMLlARET0glAUkSQoUAA5ubr6EVS8AjlQINcej5ERIFfoTi4Yc8YnIiKiVw6TC0RERC+ItCwJEp+8/E/eWTkS3IjXPkxDaiJQq5qigiMiIiKi8vbyf4MhIiIiIiIiokrFngtEREQV6Em6BClZ2ocTyOUVHEwlyJdLcOu+7t82argoYPTijP4gIiIiPTG5QEREVIEK5EBu3mv89Cxe8/MnIiJ6RTG5QEREVMbyC55NzqhNgYIP1kXJywckOi6R1ATs1UBERPSCYnKBiIiojN1PMUJGNp+CS+P2fe0TQQKAt6scZqYVGAwRERHpjRM6EhEREREREZFB2HOBiIioFPILAB0jH3QOiSDD5MsBowLtZSZGgBF/MiEiIqo0TC4QERGVQuwjI+Tlc+hDRbr7SPeQCXcnBWwtmdUhIiKqLMzxExEREREREZFBmFwgIiIiIiIiIoNwWAQREZEOt+/rzsHnF3BIxIvkYaoEj9O1vyb2VgIO1hwyQUREVJ6YXCAiItLhaR4TCC+L/AIJ8nWUFXCGTSIionLHYRFEREREREREZBD2XCAioteWQgFk5VZ2FFTe8vIlyMjR3ntBagyYmVZwQERERK8gJheIiOi1lS8venlDejWkZUmQlqX9dba1FHB3UlRwRERERK8eDosgIiIiIiIiIoMwuUBEREREREREBuGwCCIieqXl5AFP0rXn0uXsDf/ay84F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/twEi8V5//0piDp9GqNGjni2FGX1Gnj8OAlHDh/GppDQIud3WLx4MSZOnIBpH0xFq9at0fyt5rC2scGTJ49xJioKFy5cQKvWrVX127QNwLFjxzB92gdo3aYtEhMSsGPHL6hRowayc3JU9aytrdGpUyf89NN2SCQSuHu448SJE0gplFwBgLi4OIwbOwZBQUGoUcMbxibGOH7sOJ48eYIuXbqU6DoQERHRv5hcICIiek6zt97CggWfYePGjfjyy2WoVq0apn4wDYmJCXonBsqiDX01bdoUoZu3YP336/DzTz8hOzsbjk5OqF+vPvr376+qV69ePUyaNBk7dvyCyFOnoFAocODg76hWwuRC1apVsWXrNqxZ8y0OHjiArKwsODs7o1Wr1sXO7+Dg6IjQzVuwc+cO/PHHH1i37js8ffoUdnZ2qFOnDhYvWYLOnf99yO/VqxeePHmMnTt3IjIyEjVq1MB/Fi/BkSOHcf78ebW2Z8/5CAUFBdix4xeYmpoiKKgzpk2bjv79+qrquLi4oEuXrjh79gz2798PYxMTVPfywrIvl6Njx04lug5ERET0L4koqv/i/0tPT4etrS3uJSRyySYiIiIiIiKiF1R6ejrcq7khLS2tQp/fOecCERERERERERmEyQUiIiIiIiIiMgiTC0RERERERERkECYXiIiIiIiIiMggTC4QERERERERkUGYXCAiIiIiIiIigzC5QEREREREREQGYXKBiIiIiIiIiAzC5AIRERERERERGYTJBSIiIiIiIiIyCJMLRERERERERGQQE30qCSEAABkZGeUaDBERERERERGVnvK5XfkcX1H0Si4og6vt61OuwRARERERERGR4Z48eQJbW9sKO55E6JHOUCgUSExMhLW1NSQSSUXEVSbS09Ph4eGB+Ph42NjYVHY4RBWK9z+9znj/0+uO7wF6nfH+p9ddWloa3njjDaSkpMDOzq7CjqtXzwUjIyO4u7uXdyzlxsbGhh8s9Nri/U+vM97/9Lrje4BeZ7z/6XVnZFSxUyxyQkciIiIiIiIiMgiTC0RERERERERkkFc6uSCTyTB//nzIZLLKDoWowvH+p9cZ73963fE9QK8z3v/0uqus94BeEzoSEREREREREenySvdcICIiIiIiIqLyx+QCERERERERERmEyQUiIiIiIiIiMsgrmVzIycnBvHnzUKtWLZiZ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wtfXF5MmTcLff/+td5wluYbahISEqLr4x8XFqb1egYGBqnq65lxQDqcoKCjAokWLULNmTZibm6N27dpq53z8+HG0a9cONjY2sLe3x3vvvaczxoKCAqxduxYtWrSAjY0NzM3N0bBhQ3z99dcoKCjQ+9oAwMaNG6FQKDB48GCddX799Ve0aNECFhYWcHR0RL9+/bS+Bsr5L+Li4lTnrvzz/JCS3r17w9zcHOvXry9RvERERGWJwyKIiEhvHh4eAIBdu3bho48+KvbXWZlMhuDgYHz11VfYtm0bJk2apFFH+UA0duxY1bZZs2Zh+fLlkMlkaNu2LZycnPDgwQNcvnwZt27dUrUTHByMP//8E7dv30bnzp3h4uKiasPKykr1/3PmzMHSpUthamqKpk2bwtXVFZcuXcLKlSvx66+/4tSpU6hatapGbHv27MGaNWvQokULdOnSBVFRUfjhhx9w8+ZN7Ny5Ey1atIBcLkebNm0QGxuLM2fOoEuXLjh37hzq16+v1zXNyspCt27dcOLECVhaWqoSDLGxsdi2bRtsbW3RpEkT9OrVCy4uLti9ezeePHmi0f09MzMT27dvh42NDd555x3V9vv376NTp064evUq7O3tERgYCJlMhn/++Qffffcd3nzzTdSqVavYOEt7DQurWbMmgoODERoaCktLS/Tv319VVpLeLgMHDlQlELy9vREREYGRI0cCAKytrTF48GA0b94cnTt3xunTp7FlyxbcuXMHJ06cUJu/ICcnB927d0dYWBgcHBzQvHlzmJmZ4cyZM5g2bRrCwsKwZ88eGBnp91vM/v37AUAtUVLYd999hwkTJkAikaBNmzZwdXVFVFQUmjVrhh49eqjVdXFxQXBwMHbu3ImsrCwEBwerypycnNTqWllZwd/fHydPnsQ///yDGjVq6BUvERFRmRJERER6un37tjA3NxcAhLW1tQgODhbr168X0dHRoqCgQOs+MTExQiKRiAYNGmiUpaWlCQsLC2Fvby9ycnKEEELk5OQImUwmrK2txT///KNWPz8/X5w4cUJtW3BwsAAgwsLCtB7/l19+EQBEvXr1xM2bN1XbFQqFmDdvngAg3nnnHa1tGhkZif3796u2p6eni3r16gkAok6dOmLYsGEiLy9PVf7pp58KAOK9997TGos2o0aNEgBE27ZtxaNHj9TKHjx4IKKiolR///jj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", - "text/plain": [ - "
" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gaus = pulse.library.Gaussian(num_samples, amp, sigma,\n", - " name=\"Parametric Gaus\")\n", - "gaus.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Pulse waveforms described by samples\n", - "\n", - "A `Waveform` is a pulse signal specified as an array of time-ordered complex amplitudes, or _samples_. Each sample is played for one cycle, a timestep `dt`, determined by the backend. If we want to know the real-time dynamics of our program, we need to know the value of `dt`. The (zero-indexed) $i^{th}$ sample will play from time `i*dt` up to `(i + 1)*dt`, modulated by the qubit frequency." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.432148Z", - "iopub.status.busy": "2023-08-25T18:25:51.431477Z", - "iopub.status.idle": "2023-08-25T18:25:51.570643Z", - "shell.execute_reply": "2023-08-25T18:25:51.569849Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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23379omePXsKe3t7IZVKhb29vWjatKmYPn26uHz5ssZ9FeNyCSGEGDFihLL9hQsXNG4zadIk5TZ///23xm3Wr18vAAh/f/9ixaFJbm6u2Llzp3jvvfdEjRo1hLGxsTA2NhY1atQQvXr1EqtWrRIJCQlq7dLS0sRXX30l6tatK2QymahRo4YYO3asePbsmfD39xcARFBQkFq7wMBA0bx5cyGTyYSNjY0YOHCguH//vsbX8sqVK2LKlCmiefPmwt7eXshkMuHk5CR69eoljh07prbvTz75RAAQmzZt0vn8FbEqfiQSiTA3NxdOTk6iZ8+eYtGiReLp06cF7uPmzZvi3XffFebm5sLU1FR06NBBHD9+XAQFBWl8vQq6PgpRUVFiyJAhwtHRURgbG4t33nlHLFiwQGRnZ2t9T9y5c0f4+fkJW1tbYWBgoPYeBCCcnJw0Hi8wMFB07txZWFpaCiMjI1GnTh0xffp08eLFC7VtC3rfCaH9Pevl5VVgO23yvz6afl59jyuOX5Q2QuS9F9atWyc6dOggrKysRKVKlYSdnZ3o0aOHOHTokMbYCjunf/75R/Tv31/Y2toKY2Nj0bBhQ7F06VKRk5NTpGsghBDPnj0T48aNEw4ODkImkwlXV1exePFikZubq/W1vXjxoujatauwtLQUEomkWNefiIiIiEgXEiGKMKU/0WvMzc0NmZmZ+Pfff2FoaFje4RAREREREb01SndgP1EZefbsGf755x9s2bKFiQUiIiIiIqIyxp4LRERERERERKQXnXou5ObmIjo6Gubm5pBIJKUdExEREREREREVgxACycnJqFatGgwMSmyByELplFyIjo4ukaXoiIiIiIiIiKj0RUVFoXr16mV2PJ2SC+bm5gCAm7fClf9PRERERERERK+X5ORkvONav8yf3XVKLiiGQpibm8PCwqJUAyIiIiIiIiIi/ZT1lAZlNwCDiIiIiIiIiN5ITC4QERERERERkV6YXCAiIiIiIiIivTC5QERERERERER6YXKBiIiIiIiIiPTC5AIRERERERER6YXJBSIiIiIiIiLSC5MLRERERERERKQXJheIiIiIiIiISC9MLhARERERERGRXphcICIiIiIiIiK9MLlARERERERERHphcoGIiIiIiIiI9MLkAhERERERERHphckFIiIiIiIiItILkwtEREREREREpBcmF4iIiIiIiIhIL0wuEBEREREREZFemFwgIiIiIiIiIr0wuUBEREREREREemFygYiIiIiIiIj0wuQCEREREREREemFyQUiIiIiIiIi0guTC0RERERERESkFyYXiIiIiIiIiEgvTC4QERERERERkV6YXCAiIiIiIiIivVQq7wCIiIiodKW8BCA016VlSJCdq7nOWAoYVdLcUFoJkElLJj4iIiKq+JhcICIiquByBZCYKtFa/yTeALlaEgjFZSoXsDTRkngwFDCTl+zxiIiI6PXG5AIREVEFJwQQ/bxsRzqmpkuQmq45oWEmFzCTl3A2g4iIiF5rTC4QERFVAOmZwINYw/IOQyepLyW49UhzrFJDAZeqTDwQERG9aZhcICIiqgCEAHJyyjsK3RQUq4H20RtERERUgTG5QERE9JpISJUg5sWbvZBTVrYEN6O098CoXz2HCQgiIqIKiMkFIiKi14QQKPGJF19Hb8M5EhERvW2YXCAiIipDqS/zln/U5GUmv7J/nqT9GlibCVSqGNNOEBERvXWYXCAiIipDKekSPEt6s4c+6CM2Qfu1MZfnMLlARET0muKnGyIiIiIiIiLSC3suEBERlbDnyRKtQxzSM8s4mDdIbKIBDLV8LWJjlgu5rGzjISIiov8wuUBERFTCUl9KkJzG+RNKWkHX1MxYArlMlGE0RERElB+HRRARERERERGRXthzgYiIqBgSUiXI0bKkYmZ22cZCQMpLCbK1vB5yIwETDpkgIiIqVUwuEBERFUNcogSZWRz68LpISJEA0Px6VLbKhQmHTBAREZUqDosgIiIiIiIiIr2w5wIREZEWgl92vxlEwa+lhB1QiIiI9MbkAhERkRY3HhqWdwhUAuISDRCXqLnO0lSgup2WyRqIiIhIZxwWQURERERERER6YXKBiIiIiIiIiPTCYRFERPTWysnNW1KS3l4ZWcDzZM33gKEBYGXKiTeIiIh0weQCERG9tbJzgCcv2InvbfYyU4InLzQnF4yNBJMLREREOuInKiIiIiIiIiLSC5MLRERERERERKQXDosgIqI3WupL4GmC5lx6Lnu8UwEysiW490TzvSORALWqcAlLIiIiBSYXiIjojZaTK0F6BidtpKITudB670jY95OIiEgF/2kkIiIiIiIiIr2w5wIREVV4Obl5P9rqiEqcADKztVdLDfOGThAREb0tmFwg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AwAQevmysiwnJwe7du1U2S4lJQXZ2dkqZXXr1oWBgQEyMzOLcjpERET0GuGcC0RERGVMKpWiYcOGCA0NhZGRERo0aKBS7+Hujk2bNgEAmniWXHJBKpVi2bJlGDduLEYMH45OnTvDs4kn5HI5YmNjERwSjJiYGLRr317Zpn2HDvjl558xe9YsuHu4I+JOBA4ePKA2VKNOnTpwc3PDTz8FIDEpEZYWljh85LDKHA0AcOHCBSyY/z26+vjAyckJOdk5CAwMhIGBAbp06VJi50pERERli8kFIiKicuDRpAlCQ0Pxzv8fBqFS59EEmzZtgqmpKerVq1+ix3VydsaO3//Atq1bcfz43zh96hSysrJga2uLRo0bY+zHY9HB678lLkeP/ggv09Nx6NAh/PXXEbi6voPlK1YgICBAbd/fz1+Ab7+dh/W//gpzc3P49e2L5s1bYOzHY5Tb1K9XD23atMWJkBDExsbC2NgY9erVw8r/+z+4ubmX6LkSERFR2ZEI8WpnR3VJSUmwtLTEo8fRyjGVRERERERERPR6SUpKQnXHakhMTCzT53fOuUBEREREREREemFygYiIiIiIiIj0wuQCEREREREREemFyQUiIiIiIiIi0guTC0RERERERESkFyYXiIiIiIiIiEgvTC4QERERERERkV6YXCAiIiIiIiIivTC5QERERERERER6YXKBiIiIiIiIiPTC5AIRERERERER6aWSLhsJIQAAycnJpRoMERERERERERWf4rld8RxfVnRKLiiCe8e1fqkGQ0RERERERET6e/78OSwtLcvseBKhQzojNzcX0dHRMDc3h0QiKYu4SkRSUhJq1KiBqKgoWFhYlHc4RGWK9z+9zXj/09uO7wF6m/H+p7ddYmIiatasifj4eFhZWZXZcXXquWBgYIDq1auXdiylxsLCgn9Y6K3F+5/eZrz/6W3H9wC9zXj/09vOwKBsp1jkhI5EREREREREpBcmF4iIiIiIiIhIL290ckEmk+Gbb76BTCYr71CIyhzvf3qb8f6ntx3fA/Q24/1Pb7vyeg/oNKEjEREREREREZE2b3TPBSIiIiIiIiIqfUwuEBEREREREZFemFwgIiIiIiIiIr28kcmF9PR0zJ49G/Xq1YOxsTGqVauGkSNH4vHjx+UdGpFe0tLSsHfvXowaNQr169eHsbExTE1N4e7ujnnz5iElJUWtjUQiKfSnU6dO5XA2RMXj7e1d4P18+PDhQvfRpUsX5faPHj0qg6iJSs7FixcxcOBAVKtWDVKpFFZWVmjfvj3Wr1+PV6fSCg8Px7JlyzB48GC4uLgo7/vIyMjyCZ5IB5cvX8aCBQvQr18/VK9eXXnfFmbDhg1o0aIFzMzMYGNjgx49euDMmTNat8/IyMDChQvh6ekJMzMzyGQy1KpVCx999BHu3btXkqdEpLOi3v/79++Hv78/GjduDDs7O0ilUtjb26NHjx4IDAzU+bjffvut8li//fZbsWJ/4yZ0fPnyJTp27Ihz586hatWqaN++PSIjI3HhwgVUrlwZ586dQ+3atcs7TKJiWbt2LT766CMAwDvvvINGjRohKSkJZ86cQXJyMlxdXRESEgJ7e3tlm+HDh2vd34EDB/Ds2TPMnj0bc+fOLe3wiUqEt7c3QkJC0L9/f5iZmanVT5kyBY0bN9bafsOGDRgxYgQkEgmEEIiKikL16tVLM2SiErNr1y4MGjQIOTk58PT0RJ06dRAXF4eTJ08iOzsbQ4YMwZYtW5Tbf/bZZwgICFDbz/379+Hs7FyGkRPpzs/PD/v27VMrL+ixRXGvy+Vy+Pj44OXLl/j7778hhMDOnTvh5+ensn3+ZwYrKyu0adMGxsbGCA0NRWRkJMzNzREUFISmTZuW9OkRFaio9/97772H3bt3o2HDhqhZsybMzc0RGRmJ8+fPAwC++OILfP/99wUeMzw8HO7u7sjMzIQQAps3b8awYcOKHrx4w3z11VcCgGjdurVITk5Wli9ZskQAEF5eXuUXHJGeNmzYIMaMGSNu3LihUh4dHS2aNGkiAIjBgwfrtK/4+Hghk8kEAHH79u3SCJeoVHh5eQkA4v79+0VuGxsbK2xsbISPj49wcnISAERUVFTJB0lUCrKysoS9vb0AILZs2aJSd+PGDWFjYyMAiOPHjyvL165dK2bMmCF27twpIiMjRf369Yv9/iEqKwsWLBCzZs0S+/fvFzExMcrPK9ocPXpUABC2trYqn2nOnDkjjIyMhJWVlYiPj1dpExAQIACI5s2bi4SEBGV5dna2mDhxogAgOnToUOLnRlSYot7/oaGh4tmzZ2rl586dE2ZmZkIikYjr169rbZ+bmys6dOggqlSpIvr06SMAiM2bNxcr9jcquZCRkSEsLS0FABEaGqpW7+bmJgCIS5culUN0RKXrzJkzAoCQyWQiIyOj0O1/+eUXAUC0atWqDKIjKjn6JBeGDBkijI2NRUREBJMLVOGEhYUJAKJ+/foa6z/55BMBQCxcuFDrPphcoIqosIer7t27CwBi2bJlanWK98XixYtVyvv37y8AiG3btqm1efHihQAg5HK53rET6auw+78go0aNEgBEQECA1m0UzwS//fab8Pf31yu58EbNuXD69GkkJibCxcUFTZo0Uat/7733AAB//vlnWYdGVOrc3d0B5I0ffP78eaHbK8ZSffDBB6UaF9Hr4vDhw9i6dSu++uoruLi4lHc4REUmk8l02s7W1raUIyF6faSnp+P48eMA/vusn5+2z/+6vJ/4XqKKTiqVAgCMjIw01j958gTTp09H586dMXToUL2P90YlF65duwYA8PT01FivKL9+/XqZxURUVhQTD0mlUtjY2BS47cOHD3Hy5ElIpVIMGjSoLMIjKnHr1q3D+PHjMXHiRPz00094+PCh1m1TU1Mxbtw4uLq6Yvr06WUYJVHJqV27NlxcXBAeHo6tW7eq1N28eRO//fYbrK2t0bdv33KKkKjshYeHIyMjA5UrV9Y4f462z/8+Pj4AgKVLlyIxMVFZnpOTg9mzZwMARo0aVVphE5W6sLAw7NixA1KpFF27dtW4zSeffIL09HSsWrWqRI5ZqUT28ppQfLDUNjGXovzBgwdlFhNRWVFM2OXr61toNn7Lli0QQqB79+7MylOF9b///U/l96lTp2LWrFmYNWuW2razZ89GZGQkgoODtWbviV53hoaG2LhxI959910MHToUS5YsQd26dREbG4uTJ0+iQYMG2LBhQ6EJZqI3SWGf/01NTWFlZYX4+HgkJyfD3NwcADBs2DAcPnwY27dvh7OzM9q2bQtjY2NcvnwZT58+xbRp0zT+e0L0uvrzzz+xa9cuZGVl4eHDhzhz5gykUinWrFmjscdmYGAg/vjjD8ydOxd169YtkRjeqOSCYhk+ExMTjfWmpqYAgOTk5DKLiagsHDx4EOvWrYNUKsW3335b6PYcEkEVWYcOHTB69Gi0adMGVatWRVRUFHbu3In//e9/mD17NiwsLPDpp58qtw8NDUVAQAD8/f3h5eVVjpET6a9t27YICQlB3759ERoaitDQUAB5XV67du3KFbHorVPY538g7xkgISFBJblgaGiI3377DTVr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", - "text/plain": [ - "
" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import numpy as np\n", - "\n", - "times = np.arange(num_samples)\n", - "gaussian_samples = np.exp(-1/2 *((times - num_samples / 2) ** 2 / sigma**2))\n", - "\n", - "gaus = library.Waveform(gaussian_samples, name=\"WF Gaus\")\n", - "gaus.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Pulse library functions\n", - "\n", - "Our own pulse library has sampling methods to build a `Waveform` from common functions." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.575812Z", - "iopub.status.busy": "2023-08-25T18:25:51.574152Z", - "iopub.status.idle": "2023-08-25T18:25:51.694947Z", - "shell.execute_reply": "2023-08-25T18:25:51.694121Z" - }, - "scrolled": true - }, - "outputs": [ - { - "data": { - "image/png": "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UadOHWFpaSm8vLzExx9/LPLy8gye2/j4eDFw4EDh4uIizMzMSnX8iYiIiIhKQiJEBUw9T1SO+vfvjxMnTiAhIcGoV/wRERERERFR2Xrx3ylHVASlUomjR49i1qxZTCwQERERERFVEvZcICIiIiIiIiKTGDWho0qlQmJiIuzs7CCRSMo7JiIiIiIiIiIqBSEEMjMz4e7uDjOzihusYFRyITExER4eHuUdCxERERERERGVgXv37qF27doVtj2jkgvqsezXrsdxXDsRERERERHRCyozMxONfBtW+LO7UckF9VAIOzs72Nvbl2tARERERERERGSaip7SgG+LICIiIiIiIiKTMLlARERERERERCZhcoGIiIiIiIiITMLkAhERERERERGZhMkFIiIiIiIiIjIJkwtEREREREREZBImF4iIiIiIiIjIJEwuEBEREREREZFJmFwgIiIiIiIiIpMwuUBEREREREREJmFygYiIiIiIiIhMwuQCEREREREREZmEyQUiIiIiIiIiMgmTC0RERERERERkEiYXiIiIiIiIiMgkTC4QERERERERkUmYXCAiIiIiIiIikzC5QEREREREREQmYXKBiIiIiIiIiEzC5AIRERERERERmYTJBSIiIiIiIiIyCZMLRERERERERGQSJheIiIiIiIiIyCRMLhARERERERGRSZhcICIiIiIiIiKTMLlARERERERERCZhcoGIiIiIiIiITGJR2QEQERFR+UpKk0ApSt7OwVrAxqrs4yEiIqKXD5MLREREVZxSBcTdNzdYL0qRWACAtCxAYqDO2U4FV6dSrpiIiIheOkwuEBERVQFP84HEx/pHMwpR+gRCkQRgaLVPss2Qk6e/Vm4p4ObMxAMREdGrhMkFIiKiF4SqiCSBQgXk5hnqR1DxFEpAodQfj5kZoFQZTi6Yc8YnIiKilw6TC0RERC+IJ9kSJKZW/Sfv7FwJrt/TP0xDaiHQoJaqgiMiIiKi8lb172CIiIiIiIiIqFKx5wIREVEFSs2QIC1b/3ACpbKCg6kEBUoJbjw0/N1GPVcVzF6c0R9ERERkJCYXiIiIKpBCCeTlv8JPz+IV338iIqKXFJMLREREZaxA8WxyRn0UKj5YFyW/AJAYOERSC7BXAxER0QuKyQUiIqIy9jDNDJk5fAoujZsP9U8ECQDebkpYWVZgMERERGQ0TuhIRERE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", - "text/plain": [ - "
" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "gaus = library.gaussian(duration=num_samples, amp=amp, sigma=sigma, name=\"Lib Gaus\")\n", - "gaus.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Regardless of which method you use to specify your `pulse`, `play` is added to your schedule the same way:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.698705Z", - "iopub.status.busy": "2023-08-25T18:25:51.698221Z", - "iopub.status.idle": "2023-08-25T18:25:51.811737Z", - "shell.execute_reply": "2023-08-25T18:25:51.811060Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABFcAAADeCAYAAAD4ru/+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABTnklEQVR4nO3dd1hT1/8H8HdACHsqAqKgoLjFWXGBuLUVlWrraHHv1jqqHa6q31ato9T61bqtq1oVtdpatQK1DrRi3eJEEQeI7E1yfn/4S77EJBAIBsH363l4Hjnnnns/9+YGcz85QyKEECAiIiIiIiIiohIxKusAiIiIiIiIiIjKMyZXiIiIiIiIiIj0wOQKEREREREREZEemFwhIiIiIiIiItIDkytERERERERERHpgcoWIiIiIiIiISA9MrhARERERERER6YHJFSIiIiIiIiIiPTC5QkRERERERESkByZXikkikUAikcDOzg7Jyckat1m4cCEkEgnmzp1r0NheJzExMZBIJPD39y9WO39/f0gkEsTExLySuErruH/99RdGjRqFZs2aoWrVqjA1NYWDgwM6duyILVu2QAhRKnEprmPBH6lUCicnJzRv3hxjxozB0aNHS+14ZWnu3LmQSCTYtGlTWYdSLBEREfjqq6/Qq1cvVKlSBRKJBB4eHlq3z8vLw5EjRzBx4kQ0bNgQFhYWMDc3R7169TBt2jQkJCRobZuSkoIvvvgCDRo0gIWFBczMzODt7Y3JkycjPj6+2LHLZDIsX74cjRo1grm5OapUqYIBAwbg+vXrxd5XYcrra0tEREREpKtKZR1AeZWSkoJly5Zh3rx5ZR0KlYEDBw5g3bp1qFOnDpo2bQp7e3vExcXhxIkTCA8Px++//47t27eX2vEsLS3x7rvvAnjxQJycnIwrV65gzZo1WLNmDZo3b47t27ejTp06pXbM0ubv74+IiAjcu3ev0ORDeTNp0iRcvHhR5+0jIiLQrVs3AICHhwd69OiBvLw8nD59GkuXLsW2bdsQHh4Ob29vlXbPnj1DmzZtcOvWLTg7O6NLly4AgLNnz+K7777Dzp07cfr0abi7u+sUh1wuR//+/REaGgo7Ozv06tULz549w+7du3Ho0CGEhYWhVatWOp9XSXh4eOD+/fsVIjlIRERERG82JldKQNF7ICQkBJMnT4a9vX1Zh0QGNnz4cEyZMgWurq4q5bdv30aHDh2wY8cODBo0CG+//XapHK9y5coav/W/ePEipkyZguPHj6NDhw44d+4cqlevXirHNLSJEyfi/fffh4uLS1mHUixdu3ZF//790bJlS7i5uaFBgwaFbm9kZIQBAwZg6tSpKsmLlJQUvPfee/jjjz8wbNgwnDp1SqXd119/jVu3bqF3797YuXMnzMzMAADZ2dkYNGgQQkNDMXv2bGzevFmnuDds2IDQ0FDUrl0bJ06cQNWqVQEAe/bswbvvvovBgwfj+vXrqFSJ/00QERERERWFw4JKwMjICKNHj0ZqaiqWLFlS1uFQGahfv75aYgUAvLy8MH78eADA8ePHX3kcTZo0wZEjR9C1a1c8ffoUkyZNeuXHfFUqV66MunXrwtbWtqxDKZbFixfjyy+/RNeuXeHg4FDk9gEBAdi5c6darxBbW1ts2LABAHD69Gncv39fpf6vv/4CAHz++efKxAoAmJmZYdasWQCAc+fO6Rz3smXLlPErEisAEBQUhN69e+P27dvYv3+/zvsjIiIiInqTMblSQp999hnMzc2xYsUKJCYm6tTm8ePHWLx4Mfz8/FCtWjWYmprC2dkZ/fr10/pQ5OHhAYlEAgBYuXIlGjZsCHNzc9SsWROLFy9WdqePiorCO++8AwcHB1hZWSEwMFDt4UxBCIEdO3YgICAA9vb2MDMzQ7169TB37lxkZmaW4GoULjU1FZMmTUL16tWVx1q+fDnkcnmx9nPt2jUMHjwYLi4uMDU1RbVq1fDhhx8iOjpaa5vr169jxIgR8PDwUM5V0rZtWyxZsgT5+flFHjMlJQUdOnSARCLBRx99pNPwBRMTEwCAqamp7ienB2NjY/zwww+QSCTYt28fHjx4oKzbtGlTofP/aJprpuB8OampqZgyZQpq1qwJExMTfPLJJwCA5ORkrFixAt26dYO7uzukUikcHR3RvXt3HD16VOUYiv1FREQAAGrWrKkyh4xCYfNyJCYm4tNPP0Xt2rVhZmYGBwcHdO/eHUeOHNF4Xop5T2QyGRYtWoQ6depAKpWievXqmDFjBnJycnS4sobn6uqKKlWqAAAePXqkUieVSots7+joqNNx7t27h+vXr8Pc3By9evVSq1cMQfv111912p/CgQMH4OvrCwsLCzg6OiIoKAg3b95U2y48PBwSiUT5N6rg/VCRhowRERER0ZuDyZUScnFxwdixY5GWloZvv/1Wpzb79+/HjBkz8PTpUzRu3Bh9+/aFq6srQkND0bZtW60PigAwefJkfPrpp3B3d0fnzp2RmJiIGTNmYO7cuTh58iTat2+PR48eoUuXLnBxccGBAwfQqVMnZGVlqexHLpdj8ODBGDRoEM6dOwcfHx/07NkTGRkZ+Oqrr9CxY0e1NgUnVS2unJwcBAQE4KeffkKrVq3QpUsX3L9/H1OmTMHw4cN13s+ff/6JFi1aYPv27XBxcUFQUBCcnJywZcsWtGjRAidOnFBr88svv6Bp06bYsGEDLCws0LdvXzRv3hyxsbH49NNPkZ6eXugxnz59Cn9/f5w4cQKzZ8/GihUrirwGsbGxWL16NQCgZ8+eOp+fvmrXro3mzZtDCKFMYugrKysLfn5+2LRpE3x8fNC7d2/lELgzZ87g448/xs2bN+Ht7Y2+ffvC29sbR44cQbdu3ZQ9MADAysoKwcHByt4RQUFBCA4OVv4UJS4uDq1atcKSJUuQm5uLPn36oGnTpjh27Bi6deuG5cuXa207aNAgLFiwAN7e3ujatSvS0tKwePFijBgxQm1bxQN/WT7cJycnIykpCQDg7OysUte1a1cALybMzs7OVpZnZ2dj/vz5AKDxvDRRzBHTsGFDZTKwoGbNmgEALl26pHPsq1evRmBgICIjI9GyZUt06dIF58+fR6tWrXDnzh2VbZ2dnREcHAxLS0sAULkfFIkdIiIiIqJyRVCxABDGxsZCCCGePHkiLCwshKWlpYiPj1du88033wgAYs6cOSptL126JK5cuaK2z8OHDwtTU1Ph6ekp5HK5Sp27u7sAIFxdXcXt27eV5devXxdSqVRYWFgIDw8PsWrVKmVdTk6OCAgIEADEhg0bVPa3ePFiAUD4+/uLx48fq7QZMWKEACBmzJih0ubevXsCgCjO7VKwTePGjUVCQoKy7vbt28LV1VUAEKGhoSrt/Pz8BABx7949ZVl6erqoWrWqACB++OEHle2XLVsmAAg3NzeRlZWlLL9586YwMzMTlSpVEtu2bVNpI5fLxR9//CGys7O1HvfevXvCy8tLSCQS8f3332s9z1OnTong4GAxZMgQERAQIExNTYWRkZFYsGCBrpeqUIrr6O7uXuS2I0eOFADE559/rizbuHGjxntRQdP1Lvja+fr6iqSkJLV2d+/eFadPn1Yrj4qKEnZ2dsLGxkakpaUVeayC5syZIwCIjRs3qpS//fbbAoAYNGiQyMnJUZafOHFCWFhYCGNjY3HhwgWVNor469Wrp3Kf3717V9jZ2QkAKu8nIYQICwvT+Vpr8/jxY732sWDBAgFANGrUSK0uPT1ddOzYUQAQzs7OIjAwUAQGBgpnZ2dhZ2cnlixZovNxQkJCBADRt29fjfXJyckCgHBwcNBpfzExMcLMzEyYmJiIw4cPK8tzc3PF4MGDla/Hy6+t4u8bEREREVF5x54reqhatSrGjRuHjIwMLFq0qMjtGzVqpHGyy27duqF///64c+cOrly5orHtvHnz4Onpqfy9bt266NmzJzIzM+Hm5oaxY8cq60xNTZVzbxTsxZCfn4/FixfD0tISP//8s8o346amplixYgWcnZ2xZs0alSE7JiYm8Pb2Vlu9RFdLlixB5cqVlb97enoq54j44Ycfimy/a9cuPH36FL6+vpgwYYJK3eTJk9G8eXM8fPgQe/bsUZYvX74c2dnZGDlyJAYNGqTSRiKRoGvXrlqHWVy9ehVt27ZFTEwMtmzZgo8++khrbHfu3MHmzZuxdetWHD9+HDKZDPPmzcO0adOKPK/SprjGip4PpeH777+HnZ2dWnnNmjXRunVrtfKmTZtiwoQJSE1NRVhYmN7Hv3v3Lg4ePAgrKyusWLFCZahVu3btMHbsWMhkMqxcuVJr/AXv85o1a2LIkCEAoNbbycLCAt7e3irvM0O6cOECFixYAAAa/55YWlri0KFDGDJkCJ48eYL9+/dj//79ePLkCZo2bYp27drpfCxFry0LCwuN9YoeJWlpaTrtb8OGDcjOzsbAgQOVKyEBL/52hISEaD0OEREREVFFweSKnmbMmAFLS0usWrUKT58+LXL7nJwc7N+/H19++SVGjx6NoUOHYujQobh8+TIA4NatWxrbKYYEFFSrVq0i6x4/fqwsi4qKUi7nWnACSwVzc3M0b94cSUlJKnFUq1YNN27cwI0bN4o8v5c5ODgol4wtaODAgQCAU6dOFTn3iuIhePDgwRrrNT0sHzt2DAAwZsyYYsV75swZdOjQAUlJSQgNDdV6zILHFkIgJycH0dHR+OyzzzBv3jz4+fmVapJDF+L/54MpyfAtTVxcXNCiRQut9TKZDEeOHMHcuXMxZswY5b2sSKpou5eL4++//wYAdO/eXeNksR988AEA9UQJ8OLBvmPHjmrliuWqC743AKBVq1a4ceMG/vzzT73jLq6nT5+iX79+yM7OxieffIIePXqobfPgwQO0atUKv//+O3766SfEx8cjPj4emzdvxsWLF5VD2MqC4rjvv/++Wp2jo6PGv1FERERERBUJ19jUU5UqVTBhwgQsXrwYCxcuLHT+h8uXL6N3794qk4e+TNs3xdWqVVMrs7KyKrKu4MSdiuMePXq0yAfwZ8+elbinSkHu7u4ay21tbWFnZ6ecY6KwiTgVE3tqmwtDUR4XF6csi42NBYBi90L44IMPkJ+fj507dxZrGWVTU1PUqVMHCxYsgIODA6ZOnaqcp8VQnj17BgA6rVijixo1amite/jwId5++23l3B2a6NrroTAlee0VnJ2dYWxsrFZubW0NAK/NpLZpaWno2bMnYmJi0L9/fyxdulTjdsHBwbhy5QpCQ0PRp08fZfmHH34IKysrBAUFYdq0aYiMjCzymIq/D9omsM7IyADwv2tVFMXrpO39zklqiYiIiKiiY8+VUvDpp5/CysoKq1evVvs2XEEIgQEDBiAmJgZjx47Fv//+i9TUVMjlcggh8Pnnnyu308TISPtLVVhdQYoeIl5eXioTSGr60XXVkddBafXUAP7Xo2b27Nl48uRJifah6E1h6GVsL1y4AODFMtG6KqzXUMHlfl82cuRIXLx4EUFBQYiMjERycjJkMhmEEPjxxx8BaL+XS1Nhr72u74uylJ2djd69eyMqKgpdu3bF1q1bNcYdGxuL8PBwSKVSvPPOO2r1gYGBMDU1xblz51Qmu9VGkTh7+PChxnpFubZkCRERERERqWLPlVJQuXJlfPTRR/jmm2/wzTffwNXVVW0bxbCaFi1aYNWqVWr1d+/efeVxurm5AXgxX4um5W5fhYLLAheUmpqK5ORkmJuba5zToyDF9dS2tLSiR07BHjzVq1fHrVu3cOfOHfj4+Ogc77x58+Dq6opFixYhICAA4eHhcHJy0rk98KLniJGRERISEorVTh+3bt3ChQsXYGRkhA4dOijLFXOUaFsZSdHDpzgyMjJw9OhRVK1aFTt37lTrHVKa93JJXvvyIj8/H++99x7Cw8PRpk0b7N27V+vy3Ypkh6WlpcbeOMbGxrC0tERSUhKSk5PVVhp6WZMmTQAAV65cQV5entqKQVFRUQCAxo0b63QuLi4uiI6Oxv379zUm97S9fkREREREFcXr/9VuOTF16lRYW1tjzZo1GocoKObfUCQ4Xq47evToK4+xZcuWsLW1RUREBJ4/f/7KjwcAiYmJGuew+PnnnwEAvr6+Gh8WC2rfvj0AYMeOHRrrt27dqrIdAHTu3BkAsGbNmmLHvHDhQkybNg3Xr19HQEBAsZMkJ06cgFwuN9jEqDKZDBMnToQQAkFBQSr3mIuLCwDg5s2bau1u3rypNflVmJSUFMjlcri4uKi9dnl5eQgNDdXYTpE4yM/P1/lYiklaDx8+jOTkZLV6Ta99eSCEwLBhw3DgwAH4+Pjg0KFDyklkNVEkS54/f4579+6p1d+5cwdJSUmwtLRUmTxam5o1a6JevXrIysrCoUOH1Op3794NABp7yWiiuP67du1Sq3v+/LnWZeZLck8QEREREb2OmFwpJY6Ojvj444+Rk5OD9evXq9V7eXnByMgIx48fV5noMzs7G2PHjjVIskMqlWL69OlIS0tDv379NPYwiIuLw5YtW9TK6tati7p165bouNOmTUNiYqLy93v37mHevHkAoLb6jyYDBgxA1apV8ffff6slS77//nv8888/qFatGoKCgpTln3zyCczMzLB27Vrs3LlTpY0QAkePHi10zo1vv/0WU6ZMwdWrV9GpUyflfCYF6zVNWHvu3DmMGjUKADBs2DC1eg8PD0gkEoSHhxd53rq4dOkSunbtiiNHjsDFxQXfffedSn3Lli1hYWGB33//HefPn1eWP3v2DCNHjixyMmFNnJycYGtriytXruDkyZPKcplMhhkzZmhM5AD/64USHR2t87Fq1aqFXr16IS0tDZMmTUJeXp6y7vTp01i1ahWMjY11uo+KcvbsWdStWxedOnXSe19F+eSTT7B161bUrVsXR44cKbL3Vs2aNZW9SMaMGYOUlBRlXXJysnLi5j59+qBSpf91SCzsnKZMmQIAmD59OuLj45Xle/fuxYEDB+Dl5YXAwECdzmfYsGGQSqXYtm2bcjJp4EWybfLkyco5XF5WknuCiIiIiOh1xGFBpWjq1KlYsWIFUlNT1eqcnJwwYsQIrF27Fk2aNEFAQADMzc1x4sQJyGQyDB061CBDdT777DPcuHEDW7ZsQb169dC0aVPUrFkTubm5iI6OxrVr19C4cWPlvCHAiwekkj78tG7dGrm5ufDy8kJAQADy8vLw559/IjMzE0OGDEG/fv2K3IelpSW2bduGd955B2PGjMGaNWtQp04d3LhxAxcuXICVlRV27NihMkdInTp1sHHjRnz44Yd4//33MW/ePDRu3BgpKSm4cuUKYmNjkZSUpHU5ZgBYunQpZDIZQkJC0KlTJxw/flw5F8306dMxc+ZMNG3aFB4eHsjNzcXdu3eVE7wOGDBAuRx2QYpkxsvDMIry7NkzDB06FMCLJEZKSgquXr2qTJC1bNkS27dvVxuSZmVlhWnTpmHevHlo164d/Pz8IJFIEBkZiXr16sHX1xenT58uViyVKlXC9OnT8eWXX8LPzw8BAQFwcHBAZGQknj59igkTJmhcGrl3797YvHkzBg0ahK5du8LW1hYAsG7dukKP9+OPP6J9+/b46aefEBERAV9fXyQkJCA8PBwymQxLly4t1tAvbTIzMxEdHa3TnCUFrVu3TnkOiuTP48ePVZaq/u9//4tmzZoBeDEXz/fffw/gxfC1Tz/9VON+P/vsM5WE5po1a9C5c2ccPXoUXl5eeOuttwC8WOEqMTERHh4eWLx4sc7nNHz4cPz2228IDQ1VJmCePXuGiIgImJubY+vWrSqJmsLUrFkTS5cuxcSJE9GtWzd06NABzs7OOHPmDJKSkjB48GBs27ZNrV3v3r0RERGBTp06oWPHjsqeNwsXLtTpuERERERErw1BxQJAGBsba62fPXu2ACAAiDlz5qjU5efni6VLl4r69esLMzMzUbVqVTF48GARExMj5syZIwCIjRs3qrRxd3cX2l4mbW2EEOLevXsCgPDz89PYdv/+/aJXr17CyclJmJiYCCcnJ9G8eXMxffp0cf78eY37Ks7tUvD4ycnJYvz48cLV1VWYmpoKb29vsWTJEpGfn6/Wzs/PTwAQ9+7dU6u7cuWKGDhwoKhataowMTERLi4uYsiQIeLGjRta47h48aIYMmSIqFatmvI827ZtK5YuXSry8vJ0Ou5HH30kAAgfHx+RmJgohBBixYoVol+/fqJWrVrC0tJSmJqaimrVqonAwEARGhqqMZZnz54JiUQi6tSpo/HcNSl47RU/JiYmonLlyqJZs2Zi9OjR4siRI0Iul2vdh1wuF99++63w8vISJiYmws3NTUydOlVkZGRoPO+i7h2FzZs3i6ZNmwoLCwvh6OgoAgMDxcWLF8XGjRs13v9CCLF8+XJRv359IZVK1e6pwu7nZ8+eialTpwpPT09hamoq7OzsRNeuXcUff/yhMTYAwt3dXWOdtvjCwsIKbaeNIu7CfsLCwtSOX5w2Crdv3xajRo0StWrVElKpVJibm4v69euLzz77THlvFuecFH+TGjRoIMzMzISjo6N49913xdWrV4t1DRRCQ0PFW2+9JczNzYW9vb0IDAwU169f1/ra5uXliZkzZwpPT09hYmJSoutPRERERPQ6kAhhgCU9iAh79+5FUFAQtm7disGDB5d1OERERERERFRKOOcKkYGEhYWhXr16yuWeiYiIiIiIqGJgzxUiIiIiIiIiIj1wQlsdyOVyPHr0CNbW1pBIJGUdDhERERERERFpIIRAWloaXF1dYWRkuME6TK7o4NGjR6hevXpZh0FEREREREREOoiNjYWbm5vBjsfkig6sra0BANdvRCv/TURERERERESvl7S0NNSr623wZ3cmV3SgGApkbW0NGxubMo6GiIiIiIiIiApj6Ck9uFoQEREREREREZEemFwhIiIiIiIiItIDkytERERERERERHpgcoWIiIiIiIiISA9MrhARERERERER6YHJFSIiIiIiIiIiPTC5QkRERERERESkByZXiIiIiIiIiIj0wOQKEREREREREZEemFwhIiIiIiIiItIDkytERERERERERHpgcoWIiIiIiIiISA9MrhARERERERER6YHJFSIiIiIiIiIiPTC5QkRERERERESkByZXiIiIiIiIiIj0wOQKEREREREREZEemFwhIiIiIiIiItIDkytERERERERERHpgcoWIiIiIiIiISA9MrhARERERERER6YHJFSIiIiIiIiIiPTC5QkRERERERESkByZXiIiIiIiIiIj0wOQKEREREREREZEemFwhIiIiIiIiItIDkytERERERERERHqoVNYBEBEREb1KSekSZOUWv525KWBvJUo/ICIiIqpwmFwhIiKicu/OYyPk5Ek01okS5keSADx+rrlOaiLg6SIv2Y6JiIiowmFyhYiIiMqFe0+MoC1PkpMvKXESpTDa9pmTL8HdJ5pHV0sA1HRm4oWIiOhNwuQKERERvRaEAOSFJEiycl9NAqUkhBzIytHcU0YiAWSF5FaMJC+2ISIiooqDyRUiIiJ6LeTJgFtxxmUdht6EAG7Eaj+P2tVkMOUnMCIiogqFqwUREREREREREemB35sQERGRwWRkA4+TNH+387oM+XnV7scbaR0W5GIvh6WZYeMhIiIi/TG5QkRERAYjBJCT+2ZPOJKrZVUj4M1JMBEREVU0TK4QERFRqZLJgXyZ5ro82ZudWClKnkyCnDzNGZZKxoAxB3QTERG9lphcISIiolKVnC7BEy1Df6hwjxK1Xzdnezkcbdi1hYiI6HXETz5ERERERERERHpgzxUiIiIqNpn8xY+2Oip9MjmQm6+5ztiIQ4aIiIjKEpMrREREVGxPkyRISufTvCElpBghIUVznb2VHK6OHDJERERUVvipiIiIiIiIiIhID0yuEBERERERERHpgcOCiIiISKNHiRJk5WpeOplLKr9eUrOMkPVY87Agc1PBIUNERESvGJMrREREpFFOvgTZWpIr9HqRyQCZloSXkREAMLlCRET0KnFYEBERERERERGRHthzhYiI6A2Wng0ILZ0auKRyxSCTA2lZmuskEsDKzLDxEBERVURMrhAREb3B4p4ZIZ/zp1RoObkSPIg31lhXyVjA241ZNCIiIn1xWBARERERERERkR6YXCEiIiIiIiIi0gOHBREREVVgcjnw6Ln271Jkcg4JepPJ5BI8fKb9/nB1kP//akNERERUGCZXiIiIKjABICWDCRTSTIjC7w8XBwMGQ0REVI7xuwgiIiIiIiIiIj0wuUJEREREREREpAcOCyIiIirnsnKAu081L7VLpI8bD7XfV7WqymAuNWAwRERErzEmV4iIiCoCUdYBUIXE+4qIiEgnHBZERERERERERKQH9lwhIiIqB3Lzgcwczau65OYbOBgiAGnZEuRoufcspAKm/JRJRERvEP63R0REVA5k5kgQ94wdTun1kZCs/X6sVlkO00ocU0RERG8OfkojIiIiIiIiItIDkytERERERERERHrgsCAiIqLXRGKaBEnpmudVkcsNHAyRHuKTJXiWqvletrcScLTmkCEiIqpYmFwhIiJ6TeTLgJxczQ+kROVJXr72+zhfxsQKERFVPBwWRERERERERESkB/ZcISIiMqC0TCBXpvlb/Sz2WqE3QFauBIlpmutMjQWsLQwbDxERUWlgcoWIiMiAkjOMkJrJJAq9uTKyJMjI0vwesLEQsLbgBENERFT+cFgQEREREREREZEe2HOFiIiolGXlAHItc3bmywwbC1F5ki8DMrI11xlJAHOpYeMhIiLS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2SKGDBkiGjVqJBwdHUWlSpWEg4ODaNu2rVi8eLFIT08vdB9DhgwRAMSPP/6osT41NVUsXbpU9OzZU3h4eAgzMzPh6OgoWrRoIZYvXy4yMjLU2mzbtk00a9ZMmJubCwACgLh3757KNhEREaJ///7C1dVVmJiYCEdHR9G4cWMxceJEERERobJtcHCwACDCwsLUjhUWFiYAiODgYI3xu7u7i+J+vMjJyREhISGiVatWwsrKSpibmwtPT08xbNgwcf78eY1tsrKyhImJiTA3NxdJSUmF7j81NVXMmzdPNG7cWJibmwsrKytRt25dMXHiRHHr1i2dz60417Aw6enpYuLEiaJ69eqiUqVKAoDw8/NT1vv5+Wl8DQEId3d3jfss6WuWn58vNm/eLAICAoSDg4MwMTERrq6uwtfXV3z11VciOjpa5/N68OCBMDY2Fj179tS6TWhoqHjrrbeEubm5sLe3F4GBgeL69etizpw5AoDYuHGjyvZ5eXli5syZwtPTU5iYmGi8Bj/99JMAIJYuXapzrERERKVNIkQRA8iJiIioVGRmZqJatWrIz8/Ho0ePtC5bS0XbsWMHBg0ahODg4Ao10Wx517dvXxw8eBCxsbFwdnY2yDG7deuGv//+Gw8ePICjo6NBjklERPQyzrlCRERkICtXrkRycjKCg4OZWNFDXl4eFi1aBACYMGFCGUdDBc2fPx9yuRxLliwxyPGioqJw5MgRTJ06lYkVIiIqU+y5QkRE9AolJiZixowZePr0KX777TdYWFjg+vXryqVwSXcHDhzAvn37cPbsWVy9ehV9+vRBaGhoWYdFLxk+fDh27tyJe/fuKZdoflX69OmD06dP486dO7CysnqlxyIiIioMkytERESvUExMDGrWrAlTU1M0atQIS5Ysgb+/f1mHVS7NnTsXX331Fezt7dGjRw+sWLECDg4OZR0WEREREZMrRERERERERET64JwrRERERERERER6YHKFiIiIiIiIiEgPTK4QEREREREREemByRUiIiIiIiIiIj0wuUJEREREREREpAcmV4iIiIiIiIiI9MDkChERERERERGRHphcISIiIiIiIiLSA5MrRERERERERER6+D9T9nt7lnFOgAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "with pulse.build() as schedule:\n", - " pulse.play(gaus, channel)\n", - "schedule.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You may also supply a complex list or array directly to `play`" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.815561Z", - "iopub.status.busy": "2023-08-25T18:25:51.815063Z", - "iopub.status.idle": "2023-08-25T18:25:51.927010Z", - "shell.execute_reply": "2023-08-25T18:25:51.926343Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABFcAAADeCAYAAAD4ru/+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABSIklEQVR4nO3dd1QUV/8G8GfpHQREqqDYC8WOioAFsHdji2uL3Rj7GyvRJPYWYzQau8YSuyavLaKxxIoCdlFRxIIgTUQE9v7+8Lf7su5Sl6L4fM7hnHDv3Jnv7A5m9tmZOxIhhAARERERERERERWIVkkXQERERERERET0KWO4QkRERERERESkAYYrREREREREREQaYLhCRERERERERKQBhitERERERERERBpguEJEREREREREpAGGK0REREREREREGmC4QkRERERERESkAYYrREREREREREQaYLiSTxKJBBKJBBYWFkhISFC7zNy5cyGRSBAUFFSstX1MIiMjIZFI4Ovrm69xvr6+kEgkiIyMLJK6inK7gwYNUhwfZ86cKZS65K9j1h99fX3Y2Nigbt26GDp0KI4dOwYhRKFsryQFBQVBIpFgw4YNJV1Kvpw6dQrfffcd2rZti7Jly0IikcDFxSVPY1+/fo3vvvsObm5uMDExgbm5OWrVqoWRI0fi9evXasecPXsWbdq0gaWlJUxMTNCgQQNs2rSpwPUfPHgQPj4+MDMzg5mZGXx9ffHnn38WeH3qnDx5EhKJBP379y/U9RIRERERfSwYrhRQYmIiFi9eXNJl0EciODgY69atg0QiKZL1GxsbQyqVQiqVokePHmjYsCFevXqF1atXw9/fH/Xr18fdu3eLZNuFpaSCs6I2ZswYBAUF4a+//kJsbGyexz18+BBubm4ICgpCSkoKWrduDR8fH6Snp+OXX35RG97u3r0bPj4+OHz4MNzc3BAYGIh79+5BKpViwoQJ+a596dKl6NChA86dO4cmTZqgefPmuHjxItq1a4eff/453+vLr/79+0MikeDkyZNFvi0iIiIioqKkU9IFfIrkVw8sW7YMY8eORZkyZUq6JCpBb9++xdChQ1GzZk2Ym5vj3Llzhb4Na2trtVd0hIaGYty4cThx4gSaNWuGS5cuwcnJqdC3XxxGjRqFnj17ws7OrqRLyRd/f390794d9evXh6OjI2rWrJnrmLS0NLRu3RqPHz/GqlWrMHToUKX+69evw9LSUqnt1atXGDhwIDIzM7F792506dIFAPDixQs0bdoUixYtQrt27fJ8tdidO3cwYcIE6OvrIzg4GF5eXgCAu3fvonHjxhg7diwCAwNRqVKlPK2PiIiIiOhzxitXCkBLSwtDhgxBUlISFi5cWNLlUAmbPXs2IiIisGrVKujq6hbrtt3d3XH06FH4+/vjxYsXGDNmTLFuvzBZW1ujWrVqMDc3L+lS8mX+/PmYOnUq/P39VQKR7Cxbtgx37tzBuHHjVIIVAKhVqxaMjIyU2n777TckJSWhY8eOimAFAMqVK4f58+cDABYtWpTnupctW4bMzEwMGzZMEawAQJUqVTB16lRkZGRg2bJleV4fEREREdHnjOFKAf3nP/+BoaEhli9fjri4uDyNefbsGebPnw8fHx84ODhAT08Ptra26NKlCy5duqR2jIuLi+JWkxUrVqBWrVowNDREhQoVMH/+fMVcGyEhIWjfvr1iHoaOHTvi0aNHatcphMC2bdvQvHlzlClTBgYGBqhevTqCgoLw5s2bArwaOUtKSsKYMWPg5OSk2NaSJUsgk8nytZ6bN2+iT58+sLOzg56eHhwcHNCvXz/cuXMn2zG3bt3CoEGD4OLiopirpEmTJli4cCEyMjJy3WZiYiKaNWsGiUSC0aNHq8xtEh4ejgULFmDgwIFo2rRpvvansGhra+Pnn3+GRCLBvn378PjxY0Xfhg0bcpz/R92tOlnny0lKSsK4ceNQoUIF6Orq4ptvvgEAJCQkYPny5QgICICzszP09fVhZWWFwMBAHDt2TGkb8vWdOnUKAFChQgWlOWTkcppzJS4uDhMnTkTlypVhYGAAS0tLBAYG4ujRo2r3Sz7vSWZmJubNm4cqVapAX18fTk5OmDx5MtLS0vLwyhadNWvWAABGjx6d5zHyeVC6deum0te2bVsYGBjg+PHjePv2rcbrk7cdPHgwz/UBwI0bN9CpUyeUKVMGpqam8Pb2xuHDh9UuK5FIsHHjRgCAn5+f0jFR2m4dIyIiIqLSj+FKAdnZ2WHYsGFITk7GggUL8jRm//79mDx5Ml68eAE3Nzd07twZ9vb22Lt3L5o0aZLtB0UAGDt2LCZOnAhnZ2e0bNkScXFxmDx5MoKCgnD27Fl4e3vj6dOnaNWqFezs7HDgwAG0aNECqampSuuRyWTo06cPevfujUuXLsHDwwNt2rRBSkoKvvvuO/j5+amMyTqpan6lpaWhefPm2LRpExo0aIBWrVrh0aNHGDduHAYOHJjn9fz999+oV68efv/9d9jZ2aFr166wsbHB5s2bUa9ePZw+fVplzB9//AFPT0+sW7cORkZG6Ny5M+rWrYuoqChMnDgx2wlD5V68eAFfX1+cPn0aM2bMwPLly5VeA5lMhiFDhsDCwkJx5UBJqVy5MurWrQshhCLE0FRqaip8fHywYcMGeHh4oEOHDopb4M6fP4+vv/4ad+/eRdWqVdG5c2dUrVoVR48eRUBAANatW6dYj4mJCaRSKcqVKwcA6Nq1q2L+GKlUmmsd0dHRaNCgARYuXIh3796hU6dO8PT0xPHjxxEQEIAlS5ZkO7Z37974/vvvUbVqVfj7+yM5ORnz58/HoEGDVJaVT7qa18loCyoqKgoRERFwdHSEk5MTzp49i8mTJ2PYsGGYN28eIiIi1I4LDQ0FANSpU0elT09PD7Vq1cLbt2/zNPdOQkKCIoTz9PRU6XdycoK1tTUePXqEpKSkPO3X5cuX0ahRI+zfvx+Ojo5o164dUlNT0aZNG/zxxx8qy0ulUri6ugIAAgIClI4JExOTPG2TiIiIiOijIShfAAhtbW0hhBDPnz8XRkZGwtjYWMTExCiWmTNnjgAgZs6cqTQ2LCxMXL9+XWWdhw8fFnp6esLV1VXIZDKlPmdnZwFA2Nvbi4iICEX7rVu3hL6+vjAyMhIuLi5i5cqVir60tDTRvHlzAUCsW7dOaX3z588XAISvr6949uyZ0phBgwYJAGLy5MlKYx4+fCgAiPwcLlnHuLm5iZcvXyr6IiIihL29vQAg9u7dqzTOx8dHABAPHz5UtL1+/VqUK1dOABA///yz0vKLFy8WAISjo6NITU1VtN+9e1cYGBgIHR0dsXXrVqUxMplMHDlyRLx9+zbb7T58+FBUqlRJSCQS8dNPP6ndx59++kkAEBs3blRZz+nTp/P0OuVG/jo6OzvnuuzgwYMFAPHtt98q2tavX6/2WPyw3qyvd9b3zsvLS8THx6uMe/Dggfj3339V2kNCQoSFhYUwMzMTycnJuW4rq5kzZwoAYv369Urt7dq1EwBE7969RVpamqL99OnTwsjISGhra4urV68qjZHXX716daXj/MGDB8LCwkIAUPp7EkKI4ODgPL/W2Xn27Fmu6zh8+LAAIBo0aCBGjBihqFX+o6OjIxYuXKg0JjExUdGfmJiodr2dOnUSAMSBAwdyrTM0NFQAEGXKlMl2GQ8PDwFAhIWF5bo+mUwmatSoIQCIGTNmKPWtWLFCUbtUKlXqk0qlAoAIDg7OdRtERERERB8zXrmigXLlymH48OFISUnBvHnzcl2+du3aaie7DAgIQPfu3XH//n1cv35d7dhZs2YpvuUFgGrVqqFNmzZ48+YNHB0dMWzYMEWfnp6eYu6NrFcxZGRkYP78+TA2Nsb27dtha2urNGb58uWwtbXF6tWrlW7Z0dXVRdWqVVG1atVc91GdhQsXwtraWvG7q6srpk+fDgB5eiLJzp078eLFC3h5eWHkyJFKfWPHjkXdunXx5MkT7N69W9G+ZMkSvH37FoMHD0bv3r2VxkgkEvj7+0NfX1/t9m7cuIEmTZogMjISmzdvVnvrxpMnTzB16lT4+vqiX79+ue5DcZC/xvHx8YW2zp9++gkWFhYq7RUqVECjRo1U2j09PTFy5EgkJSUhODhY4+0/ePAAhw4dgomJCZYvXw49PT1FX9OmTTFs2DBkZmZixYoV2daf9TivUKEC+vbtCwAqVzsZGRmhatWqSn9nRUH+/oSEhGDVqlUICgpCVFQUnj17pvh3ZMKECUqPQ856ldWHc7HIGRsbAwCSk5NzrUG+vuzWld/1nTx5Ejdv3kTFihUxY8YMpb4RI0agYcOGua6DiIiIiOhTxnBFQ5MnT4axsTFWrlyJFy9e5Lp8Wloa9u/fj6lTp2LIkCHo378/+vfvj/DwcADAvXv31I7z9/dXaatYsWKufc+ePVO0hYSEIDY2Fo0bN1bcopGVoaEh6tati/j4eKU6HBwccPv2bdy+fTvX/fuQpaUlWrVqpdLeq1cvAMC5c+dynXtF/iG4T58+avvVfVg+fvw4AKidLDQn58+fR7NmzRAfH4+9e/dmu82RI0ciLS0NK1euzNf6i5L4//lgCutx0HZ2dqhXr162/ZmZmTh69CiCgoIwdOhQxbEsD1WyO5bz48yZMwCAwMBAtZPFfvnllwBUgxLgfSjo5+en0l6lShUAyn8bANCgQQPcvn0bf//9t8Z150R+vGdkZGDo0KGYOXMmHB0dYWtri0mTJmHs2LEAgB9//LFI6yhM8te/W7du0NbWVumX/70TEREREZVWfBSzhsqWLYuRI0di/vz5mDt3bo7zP4SHh6NDhw45TtaY3bfEDg4OKm3yeQly6ss6cad8u8eOHcv1A3hsbGyBr1TJytnZWW27ubk5LCwskJCQgPj4eFhZWWW7jqdPnwJAtnNhyNujo6MVbVFRUQCQ76sQvvzyS2RkZGDHjh1o166d2mV2796NAwcOYPr06ahWrVq+1l+UYmNjASDPT6zJTfny5bPte/LkCdq1a6eYB0SdvFzxkJuCvPdytra2aj/om5qaAkCJTWqbdT6RAQMGqPQPGDAACxYswIULF/D27VsYGBgojXnz5g3MzMxUxqWkpAD43/7lpYacJrDOz/rk71N2f+9FPY8NEREREVFJY7hSCCZOnIhffvkFq1atwqRJk9QuI4RAjx49EBkZiWHDhmHYsGGoWLEiTExMIJFIMGXKFMyZM0flaTRyWlrZX2SUU19W8m/MK1WqhCZNmuS4bE5hx8emsK7UAN5/w75582bMmDEDzZo1U7qlRE7+BJVjx47hn3/+Ueq7du0agPdPgTE3N1dczVEcrl69CgCoUaNGnsfkdNWQgYFBtn2DBw9GaGgounbtikmTJqFq1aowNTWFlpYWVq9ejaFDh2Z7LBemnN77vP5dFLesAYS60EHelpmZiVevXsHe3h5mZmYwNzdHYmIinjx5ovY9fvLkicr6syMPzuLj45GSkqK4Baig6yMiIiIi+twxXCkE1tbWGD16NObMmYM5c+bA3t5eZRn5bTX16tVTeyvJgwcPirxOR0dHAO/na1H3uNuikPWxwFklJSUhISEBhoaGauf0yEr+emb3aGn5FTlZr+BxcnLCvXv3cP/+fXh4eOS53lmzZsHe3h7z5s1D8+bNcfLkSdjY2Khd9vz589muRx6y+Pr65nnbmrh37x6uXr0KLS0tNGvWTNEun6Mkuycjya/wyY+UlBQcO3YM5cqVw44dO1SuDinMY7kg7/3Hrlq1ajAwMMDbt28RHx+PsmXLKvW/evVK8d9Zr1hxd3fHP//8g5CQEJVwJT09HdevX4eBgYHitqecWFhYoHz58nj8+DGuXr2q8hjxqKgoxMbGwtnZWe1VMh+ys7MDkP37lF07EREREVFp8XF+tfsJGj9+PExNTbF69Wq1tyjIJ7GUBxwf9h07dqzIa6xfvz7Mzc1x6tQppQ9wRSkuLk7tHBbbt28HAHh5eam9dSMrb29vAMC2bdvU9m/ZskVpOQBo2bIlAGD16tX5rnnu3LmYMGECbt26hebNm+Ply5dK/Rs2bIAQQu2Pj48PgPdzUAghEBQUlO/t51dmZiZGjRoFIQS6du2qdIzJP/Sqezzv3bt3sw2/cpKYmAiZTAY7OzuV9y49PR179+5VO04e9GRkZOR5W/IP/YcPH0ZCQoJKv7r3/mOnr6+PgIAAAO8ngv2QfBLqihUrKgUbbdu2BQDs2rVLZcyhQ4fw9u1btGzZMscrjrLKaX3ytvbt2+dpXfLXf/fu3WqvhpL/vX+oIMcEEREREdHHiOFKIbGyssLXX3+NtLQ0rF27VqW/UqVK0NLSwokTJ5Qm+nz79i2GDRtWLGGHvr4+Jk2ahOTkZHTp0kXtFQbR0dHYvHmzSlu1atUKPL/IhAkTEBcXp/j94cOHmDVrFgCoPP1HnR49eqBcuXI4c+aMSljy008/4fLly3BwcEDXrl0V7d988w0MDAywZs0a7NixQ2mMEALHjh3Lcc6NBQsWYNy4cbhx4wZatGihmM9EUy4uLpBIJGo/VBdEWFgY/P39cfToUdjZ2WHp0qVK/fXr14eRkRH++9//4sqVK4r22NhYDB48ONfJhNWxsbGBubk5rl+/jrNnzyraMzMzMXnyZLVBDvC/q1Du3LmT521VrFgRbdu2RXJyMsaMGYP09HRF37///ouVK1dCW1s7T8dRbi5evIhq1aqhRYsWGq8rN/LbB2fPnq30ej18+FDxJK2sTwAD3t+KZWZmhv3792PPnj2K9piYGMX6xo8fr7It+d/uh6HvmDFjoK2tjVWrVildhXXv3j388MMP0NHRUTx1LDe+vr6oVq0a7t+/j++//16p79dff8W///6rdlxBjgkiIiIioo9SiTwA+hMGQGhra6vte/XqlTAzMxMABAAxc+ZMpf6vvvpKABCGhoaibdu2olu3bqJcuXLC2tpa9O/fXwAQ69evVxrj7OwssnubZs6cqXaMEEI8fPhQABA+Pj5K7ZmZmeLLL78UAISenp5o2LCh6Nmzp+jSpYuoWbOmkEgkwt3dXe268nO4yMc0atRI1KlTR1hYWIguXbqI9u3bCyMjIwFA9O3bV2Wcj4+PACAePnyo1H78+HFhaGgoAIi6deuKXr16CU9PTwFAmJiYiH/++UdlXdu2bRO6uroCgKhRo4bo2bOnaN26tXBychIARHx8fK7bHTNmjAAg3NzcRGxsbK77LV/P6dOn1fbLt33mzJlc1yXE/15HY2NjIZVKhVQqFX379hXt27cXFStWVLwv9evXF/fu3VO7jhkzZggAwsDAQAQEBIjAwEBRpkwZ0bhxY+Hl5aWy39kdO1n98MMPir+FVq1aiS+++EK4uLgIQ0NDMXLkSLXH/+7duwUAYWZmJrp16yYGDRokBg0apOjP7nh+8uSJqFChggAgnJ2dRc+ePUWLFi2Etra2ACAWLVqkUp98WXXWr1+vtr7g4OAcx2VnzZo1omHDhqJhw4aiTp06Sn9b8p8rV66ojJO/L0ZGRqJVq1YiMDBQmJqaCgCidevWIiMjQ2XMrl27hJaWlpBIJMLPz09069ZNWFhYCABi3LhxauuTHyMfHttCCLF48WIBQOjo6IjWrVuLjh07Kv7Ofvrpp3y9DufPnxfGxsYCgKhdu7bo1auXqF+/vpBIJGLEiBECgJBKpUpjLl++LCQSiTAwMBAdO3ZUHBN5+VsjIiIiIvqYMFzJp5zCFSH+94FJ3Ye3jIwMsWjRIlGjRg1hYGAgypUrJ/r06SMiIyOz/WBZ2OGK3P79+0Xbtm2FjY2N0NXVFTY2NqJu3bpi0qRJKh8ENQlXfHx8REJCghgxYoSwt7cXenp6omrVqmLhwoVqPzxmF3IIIcT169dFr169RLly5YSurq6ws7MTffv2Fbdv3862jtDQUNG3b1/h4OCg2M8mTZqIRYsWifT09Dxtd/To0QKA8PDwEHFxcTnud07hSmxsrJBIJKJKlSpq912drK+9/EdXV1dYW1uLOnXqiCFDhoijR48KmUyW7TpkMplYsGCBqFSpktDV1RWOjo5i/PjxIiUlRe1+5yVcEUKIjRs3Ck9PT2FkZCSsrKxEx44dRWhoaLbhhRBCLFmyRNSoUUPo6+urHFM5Hc+xsbFi/PjxwtXVVejp6QkLCwvh7+8vjhw5ora24gxX5HXn9BMcHKx27O7du4W3t7cwNTUVhoaGwsPDQyxZskTp2PzQmTNnRGBgoLCwsBBGRkaiXr16YsOGDdkun1O4IoQQBw4cEN7e3sLExESYmJgIb29vcfDgwfy8BAphYWGiffv2wtzcXBgbGwsvLy9x6NAhxWv7YbgihBBbt24VderUUYQ6OdVKRERERPSxkghRDI/0ICLs2bMHXbt2xZYtW9CnT5+SLoeIiIiIiIgKCedcISomwcHBqF69Onr16lXSpRAREREREVEh4pUrREREREREREQa0CnpAj4FMpkMT58+hampKSQSSUmXQ0RERERERERqCCGQnJwMe3t7aGkV3806DFfy4OnTp3BycirpMoiIiIiIiIgoD6KiouDo6Fhs22O4kgempqYAgFu37yj+m4iIiIiIiIg+LsnJyaherWqxf3ZnuJIH8luBTE1NYWZmVsLVEBEREREREVFOintKDz4tiIiIiIiIiIhIAwxXiIiIiIiIiIg0wHCFiIiIiIiIiEgDDFeIiIiIiIiIiDTAcIWIiIiIiIiISAMMV4iIiIiIiIiINMBwhYiIiIiIiIhIAwxXiIiIiIiIiIg0wHCFiIiIiIiIiEgDDFeIiIiIiIiIiDTAcIWIiIiIiIiISAMMV4iIiIiIiIiINMBwhYiIiIiIiIhIAwxXiIiIiIiIiIg0wHCFiIiIiIiIiEgDDFeIiIiIiIiIiDTAcIWIiIiIiIiISAMMV4iIiIiIiIiINMBwhYiIiIiIiIhIAwxXiIiIiIiIiIg0wHCFiIiIiIiIiEgDDFeIiIiIiIiIiDTAcIWIiIiIiIiISAMMV4iIiIiIiIiINMBwhYiIiIiIiIhIAwxXiIiIiIiIiIg0wHCFiIiIiIiIiEgDOiVdABEREREREREVjxuPtPO8bE3nzCKspHRhuEJERERERET0kctPKELFj+EKERERERERUQlgYFJ6MFwhIiIiIiIiyicGI5QVwxUiIiIiIiL6rDAYocLGcIWIiIiIiIg+aQxLqKQxXCEiIiIiIqISw2CESgOGK0RERERERFRoGJbQ54jhChEREREREQFgMEJUUAxXiIiIiIiISgEGI0Qlh+EKERERERHRR4qBCdGngeEKERERERFRIWMoQvR5YbhCRERERESUBYMRIsovhitERERERFTqMTAhoqLEcIWIiIiIiD5qDEaI6GPHcIWIiIiIiIoVwxIiKm0YrhARERERUZ4xGCEiUsVwhYiIiIjoM8FghIioaDBcISIiIiL6RDEsISL6ODBcISIiIiIqAQxGiIhKD4YrRERERET5xGCEiIiyYrhCRERERAQGJkREVHAMV4iIiIjok8ZQhIiIShrDFSIiIiL66DAwISKiTwnDFSIiIiIqVAxGiIjoc8NwhYiIiIgUGIwQERHlH8MVIiIiolKMYQkREVHRY7hCRERE9JFiMEJERPRpYLhCREREVAQYjBAREX0+GK4QERER5QHDEiIiIsoOwxUiIiIq9RiMEBERUVFiuEJERESfJAYmRERE9LFguEJERETFjsEIERERlSYMV4iIiCjPGIoQERERqWK4QkRE9JljYEJERESkGYYrREREnzAGI0REREQlj+EKERFRCWEwQkRERFQ6MFwhIiIqJAxLiIiIiD5PDFeIiIjAYISIiIiICo7hChERffIYjBARERFRSWK4QkREHx2GJURERET0KWG4QkREhYrBCBERERF9bhiuEBFRrhiYEBERERFlj+EKEVEpx2CEiIiIiKhoMVwhIvpIMRQhIiIiIvo0MFwhIipGDEyIiIiIiEofhitERHnEYISIiIiIiNRhuEJEnwUGI0REREREVFQYrhDRJ4lhCRERERERfSwYrhBRsWMwQkREREREpQnDFSIqFAxMiIiIiIjoc/VJhysSiUTpdx0dHZibm8POzg5169ZF+/bt0bFjR+joZL+bqampmDNnDrZv347Hjx/D0tISgYGBmD17NhwcHIp6F4hKHEMRIiIiIiIizUiEEKKkiygoebgilUoBADKZDImJibh79y7u3LkDIQQqVaqErVu3okGDBirj3759Cz8/P5w/fx52dnbw9vZGZGQkLl68iLJly+L8+fOoWLEikpKSYG5ujifRT2FmZlas+0iUEwYjRERERERUVGo6Z5Z0CfmWlJQERwd7JCYmFuvn90/6yhW5DRs2qLTdv38fU6ZMwc6dO+Hn54ezZ8/Cw8NDaZnvv/8e58+fh5eXF44ePQoTExMAwOLFizF+/HgMHDgQJ0+eLPodIMqCgQkREREREdGnpVRcuZLTLgwePBhr166Fp6cnQkJCFO3v3r2DjY0NEhMTERISAk9PT6Vx7u7uCAsLw+XLl1G5cmVeuUJ5wmCEiIiIiIhKC165knel4sqVnCxatAjbt2/H1atXcebMGTRt2hQAcPbsWSQmJsLV1VUlWAGAbt26ISwsDAcPHsS4ceOKu2wqZgxFiIiIiIiIqKBKfbhibm6O1q1bY9euXQgODlaEK6GhoQCAOnXqqB0nbw8LCyueQqnQMTAhIiIiIiKi4lDqwxUA8PDwwK5du3Dr1i1F2+PHjwEAjo6OasfI2x89elT0BZIKBiNERERERET0qfgswhVr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rUPZsmWxdetW7Nu3D82bN8eff/7JeVmIiIiKiIenJ0JCQlD9/28DUurz8MSmTZtgbGyMKlXe37Lr4lIBCxcuwooVP2PJ4kWwsrJC9x49UKaMJYJmzlBZv76+Pnx8fPHXX3+iYaNGsLSyUllm4KBBcHZ2xpYtm/HrqlUA3k+07+XlBV8f31z3YfjwEShTxhI7tm/DwgXzYW5uji5du2L06K+znbyWiIiIPh8SwZnSALyffHfmzJkICgpS6UtKSoK5uTmeRD+FmZlZ8RdHRERERERERLlKSkqCo4M9EhMTi/Xze5HMuUJERERERERE9LlguEJEREREREREpAGGK0REREREREREGiiyCW0/NZx6hoiIiIiIiIgKgleuEBERERERERFpgOEKEREREREREZEGGK4QEREREREREWmA4QoRERERERERkQYYrhARERERERERaYDhChERERERERGRBhiuEBERERERERFpQKekC/gUCCEAAMnJySVcCRERERERERFlR/65Xf45vrgwXMkD+ZtTvVrVEq6EiIiIiIiIiHITFxcHc3PzYtueRBR3nPMJkslkePr0KUxNTSGRSEq6nDxLSkqCk5MToqKiYGZmVtLlEBUrHv/0uePfAH3OePzT54zHP33uEhMTUb58ecTHx8PCwqLYtssrV/JAS0sLjo6OJV1GgZmZmfEfVvps8finzx3/BuhzxuOfPmc8/ulzp6VVvFPMckJbIiIiIiIiIiINMFwhIiIiIiIiItIAw5VSTF9fHzNnzoS+vn5Jl0JU7Hj80+eOfwP0OePxT58zHv/0uSupvwFOaEtEREREREREpAFeuUJEREREREREpAGGK0REREREREREGmC4QkRERERERESkAYYrpVBqaipmzJiBKlWqwMDAAPb29hg4cCCio6NLujSiQrF48WJ06dIFlStXhrm5OfT19eHs7Ix+/fohPDw8T+to2bIlJBIJJBIJnjx5UsQVExW+ly9fYsKECahatSoMDQ1haWmJOnXqYOLEiUrLubi4KI717H4qVqxYQntBpN6VK1cwd+5cdOnSBY6OjopjNTcbNmxAgwYNYGJiAktLS7Rp0wbnzp1Tu2xKSgo2b96M0aNHo2HDhtDX14dEIkFQUFAh7w1R/uT3+D9w4ACkUilq164Na2tr6OrqwsbGBm3atMGhQ4fyvN3Zs2crtrVly5bC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V1dXoaurq/Y12LRpkwAgFi1alOdaiYiICptEiFxuICciIqJC8ebNGzg4OCAjIwNPnz7N9rG1lLtt27ahd+/ekEqlpWqi2U9d586dcejQIURFRcHW1rZYthkQEIAzZ87g8ePHsLKyKpZtEhERfYhzrhARERWTFStWICEhAVKplMGKBtLT0zFv3jwAwMiRI0u4Gspq9uzZkMlkWLhwYbFsLyQkBEePHsX48eMZrBARUYnilStERERFKC4uDpMnT8aLFy/w119/wcjICLdu3VI8Cpfy7sCBA9i3bx8uXryIGzduoFOnTti7d29Jl0UfGDhwIHbs2IGHDx8qHtFcVDp16oR///0X9+/fh4mJSZFui4iIKCcMV4iIiIpQZGQkKlSoAD09PdSuXRsLFy6Er69vSZf1SQoKCsJ3332HMmXKoHXr1li+fDksLS1LuiwiIiIihitERERERERERJrgnCtERERERERERBpguEJEREREREREpAGGK0REREREREREGmC4QkRERERERESkAYYrREREREREREQaYLhCRERERERERKQBhitERERERERERBpguEJEREREREREpAGGK0REREREREREGvg/jp6PKBTMCOMAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "with pulse.build() as schedule:\n", - " pulse.play([0.001*i for i in range(160)], channel)\n", - "schedule.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `play` instruction gets its duration from its `Pulse`: the duration of a parametrized pulse is an explicit argument, and the duration of a `Waveform` is the number of input samples.\n", - "\n", - "## `set_frequency`\n", - "\n", - "As explained previously, the output pulse waveform envelope is also modulated by a frequency and phase. Each channel has a [default frequency listed in the backend.defaults()](08_gathering_system_information.ipynb#Defaults).\n", - "\n", - "The frequency of a channel can be updated at any time within a `Schedule` by the `set_frequency` instruction. It takes a float `frequency` and a `PulseChannel` `channel` as input. All pulses on a channel following a `set_frequency` instruction will be modulated by the given frequency until another `set_frequency` instruction is encountered or until the program ends.\n", - "\n", - "The instruction has an implicit duration of `0`. \n", - "\n", - "**Note**: The frequencies that can be requested are limited by the total bandwidth and the instantaneous bandwidth of each hardware channel. In the future, these will be reported by the `backend`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.930887Z", - "iopub.status.busy": "2023-08-25T18:25:51.930371Z", - "iopub.status.idle": "2023-08-25T18:25:51.935395Z", - "shell.execute_reply": "2023-08-25T18:25:51.934825Z" - } - }, - "outputs": [], - "source": [ - "with pulse.build(backend) as schedule:\n", - " pulse.set_frequency(4.5e9, channel)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `shift_phase`\n", - "\n", - "The `shift_phase` instruction will increase the phase of the frequency modulation by `phase`. Like `set_frequency`, this phase shift will affect all following instructions on the same channel until the program ends. To undo the affect of a `shift_phase`, the negative `phase` can be passed to a new instruction.\n", - "\n", - "Like `set_frequency`, the instruction has an implicit duration of `0`." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.938933Z", - "iopub.status.busy": "2023-08-25T18:25:51.938471Z", - "iopub.status.idle": "2023-08-25T18:25:51.943282Z", - "shell.execute_reply": "2023-08-25T18:25:51.942719Z" - } - }, - "outputs": [], - "source": [ - "with pulse.build(backend) as schedule:\n", - " pulse.shift_phase(np.pi, channel)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `acquire`\n", - "\n", - "The `acquire` instruction triggers data acquisition for readout. It takes a duration, an `AcquireChannel` which maps to the qubit being measured, and a `MemorySlot` or a `RegisterSlot`. The `MemorySlot` is classical memory where the readout result will be stored. The `RegisterSlot` maps to a register in the control electronics which stores the readout result for fast feedback.\n", - "\n", - "The `acquire` instructions can also take custom `Discriminator`s and `Kernel`s as keyword arguments." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.947659Z", - "iopub.status.busy": "2023-08-25T18:25:51.946326Z", - "iopub.status.idle": "2023-08-25T18:25:51.951919Z", - "shell.execute_reply": "2023-08-25T18:25:51.951339Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.pulse import Acquire, AcquireChannel, MemorySlot\n", - "\n", - "with pulse.build(backend) as schedule:\n", - " pulse.acquire(1200, pulse.acquire_channel(0), MemorySlot(0))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we know how to add `Schedule` instructions, let's learn how to control exactly when they're played.\n", - "\n", - "# Pulse Builder\n", - "Here, we will go over the most important Pulse Builder features for learning how to build schedules. This is not exhaustive; for more details about what you can do using the Pulse Builder, check out the [Pulse API reference](https://qiskit.org/documentation/apidoc/pulse.html).\n", - "\n", - "## Alignment contexts\n", - "The builder has alignment contexts which influence how a schedule is built. Contexts can also be nested. Try them out, and use `.draw()` to see how the pulses are aligned.\n", - "\n", - "Regardless of the alignment context, the duration of the resulting schedule is as short as it can be while including every instruction and following the alignment rules. This still allows some degrees of freedom for scheduling instructions off the \"longest path\". The examples below illuminate this.\n", - "\n", - "## `align_left`\n", - "The builder has alignment contexts that influence how a schedule is built. The default is `align_left`." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:51.956307Z", - "iopub.status.busy": "2023-08-25T18:25:51.954946Z", - "iopub.status.idle": "2023-08-25T18:25:52.117371Z", - "shell.execute_reply": "2023-08-25T18:25:52.116635Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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5Ihat26tunXravbs2WrRokWyMWQcPScAAAByPYN0kWScnJyMMcacPHnSeHp6Gi8vL3P69GnbNKNHjzaSzIgRI+zm3b59u/nrr7+SLXP58uXG1dXVBAUFmaSkJLu60qVLG0mmePHiZv/+/bbyXbt2GTc3N+Pp6WnKlCljpk2bZqu7du2aadGihZFkPv/8c7vljR071kgyzZo1MydOnLCb55lnnjGSzOuvv243z6FDh4wkk97TJT3ztGvXzkgyL774orl69aqt/OTJk6ZOnTpGkvnvf/9rN09ERISRZCIjI1Ncd+nSpdMVrzHGJCYmmkWLFpnr16/blZ8+fdoWx9q1a+3qvvjiixSP94gRI4wk88UXX9iVt2zZ0kgy/fv3Nzdu3LCVL1++3Li4uBhJJiwsLMVlSTKTJk2yq3vppZeMJPPUU0+leTsHDBhgJJnHH3/cXLhwwVYeHx9vOxa3n1PGGDNr1iwjyYSGhppr164ZY4zp2bOnkWReeumlZOtYvXq1OXnypF3ZzZs3zXvvvWckmT59+tjV3X6etWjRwly6dMlWZ93H5cuXN35+fmbz5s22umPHjpkiRYoYSebAgQN2y7R+fnx8fMyWLVts5RcvXrR9Rl588UW7eVI7bhnZZxlhXf+d59Odbt68adzc3Iwks2fPHlt5WFiYkWQOHTqUbB7rPr7b+TVmzJgU15fR43nnum5nPT63++eff4yHh4dxcnIyP/zwg926rOd6nTp17Oaxnh+SzNChQ83NmzdtdRMnTjSSTNOmTZOt3/odcq99nRb79++3HY/bzzVjjAkNDTWS7Lbndh999JGRZF555RVb2csvv2wkmZdffjnFeaKjo40kU6tWrTTF16VLlxS/P6wWL15sJJlHH300TcubP3++kWRKlSpld66dOnXKVK9e3XY8bq9LyzkBAABwv6LnigOKFi2qF154QQkJCRozZsw9p69Ro4aqVauWrDw8PFzdunXTgQMHUu198P7779t6aki3Hgto166dLl++rJIlS6p///62OldXV7344ouSbnW/trpx44bGjh0rLy8vzZ8/3+7uqqurqyZPnqyAgAB9+umndnfgXVxcVKlSJVWqVOme25gR0dHRWrZsmerWrasJEybYdZsvWrSoPv30U0nStGnTsmT9t7O+QcPFxcWuvHDhwrYBJ3/44YcML3///v365ZdfVKBA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uNDhuBo1aiRJWrFiha0bfmav416aN28uJycnLVmyRImJiWme78iRIxowYIDc3Nz09ddfa86cOXJ3d9fgwYN16NAhu2nvth9//fVXnTp1yrGNSKeU9uvu3bsVHR2t/Pnz3/WHsZTxfZZVdu/ebXtt953JDmsCb+/evcnmy+j3TEaOpzXJk97PYdOmTSUpxUfzrl+/rm+++cZuupywbNkyPf3003J2dtaiRYvu2ROwffv2kqSffvop2aNJp06d0rp16+Tn52e3nMaNG8vX11cHDhxQdHR0smV+++23kqQOHTqkKeY2bdooX758WrdunU6fPm1Xd+3aNf30009ycnJSu3bt7rksFxcX1a9fX1LKn62VK1em2MZk9JwAAAC4H5BcyST+/v4aMmSIrl27ppkzZyarL1++vPLly6fVq1fbDYh69epV9e/fP1uSHW5ubho2bJguXryoRx99NMW7zceOHdNXX32VrKxy5cqqXLlylsRVv359tWrVShs2bNDAgQMVHx+fbJpt27Zp+fLlaV5m8eLFderUKV24cCFdsVgHUp0+fbpd+bfffqsvv/wyXctKSYUKFdSyZUudP39er7/+ut1jGj///LPmz5/v8DrupUSJEurbt69iYmLUo0ePFH8Y79+/X999953t76SkJD399NO6cOGC/vWvf6lmzZqqWrWqxowZo/j4eD311FO2wU2l/+3HOXPm2CWRjh07Zvdmq+wyefJkbd261fb35cuXNXjwYBlj1KdPn3sOQJuRfSbdGgjXYrGk+bGde7lx44bmzp2rpk2bKiEhQU8//XSyH8NhYWGSpPHjx9uNr7F69WpbQia9MnI8CxUqJBcXFx04cMDu3LiXZ555Rh4eHpo/f76WLl1qK09KStJbb72lY8eOqXbt2vdMaKTFm2++qcqVK+s///lPmufZsGGDHnvsMRljtGDBghQHWL5TvXr11LhxY50+fVqvv/66rfzGjRsaMGCAEhMTNWTIELu3lLm6umrQoEGSpIEDB9rt9wkTJmj79u0KCwtL9mai//znP6pcubLefPNNu/JixYqpR48eun79ugYMGGCX4Bg2bJjOnDmjXr16qUiRImnaDy+88IIkacSIEXa9VM6ePZvqG6cyek4AAADcD3gsKBMNHTpUkydPTjE5UKRIET3zzDOaMWOGgoOD1aJFC3l4eGjdunW6efOmevfu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Wc/l2NhYvfnmm+rfv78OHDigs2fPaubMmfLw8NBHH32kKVOmJJt37Nix+uijj5SYmKgVK1bom2++0a5du1S6dGm1atWKR+aQayxbtkwzZ86Ui4uLRo0a5dCybn+dc2b/bia5AuC+8c8//2j48OEKCwtT7969M7ychIQEPfroozp79qw6d+6s/v37Z16QQBabN2+e6tWrp8DAQG3cuFGXLl3S3r171bt3b40fP14tWrTQtWvXJN26KBg0aJAqVqyoN998M4cjB7JHWr7jrXf2/fz89N///lehoaHy9vZWy5Yt9eOPP8pisejbb79Nd5dwILtZz+UbN26obdu2mjJlisqVKyd/f3/17dtXH374oSRp9OjRdvNdu3ZNjz/+uIYOHaq33npLhw4dUnx8vFavXi13d3c9+eSTtnmBnLR792716tVLxhh9+OGHtrFXciOSK3lI/vz5JUmXL19OsT4hIUGS5O3tnW0xAZlp4MCBun79uqZPn57hZSQmJqpbt27asmWLmjRpkmzcCiA327dvnyIiIlSoUCEtWbJE9erVk5eXlypUqKBPPvlEjzzyiKKiovT5559Lkt566y0dPXpUU6dOpXcWHghp/Y63XjN169bNdofSqnr16qpbt64k6ddff83agAEHWc9lSerTp0+yeuvNqGPHjmn//v228tGjR2vhwoUaNGiQ3nvvPZUpU0be3t5q3ry5li5dKi8vL40cOVJnz57N8m0AUnPs2DG1adNG58+f1yuvvKIXX3zR4WXe/pnJ7N/NJFfykFKlSkmSjh49mmK9tbx06dLZFhOQmZYsWSJPT0/1799fzZo1s/174oknJN36AraWnTx5Mtn8SUlJioiI0H//+1+FhITop59+ossr7ivz589XYmKi2rRpY3dxYNW9e3dJ//tB+NNPP8nd3V2jRo2y+8w0a9ZMknTz5k3b39HR0dm1GUCWSM93vPVaqEyZMinWW8tPnz6dFaECmeb26/qUzmdPT08VKVJEkv35/NVXX0lK+W0ppUqVUv369XX58mX9+eefmRwxkDbnzp1T69atdfjwYfXp00fjxo3LlOX6+PjY3oKV2b+bGdA2D7F2kYqKikqx3lqenkF5gNzmwoULWrt2bYp1V69etdWl9Oq0wYMHa968eapYsaJWrFjBQIW471gb+9RejWkttz6DL9l/LlJirbtw4UImRQnk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hx/WlClTbNNZLBbbD8rmzZvLYrHY/t05Xsfy5cvVvn17FS5cWG5ubipXrpxeeeUVxcbGJovp9keNVq1apYceekje3t4qUqSInn32WduYMqdPn9bzzz+vEiVKyN3dXfXq1cvQ40mJiYmaPn26mjRpogIFCsjDw0Ply5dXnz599Oeff0qSvv32W1ksFvXs2TPV5Tz33HOyWCz64osv7MoTEhI0ZswY1alTRz4+PvLy8lLlypU1cOBA7d27N81xpmcfpmTWrFm2R1wOHz5sd7yaNWtmmy61MVesjxPduHFDo0aNUvny5eXh4aEqVarYbfPq1avVvHlz+fj4yM/PT08//XSqMd64cUPTpk1Tw4YN5ePjIw8PD4WEhOijjz7SjRs30rxvJOnzzz9XUlKSevTokeo0P/74oxo2bChPT0/5+/ura9euKR4D6/g3hw8ftm279d+dj1R17txZHh4emjFjRrriBQAgM/FYEAAAaRQYGChJ+u677/Tmm2/e8+68m5ubIiIiNGHCBM2dO1cDBw5MNo31B+Fzzz1nKxs2bJjGjRsnNzc3PfTQQypUqJBOnjyp7du3a//+/bblREREaP369Tpw4IDCw8MVEBBgW0b+/Plt///GG29ozJgxcnV1Vd26dVWsWDFt27ZNEydO1I8//qgNGzaoaNGiyWJbtGiRpkyZooYNG6pNmzb6448/9Nlnn2nfvn369ttv1bBhQ928eVNNmzZVTEyMNm7cqDZt2mjz5s2qUaNGmvZpQkKC2rVrp19//VVeXl62BEtMTIzmzp0rX19f1a5dW506dVJAQIC+//57xcbGJnv849KlS5o3b558fHz0+OOP28pPnDihVq1a6e+//5afn5+aNWsmNzc3HTx4UNOnT1eFChVUsWLFe8aZ0X14u/LlyysiIkKzZ8+Wl5eXHnvsMVtdeno7de/e3ZZACQoK0tq1a9W3b19Jkre3t3r06KEGDRooPDxcv//+u7766isdOnRIv/76q934JVeuXFH79u0VGRmpggULqkGDBnJ3d9fGjRv18ssvKzIyUosWLVK+fGm7F7dkyRJJsksU3W769Ol64YUXZLFY1LRpUxUrVkx//PGH6tWrpw4dOthNGxAQoIiICH377bdKSEhQRESEra5QoUJ20+bPn1916tTRunXrdPDgQZUrVy5N8QIAkKkMAABIkwMHDhgPDw8j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aeXKlSnOd7fz+3a7du2SdKsnDgAAOYHkCgAAafTOO+/otddeS7EnxLFjx/T8889Lkjp27Gj7UXg763ghEydOlJRyQuCff/7RrFmzdPnyZbvyq1evKjIyUtL/emRIUvHixSUp1R+177zzjpKSktS1a1dFR0cnq4+NjU020G52KV68uJ5++mldvXpVERERio2Ntas/ffq0Nm7cmGy+YsWKqWPHjtq6davWrl2rmjVrql69esmmq1evnpo3b67Tp0/rueeeU0JCgl19TEyMduzYcc84M3sfFi9eXKdOncrR8UFKlCihvn37KiYmRj169NCpU6eSTbN//3599913aVqel5eXQkNDdeLECR07dixZfZ8+feTm5qa5c+faBhqWpMTERL388svJjo3Vvc5vq02bNkmSwsLC0hQvAACZjceCAABIo0uXLmnSpEkaN26cKlasqKpVq8rd3V1Hjx7Vxo0blZiYqPLly+ujjz5KcX7reCFHjhxJ9mYbq3PnzqlPnz4aOHCg6tSpo5IlSyohIUG//fabzpw5ozp16ujRRx+1Td+hQwe9//77evXVV/Xzzz/besyMGTNG/v7+6tmzp/7++2/9+9//Vu3atRUSEqKgoCAZY3TgwAFt375d+fPnTzHRkx0mTZqkPXv2KDIyUqVLl9ZDDz0kHx8fHT58WFFRUXrhhRdUv379ZPP1799f33//vST7ty3d6auvvlLLli01b948rVixQk2aNJGbm5sOHDig6OhojR8/XjVq1LhrjJm9Dzt27KjJkyerVq1aatSokdzd3VWpUiW99tpraZo/s0yaNEkxMTH67rvvtHz5coWEhKhUqVJKSEjQzp07tX//fnXq1Eldu3ZN0/Lat2+vzZs3a82aNXryySft6sqWLavx48dr0KBBCg8P10MPPaSAgAD98ccfOn/+vJ588knNnTs32TI7duyotWvXqmXLlmrevLm8vLxUqFAhffDBB7ZpLl26pC1btqhy5cq2NyMBAJDtcvZN0AAA3D/OnDljvvrqK9OrVy9To0YN4+/vb5ydnU3BggVN48aNzdixY82lS5fuuoxevXoZSeaTTz5JsT4+Pt6MHz/etGvXzpQpU8a4u7sbf39/U6dOHTNx4kSTkJCQbJ65c+eaWrVqGQ8PDyPJSDKHDh2ym2bt2rWmW7dupnjx4sbFxcX4+/ubmjVrmkGDBpm1a9faTRsREWEkmcjIyGTrioyMNJJMREREivGXLl3apPfy4tq1a2bSpEmmXr16Jn/+/MbDw8MEBQWZPn36mD///DPFea5cuWJcXFyMh4eHOX/+/F2XHx8fb95//31Ts2ZN4+HhYfLnz28qV65sBg0aZPbt25fmbUvPPrybS5cumUGDBpnAwEDj7OxsJJmwsDBbfVhYWIrHUJIpXbp0isvM6DG7ceOGmT17tmnRooUpWLCgcXFxMcWLFzcNGzY07733ntmzZ0+at+uff/4xTk5Opl27dqlOs2jRIlO/fn3j4eFh/Pz8TKdOncyuXbvMiBEjjCTzxRdf2E2fmJhohg8fboKCgoyLi0uK++DLL780ksz48ePTHCsAAJnNYsw9HiAHAACZ4vLlyypRooRu3Lih48ePp/raWtzbvHnz1LNnT0VEROSpgWbvd126dNGSJUt05MgRBQQEZMs6w8PDtX79ev3zzz/y9/fPlnUCAHAnxlwBACCbTJkyRRcuXFBERASJFQckJiZqzJgxkqSBAwfmcDS43ahRo5SUlKRx48Zly/qioqK0cuVKDR06lMQKACBH0XMFAIAsFBsbq9dff12nTp3SsmXL5OnpqV27dtlehYu0+/HHH7V48WJt2rRJf//9tzp37qxFixbldFi4Q9++fbVgwQIdOnTI9ormrNK5c2f9/vvvOnDggPLnz5+l6wIA4G5IrgAAkIViYmJUtmxZubq6qkaNGho3bpyaNWuW02Hdl0aOHKn33ntPfn5+atu2rSZPnqyCBQvmdFgAAAAkVwAAAAAAABzBmCsAAAAAAAAOILkCAAAAAADgAJIrAAAAAAAADiC5AgAAAAAA4ACSKwAAAAAAAA4guQIAAAAAAOAAkisAAAAAAAAOILkCAAAAAADgAJIrAAAAAAAADvj/9nM4T6ZCOBgAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "with pulse.build(backend, name='Left align example') as program:\n", - " with pulse.align_left():\n", - " gaussian_pulse = library.gaussian(100, 0.5, 20)\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(0))\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(1))\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(1))\n", - "\n", - "program.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice how there is no scheduling freedom for the pulses on `D1`. The second waveform begins immediately after the first. The pulse on `D0` can start at any time between `t=0` and `t=100` without changing the duration of the overall schedule. The `align_left` context sets the start time of this pulse to `t=0`. You can think of this like left-justification of a text document.\n", - "\n", - "\n", - "## `align_right`\n", - "Unsurprisingly, `align_right` does the opposite of `align_left`. It will choose `t=100` in the above example to begin the gaussian pulse on `D0`. Left and right are also sometimes called \"as soon as possible\" and \"as late as possible\" scheduling, respectively." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.121509Z", - "iopub.status.busy": "2023-08-25T18:25:52.121059Z", - "iopub.status.idle": "2023-08-25T18:25:52.272360Z", - "shell.execute_reply": "2023-08-25T18:25:52.271385Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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I0eOTLVc1n3w2WefNe7u7qZdu3amR48epmTJkkaSadGihUlJSbEbJ6vHxbRs27bNdi64cuWKrXzTpk22ZXHk4sWLRpIpWrSoXbl13/7zzz8djtegQQMjyeEx6Vbnzp2zLevFixcdDlO8eHEjycTHx992esYYs2HDBuPr62skmdq1a5t+/fqZ+vXrG4vFYh599NFUv8OM/CYAAADyEsmVTLo5uXLixAnj7e1tfHx8zMmTJ23DpJVc2bp1q/njjz9STfO7774z7u7uJjQ0NNUFvPWiv0yZMmbv3r228h07dhgPDw/j7e1tKlasaKZNm2arS0pKMq1btzaSUt38vfnmm0aSadWqlV1SISkpyQwdOtRhsignkistW7Y0kszcuXNT1Tm6gbt+/botIfLPf/7TbvgPPvjANq+0kisuLi5myZIltvJr166ZXr16GUnm5Zdfthtn7NixRpL56KOPMrW8xhjz66+/mv3796cq//DDD40kExUVlaouM8mVn376yUgyxYoVM9u2bbOVJyYmmujoaNt6cHTzK8nUrFnTbrvv37/fBAYGGkl2v6/0HDhwwPj4+BhfX1/z/fff29X95z//MW5ubiYkJMQkJSXZyi9dumRLMn3++efGmBv7g4eHhwkICDAHDx60m87p06fN999/n2p/2L9/v6lYsaJxcXExBw4csKu7eVsvXbrUVp6QkGBq165tJJlatWqZgQMHmqtXr9rqx4wZYySZBx54wG561pt3a11ycrKt7ptvvjGurq7G29vbHDlyxG48R9stK+ssqzK6r/7jH/8wkkz//v1tZc4kVySZKlWqpFofxji3PW+dl1VayZXHHnvMtq8lJCTYynfs2GFLlnzzzTd241j3weDgYLNz505b+alTp2zJ6h9++MFunOxOrgwbNsxIMl26dLEr/+qrr4wkExERkea41n3Yurzx8fG22NJKdnTv3t1IMl9//fVtY9uyZYvDBM7NwsPDjaRUSUpHUlJSTK1atWwPCG42ZcqU2x7P0/pNAAAA5CVeC3JCqVKl9MgjjygxMVFvvPHGbYevU6eO7XWYm0VHR6tPnz7at2+f/vjjD4fjvvbaa3b9b9SoUUMdO3bUpUuXVK5cOQ0fPtxW5+7urieeeEKStGbNGlv5tWvX9Oabb8rHx0cLFixQcHCw3TjvvfeegoOD9f777yslJcVW5+bmpurVq6t69eq3Xcb0pKSkaN++fXrkkUf0448/qlu3brr33nszNO6qVau0e/duVa1aVc8//7xd3dChQ9WsWbN0x+/fv7/tFQVJcnV11QsvvCDpxutC2aVJkyaqVKlSqvLBgwerWbNmWr16te31lKyYPn26JOmpp55S7dq1beXe3t5699135eKS/i797rvv2m33SpUq2ToU/umnnzIUwzvvvKPExERNmDBBbdq0satr3769HnnkER0+fNju86xeXl6aN2+e3N3d9fDDD2vfvn0aMGCAkpKSNGXKFFWoUMFuOkFBQWrTpk2qT1RXqlRJL730klJSUtJ8BeG+++5Tp06dbP/28/PTgw8+KEk6cuSI3n33XbuOgp955hlZLBa7feVmvr6+euedd1SkyP/6/+7cubN69+6tS5cu6aOPPkpvdUnK2jrLada+Ss6dO5dt05wwYYLKli2bqtyZ7ZkZiYmJmjVrllxcXDR16lT5+fnZ6mrUqKExY8ZIUpp9g4wfP97uOFe8eHHbsfXW40R2HRelG6/OzZo1S25ubho/frxdnfXzzOn1QeXj4yNJttfubv6kc1rj3TpOerISQ3pWr16t7du3q3LlynrllVfs6h599FE1btz4ttMAAADIb/hakJOee+45TZ8+XdOmTdOzzz6rUqVKpTt8UlKSvvvuO61fv16nTp2ydVy4bds2SdKePXtUp06dVOO1a9cuVVnlypVvW3f8+HFbWVxcnE6fPq22bds6jNPLy0v169fXsmXLtGfPHttNQ9myZbVz5850lys9t95QSdKDDz6oGTNmOKxz5Oeff5Yk9erVy2EC4d5777UN44ijdVStWjVJ9usoO1y8eFHffPONNm/erLNnzyo5Odk2H2OM9u3bp3r16mVp2tZl7NOnT6q6atWqKTw8XHFxcQ7HdXNzU1RUlMPxrPFlxIoVKyTJrpPMm7Vo0ULvvvuu1q9frx49etjKIyIi9I9//EOjR49WvXr1lJCQoP79+2vAgAFpzmvt2rVavXq1jh49qitXrsgYY4tzz549DsdJb39o0KBBqq97BQQEqFixYmkuf7t27Rx+Eax///767LPPMpSUyuo6y0nmv/01ZXQfvB2LxaIuXbqkO0xWtmdmbNy4UZcvX1aDBg1Uo0aNVPX333+/Ro4cqZ9//lkpKSmpjiWZOU44e1y02rlzpwYOHChjjP7973/b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RZJUr149h+NZy7du3Zo7gQIAgDsOF9MAACs6yy3YCn1yRZLCw8O1aNEi7dixw1b2119/SZLKlSvncBxr+aFDh3I+QAAAUOiQOAEAOCsj5xISMPnDHZFcKV68uCTp3LlztrKLFy9Kkry9vR2O4+PjI0m6cOFCDkcHAAAKOhIpAIC8QouX/OGOSK5YO7y1doALAACQUSROAAAFDS1ect8dkVw5ffq0JKlYsWK2Mutnly9duuRwnMTEREmSn5+frWzbtm22Fi0AAKDgO3y+aF6HAABAnth/4PbDhASeu/1A+Yz1Xj633RHJlU2bNkmSatWqZSsrX768JOnIkSMOx7GWV6hQwVbWoX10ToUIAAAAAAAKqEKfXImPj9fy5cslSVFRUbbysLAwSVJcXJzD8azldevWtZX957vltFwBAAAAACCfSkxMzJOGEYU+uTJq1CglJiaqYcOGatq0qa28WbNmCggI0L59+7R582aFh4fbjbdo0SJJUpcuXWxlderUkb+/f67EDQAAAAAAMichISFP5uuSJ3PNBfv379e9996rWbNmycfHR7NmzbKrd3d312OPPSZJGjFihN17WW+//ba2bt2qyMhI1a9fP1fjBgAAAAAABUuhaLkyaNAgSVJKSooSEhK0e/du7dy5U8YYVa1aVZ9++qnq1KmTarwxY8Zo5cqV+uWXX1S1alW1aNFChw4d0rp161SiRAl9+OGHubwkAAAAAACgoLEY63eKC6BbP61cpEgR+fv7q0yZMqpfv766deumrl27ytU17c9QXb58WRMmTNCnn36qw4cPq1ixYmrfvr3Gjx+vcuXKSbrRrCggIEBHjh7jtSAAAAAAAPKphIQElStbRvHx8bl6/16gkyu5heQKAAAAAAD5X14lVwptnysAAAAAAAC5geQKAAAAAACAE0iuAAAAAAAAOIHkCgAAAAAAgBNIrgAAAAAAADgh3yZXvvvuO4WHh8vT01MWi0Xnz5/P65AAAAAA5LKl33yj7t26qkH9emrevFlehwMADuXL5MqZM2fUt29feXl5acqUKfrkk0/k4+OT12EBAAAABdLy5csVHlZXq374IVVd3z69FR5WVxvWr09V1z66nR544P7cCNGhAwcO6JVXXla5kBC9/MpYvfzyK3kWCwCkp0heB+DIhg0bdOHCBY0fP15t2rTJ63AAAACAAi0iIkKStGnT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KSm2+nHjxumJJ57QpEmT7MaLjIzUs88+q3//+9+qUqWKWrZsKX9/f/3xxx/au3evBg0axDkD+crkyZMlSe3bt3cq6ZeQkGA75qd33/z777/r0KFDqlu3boanTcuVQsT6aSpvb2+H9T4+PpKkCxcu5FpMQHZ77733tGHDBv373/92+OpDRh0+fFhPPvmkJOm1117jyQwKjGbNmmnNmjWqXLmy4uLi9Nlnnyk2NlYuLi5q27atKleubDf8V199pcWLF+v555+3vSIBFHa3O8afO3dOkvT000+rbt26iouLU0JCglauXKlKlSpp9erVevDBB3M7bCDTrL/lM2fO6IUXXtDw4cO1b98+nT59WrNmzZKXl5feeecdTZkyJdW4b775pt555x0lJydr+fLl+vzzz7Vjxw5VqFBBbdu25ZU55BvffvutZs2aJTc3N40fP96pad38Oefsvm8muQKgwPjrr780ZswYRUZGatCgQVmeTmJionr27KnTp0+re/fuGj58ePYFCeSw+fPnq1GjRgoJCdG6det08eJF7d69W4MGDdLEiRPVunVrJSUlSbpxUfDYY4+pWrVqeuGFF/I4ciB3ZOQYb32yX7RoUf3nP/9RRESE/Pz8dM899+jrr7+WxWLRokWLMt0kHMht1t/ytWvX1KFDB02ZMkWVK1dWUFCQhgwZon//+9+SpAkTJtiNl5SUpHvvvVejRo3Siy++qAMHDighIUGrVq2Sp6enBgwYYBsXyEs7d+7UwIEDZYzRv//9b1vfK/kRyZVCxNfXV5J06dIlh/WJiYmSJD8/v1yLCchOI0aM0NWrVzV9+vQsTyM5OVl9+vTR77//rubNm6fqtwLIz/bs2aOYmBgVL15cS5cuVaNGjeTj46OqVatqxowZ6ty5s+Li4vThhx9Kkl588UUdOXJEU6dOpXUW7ggZPcZbr5n69Olje0JpVbt2bTVs2FCS9OOPP+ZswICTrL9lSRo8eHCqeuvDqKNHj2rv3r228gkTJmjhwoV67LHH9Oqrr6pixYry8/NTVFSUli1bJh8fH40bN06nT5/O8WUA0nL06FG1b99e586d09NPP60nnnjC6WnevM9k930zyZVCpHz58pKkI0eOOKy3lleoUCHXYgKy09KlS+Xt7a3hw4erVatWtr9+/fpJunEAtpadOHEi1fgpKSmKiYnRf/7zH4WHh+ub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AAAFxcaNGzVu3Di5ublp4cKF6t69u139iRMnNHfuXAUGBuZJfCgcPv74Y126dElly5bN61AybcyYMXJ1ddUzzzyTK/OrV6+eoqOjNXHiRD3xxBMKCgrKlfkCAHArWq4AAJBBixcvljFGffv2TZVYkaTg4GA988wzqlGjRu4Hh0KjfPnyqlGjhtzc3PI6lEw5fPiwli5dqujo6GxptZJRAwcO1KVLlzRnzpxcmycAALciuQIAQAadOnVKklSiRIkMDZ+UlKTixYvL29tb58+fdzjML7/8IovFosjISFuZMUbz5s1T8+bNVapUKXl6eiokJERt2rTRlClTbMNZLBbbDWVUVJQsFovt79b+Or777jt16tRJJUqUkIeHhypXrqynn35aZ86cSRXTza8arVy5Ui1btpSfn59KliypBx980NanzMmTJ/Xwww+rbNmy8vT0VKNGjbL0elJycrKmT5+u5s2bKzAwUF5eXqpSpYoGDx6sjRs3SpIWLVoki8Wi++67L83pPPTQQ7JYLProo4/syhMTE/XGG2+oQYMG8vf3l4+Pj2rUqKERI0Zo9+7dGY4zM+vQkdmzZ9tecTl06JDd9mrVqpVtuLT6XLG+TnTt2jWNHz9eVapUkZeXl2rWrGm3zKtWrVJUVJT8/f1VtGhRPfDAA2nGeO3aNU2bNk1NmzaVv7+/vLy8FB4ernfeeUfXrl3L8LqRpA8//FApKSnq379/msN8/fXXatq0qby9vRUUFKRevXo53AbW/m8OHTpkW3br362vVHXv3l1eXl6aOXNmpuIFACA78VoQAAAZFBISIkn64osv9MILL9z26byHh4diYmL09ttva968eRoxYkSqYaw3hA899JCtbPTo0Xrrrbfk4eGhli1bqnjx4jpx4oS2bt2qvXv32qYTExOjtWvXat++fYqOjlZwcLBtGr6+vrb/f/755/XGG2/I3d1dDRs2VOnSpbVlyxZNmjRJX3/9tX7++WeVKlUqVWxLlizRlClT1LRpU7Vv316//fabPvjgA+3Zs0eLFi1S06ZNdf36dbVo0UIHDx7UunXr1L59e23YsEF16tTJ0DpNTExUx44d9eOPP8rHx8eWYDl48KDmzZungIAA1a9fX926dVNwcLAWL16sM2fOpHr94+LFi5o/f778/f1177332sqPHz+utm3b6s8//1TRokXVqlUreXh4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mf6t8vs6zCrrcjVv3lzvvfee09OzfjkrrVYupUuX1q5du3To0CHVqlUrVb31k8tZde7cOUkZ/0w6AADZjdeCAADIIGNMuvV79+6VdOO1oVs/F2vtL2T9+vUaM2aM4uPjNWDAALsETHqaNGmi+++/X5L0xx9/2Mrd3d0lSdeuXUs1Trly5VSjRg1t375du3fvztB8clOrVq3k6uqq5cuX217hyYhhw4apSJEimjlzZpqvBElSmzZtJMnudazMyol16Obm5nB75aaoqCi5urpq6dKlSk5Odnp6d911l4oUKZKqvyGrFi1aSJIWLlyYqu7s2bNasWKFw/HS+33fbMeOHZJutMQBACAvkFwBACCDXn75ZT377LMOW0IcPXpUDz/8sCSpa9eutpvCm1n7C5k0aZIkxwmBv/76S7Nnz9alS5fsyq9cuaLY2FhJ/2uRIUllypSRpDRval9++WWlpKSoV69e2rx5c6r6M2fOpOpoN7eUKVNGDzzwgK5cuaKYmBidOXPGrv7kyZNat25dqvFKly6trl27atOmTVqzZo3q1q2rRo0apRquUaNGioqK0smTJ/XQQw8pMTHRrv7gwYPatm3bbePM7nVYpkwZ/f3333naP0jZsmU1ZMgQHTx4UP3799fff/+dapi9e/fqiy++yND0fHx8FBERoePHj+vo0aOp6gcPHiwPDw/NmzfP1tGwJCUnJ+upp55KtW2sbvf7tlq/fr0kKTIyMkPxAgCQ3XgtCACADLp48aImT56st956S9WqVVOtWrXk6empI0eOaN26dUpOTlaVKlX0zjvvOBzf2l/I4cOHU33Zxurs2bMaPHiwRowYoQYNGqhcuXJKTEzUL7/8olOnTqlBgwbq2bOnbfguXbrotdde0zPPPKPvv//e1mLmjTfeUFBQkO677z79+eef+te//qX69esrPDxcoaGhMsZo37592rp1q3x9fR0menLD5MmTtWvXLsXGxqpChQpq2bKl/P39dejQIcXFxemRRx5R48aNU403fPhwLV68WJL915Zu9cknn+iee+7R/PnztXz5cjVv3lweHh7at2+fNm/erIkTJ6pOnTrpxpjd67Br16567733VK9ePd19993y9PRU9erV9eyzz2Zo/OwyefJkHTx4UF988YW+++47hYeHq3z58kpMTNT27du1d+9edevWTb169crQ9Dp16qQNGzZo9erVGjBggF1dpUqVNHHiRD322GOKjo5Wy5YtFRwcrN9++03nzp3TgAEDNG/evFTT7Nq1q9asWaN77rlHUVFR8vHxUfHixfX666/bhrl48aJ+//131ahRw/ZlJAAAcl3efgkaAICC49SpU+aTTz4xAwcONHXq1DFBQUGmSJEiplixYqZZs2bmzTffNBcvXkx3GgMHDjSSzIwZMxzWJyQkmIkTJ5qOHTuaihUrGk9PTxMUFGQaNGhgJk2aZBITE1ONM2/ePFOvXj3j5eVlJBlJ5sCBA3bDrFmzxvTp08eUKVPGuLm5maCgIFO3bl3z2GOPmTVr1tgNGxMTYySZ2NjYVPOKjY01kkxMTIzD+CtUqGAye3mRlJRkJk+ebBo1amR8fX2Nl5eXCQ0NNYMHDzYbN250OM7ly5eNm5ub8fLyMufOnUt3+gkJCea1114zdevWNV5eXsbX19fUqFHDPPbYY2bPnj0ZXrbMrMP0XLx40Tz22GMmJCTEFClSxEgykZGRtvrIyEiH21CSqVChgsNpZnWbXbt2zcyZM8e0bt3aFCtWzLi5uZkyZcqYpk2bmldffdXs2rUrw8v1119/GVdXV9OxY8c0h1myZIlp3Lix8fLyMkWLFjXdunUzO3bsMGPHjjWSzEcffWQ3fHJyshkzZowJDQ01bm5uDtfBxx9/bCSZiRMnZjhWAACym8WY27xADgAAssWlS5dUtmxZXbt2TceOHUvzs7W4vfnz5+u+++5TTExMoepotqDr0aOHli5dqsOHDys4ODhX5hkdHa21a9fqr7/+UlBQUK7MEwCAW9HnCgAAuWTKlCk6f/68YmJiSKw4ITk5WW+88YYkacSIEXkcDW42fvx4paSk6K233sqV+cXFxWnFihUaNWoUiRUAQJ6i5QoAADnozJkzeu655/T333/r22+/lbe3t3bs2GH7FC4y7uuvv9aXX36p9evX688//1T37t21ZMmSvA4LtxgyZIg+++wzHThwwPaJ5pzSvXt3/frrr9q3b598fX1zdF4AAKSH5AoAADno4MGDqlSpktzd3VWnTh299dZbatWqVV6HVSCNGzdOr776qooWLaoOHTrovffeU7FixfI6LAAAAJIrAAAAAAAAzqDPFQAAAAAAACeQXAEAAAAAAHACyRUAAAAAAAAnkFwBAAAAAABwAskVAAAAAAAAJ5BcAQAAAAAAcALJFQAAAAAAACeQXAEAAAAAAHACyRUAAAAAAAAn/D8mVcaPAWzHJAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "with pulse.build(backend, name='Right align example') as program:\n", - " with pulse.align_right():\n", - " gaussian_pulse = library.gaussian(100, 0.5, 20)\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(0))\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(1))\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(1))\n", - "\n", - "program.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `align_equispaced(duration)`\n", - "\n", - "If the duration of a particular block is known, you can also use `align_equispaced` to insert equal duration delays between each instruction." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.276591Z", - "iopub.status.busy": "2023-08-25T18:25:52.275760Z", - "iopub.status.idle": "2023-08-25T18:25:52.464817Z", - "shell.execute_reply": "2023-08-25T18:25:52.464072Z" - }, - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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JDNnzpxMMeRlcuWf//ynkWTq1atnV75582YjyZQoUeKG60ZGRhpJZsuWLbYyayLvRsmOJ5980kgyb7755i1jS05Oth3rtQmca/Xs2dNIMl999dUtt2eMMZ07d7Yl99LS0mzlX331lXF1dXX4PrzVewIAAADIDwwLyoXSpUvrkUceUWpqql577bVbLl+vXj3VqVMnU3mnTp3Ur18/7d27V3/88YfDdV966SWFh4fb/l2zZk117dpV58+fV/ny5TV8+HBbnYeHh5544glJV588YpWenq6JEyfK19dXn3zyicLCwuzWeeuttxQWFqZ33nnHbgiGu7u7atSooRo1atzyGK3ee+89HT16VE8++aQGDhxoV9eoUSONGzdO586ds+veb7FYNG/ePJUoUUIvvPCCNm7cqMGDB+vUqVMaP368GjVqZLcdNzc39ezZU+7u7nbloaGheuWVVyRJX375pcP4WrZsaXfO3N3d9dhjj0mSduzYoZkzZ8rX19dWP3jwYJUsWVK//vqr0tLSMm3PxcVFb731lt06jRs31ogRI5SRkaEZM2bc9HxJOTtn+a1kyZKSpKSkpDzb5rPPPuvwc5Cb1zO73nzzTUnSlClTVLp0aVt5mTJl9Prrr0tSpvlJrJ566im7OU18fHz09NNPS5J++OGHTMtbPzvXH1d2bdy4Uf/85z8lKdP3zblz52yx3Ij1vXn27Nksr+donRuxbiuvtrdv3z4tX75cAQEBmjJlitzc/jf/+j333ONw6BEAAADgLDwtKJeeffZZzZo1SzNnztTo0aPtbtQcuXTpkpYvX65169bp5MmTunz5siRp69atkqTdu3erXr16mdbr2LFjprKqVavesu7o0aO2svj4eJ06dUodOnRwGKe3t7caNmyoZcuWaffu3bZkSrly5bRjx46bHtf1Vq5cKUl2k1peq1WrVpKkdevW2ZWXL19eb7/9tmJiYtS6dWulpKSoZcuWeu655264r02bNmnlypU6ePCgzp8/L2OM7eZt9+7dDte52TmrXLmyqlevblfn6uqqSpUqacOGDTp16lSmCVIjIyMdJp8GDBig1157TT/++OMN47fK6TnLT+b/5/SxzvuTF7p3737T+py8ntnx119/6a+//lJoaKjD98Hdd9+toKAg7dmzR8eOHbNLQkqO3zvW98u1nzer7H52HDl+/Lh69+6tixcv6sknn1SXLl1yvc3C7qeffpIkde7c2eHTngYMGKBPP/20oMMCAAAAHCK5kkuhoaEaMWKEJk6cqFdffVVTp0694bJbt25V9+7dbzo55I1+0S1XrlymMj8/v1vWXfuUF+t+v/3221veLJ86dSpbPVWuZ91XixYtbrmf6/Xr10+9evXSkiVL5OPjow8//FCurq6Zlrt8+bIGDx6sBQsW3HD7eXU+r6139OScSpUqOVzH+pjj6ycfdSQ35yy/WPcVHBycZ9usWLGiw/LcvJ7ZYX0tbvSaWSwWVapUSWfOnFFCQkKm5Er58uUzrePv7y/J8Xsjt86ePauuXbvqwIED6tevn8On7VjfmzebkDo1NdUuVut6SUlJN1zP0To3Yo3BGkdAQECutner1+n6R4gDAAAAzkRyJQ+MHj1aM2bM0KxZszRmzBiHyxhjFBMTowMHDmj48OEaPny4qlatKj8/P1ksFj3//PN65ZVXbD0FruficuMRXDeru5Z1qE+1atVueQMfEhKSpW3eal99+/a1GypzvZo1a2YqO3r0qK2nx/nz57Vt2zaHN1JTpkzRggULVK9ePU2cOFENGjRQiRIl5O7url27dqlGjRr5ej7zWm7OWX7ZuHGjJKl27dpZXsfRU5qu5eXl5bA8N69nXrtZ8rEg3x8XL15U9+7dFR8fr44dO2r+/PkO929NWCUlJSk1NdXh+8f6NKNrkxUVK1ZUUlKSDh8+rPr162dpnRsJCAhQYGCgkpOTdfjwYYfvmexsDwAAAChKSK7kgZIlS+qxxx7TK6+8oldeeUVly5bNtMyOHTu0Y8cONWrUSDNnzsxUv2/fvnyP0/qLe82aNTM9Jjc/9rVz504999xzatiwYZbXM8ZoyJAhOnXqlAYMGKCFCxdq6NCh2rp1q+0RtFZLliyRJC1YsCDTHB4FcT6vdaPHXlvLHb0nrpfTc5Zfrly5Ynv8dJs2bWzlHh4ekuzn2LjWoUOHcrS/gno9ra/FjV6za+tu1IupIKSnp+vee+/V6tWr1bx5cy1evNh27q8XFBSkihUr6q+//tLGjRvVsmVLu/pDhw7p1KlTqlSpkl2PkoiICG3evFnx8fHq2rVrpu3Gx8dLksPEiyMRERH64YcfFB8fnym5kpaWpj/++ENeXl6Zht05Yh16d6vPFgAAAFAYMKFtHhk1apT8/f31zjvvKCEhIVO9dUJQR0MKkpKS9O233+Z7jI0bN1ZgYKDWrFmj06dP5+u+OnToIOl/N8xZ9dZbb2nFihVq0aKFPvzwQ73wwgs6fvy4HnjggUzL3uycLly4MAdR59ymTZsczgfyySefSFKmm11HcnrO8su///1v/fXXXypXrpz69OljKy9ZsqTc3Ny0f/9+paen262TlpZmN4lyduTk9bQmG66P42YqVqyoihUr6uTJk/r+++8z1S9btkxJSUmqVq1apiFBBcWaZPzqq68UGRmpZcuW3bQ3kyR169ZNkmwJsWtZy+65554sr7Nx40bt27dPdevWzfIQnJttb+nSpbp48aLat29/w95L17J+ZpYvX66UlJRM9dbP1vVy8p4AAAAAcovkSh4JCQnR448/rkuXLmnOnDmZ6qtVqyYXFxetWrXK7ib84sWLGj58eL4nOyTJ09NTY8aM0dmzZ9W7d2+HvQESEhL04YcfZiqrWbNmtoajPPzwwypVqpQmTpyY6elD0tUbnxUrVtg9HWnbtm169tln5e/vb5tnZdy4cWrSpIm+/vprzZo1y24b1l+/ry9ftGiRPvjggyzHmhcyMjL02GOP2c1dsWHDBv3nP/+RxWLRI488cstt5OScSVefZGSxWDRhwoQ8OZZjx47pscce04svvihXV1e9//77dj0mPDw81KxZM50+fVrTp0+3i2/UqFHav39/jvabk9fT2gtl586d2dqX9clQTz/9tE6ePGkrP3bsmEaPHi1Jtidu5Zb1s+Mo6XojTz75pObPn6+aNWtq5cqVCgoKuuU6TzzxhFxdXTVr1iz99ttvtvLdu3frX//6l9zc3DIdU69evVSlShVt3rzZbr6o1NRUjRgxQtLVxPH12rVrp5o1a2aaXHnYsGEKCAjQl19+qcWLF9vKT5w4YRsy6Wh7joSHh6tjx45KSUnRqFGjdOXKFVvdN998o88++8zhejl9TwAAAAC54rSHQBdRkoyrq6vDutOnT5uAgAAjyUgy48ePt6t/8MEHjSTj7e1tunXrZvr27WtKly5tSpYsaQYPHmwkmffff99unUqVKpkbvUzjx493uI4xxuzfv99IMtHR0XblV65cMX/729+MJOPh4WGaNm1q+vfvb3r37m3q1KljLBaLiYiIcLit7L5dfv31V1OyZEkjyVSoUMF06dLF3HfffaZt27YmKCjISDJLliwxxhhz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io6OtpUZY/TRRx+pZcuWKl26tLy8vFShQgW1b99e06dPty1nsVhsN5Rt2rSRxWKx/V0/X8fy5cvVrVs3hYaGytPTU1WrVtXTTz+txMTETDFdO9Tou+++01133SV/f3+VKlVKDz74oG1OmRMnTujhhx9WuXLl5OXlpSZNmuRoeFJaWppmzZqlli1bKigoSN7e3qpWrZqGDBmiDRs2SJIWLVoki8Wi++6774bbeeihh2SxWPT+++/blaempuq1115To0aNFBAQIF9fX9WsWVMjRozQrl27shxnds6hI3PnzrUNcTl48KDd69W6dWvbcjeac8U6nCg9PV0vv/yyqlWrJm9vb9WqVcvumFetWqU2bdooICBAJUqU0P3333/DGNPT0zVz5kw1a9ZMAQEB8vb2VmRkpN544w2lp6dn+dxI0nvvvaeMjAwNGDDghst89dVXatasmXx8fBQSEqI+ffo4fA2s898cPHjQduzWv+uHVPXs2VPe3t6aPXt2tuIFACAvMSwIAIAsqlChgiTp888/19///vdb/jrv6emp2NhYTZkyRR999JFGjBiRaRnrDeFDDz1kKxszZowmTZokT09P3XXXXSpZsqSOHTumLVu2aM+ePbbtxMbG6qefftLevXvVqVMnhYWF2bbh5+dn+//nnntOr732mjw8PNS4cWOVKVNGmzdv1tSpU/XVV1/p559/VunSpTPFtmTJEk2fPl3NmjVT586d9dtvv+ndd9/V7t27tWjRIjVr1kxXrlxRq1atdODAAa1du1adO3fW+vXrVa9evSyd09TUVHXt2lU//PCDfH19bQmWAwcO6KOPPlJgYKAaNmyoHj16KCwsTIsXL1ZiYmKm4R/nzp3TggULFBAQoHvvvddWfvToUXXo0EF//vmnSpQoodatW8vT01P79u3TrFmzdMcdd6h69eq3jDOn5/Ba1apVU2xsrObNmydfX1/17dvXVped3k4xMTG2BEp4eLjWrFmjoUOHSpL8/f01YMAA3XnnnerUqZN+/fVXffjhh9q/f79++OEHu/lLLly4oG7duikuLk7BwcG688475eXlpbVr1+qpp55SXFyclixZIheXrP0Wt3TpUkmySxRda9asWXrkkUdksVjUqlUrlSlTRr/99puaNGmie+65x27ZsLAwxcbGatGiRUpNTVVsbKytrmTJknbL+vn5qVGjRvrxxx+1b98+Va1aNUvxAgCQpwwAAMiSvXv3Gm9vbyPJ+Pv7m9jYWDN79mwTHx9v0tPTHa6zc+dOY7FYTERERKa65ORk4+PjY0qUKGEuXLhgjDHmwoULxtPT0/j7+5t9+/bZLZ+WlmZ++OEHu7LY2FgjycTFxTnc/8KFC40kU7duXbN7925beUZGhnnxxReNJHPvvfc63KaLi4tZunSprTwlJcXUrVvXSDK1a9c2gwYNMpcvX7bVjx071kgy999/v8NYHHnggQeMJHPXXXeZEydO2NUdO3bM/Pbbb7Z/P//880aSmTp1aqbtzJ4920gyjzzyiF15u3btjCQTExNjzp49a1e3f/9+s3nzZtu/4+LijCQTGxtrt1xOzuHNSDKVKlW6YX10dLSRZPbv359pPWsc156rVatWGUmmTJkyJiQkxO41S05ONnXq1DGSzKpVq+y29+ijj9piP3PmjK08JSXFdO3a1UgyM2fOzNIxnT171ri6upqyZcs6rD9w4IDx8vIy7u7uZvny5bbyy5cvm4EDB9qO7f3337dbr1KlSiYrl6ujRo0yksx7772XpXgBAMhrJFcAAMiG7777zlSoUMF2M2j9CwoKMo888og5cuRIpnXatm1rJJl169bZlc+cOdNIMo8//rit7Pjx40aSiYyMzFI8t0quREREGElm69atmeoyMjJMZGSkcXV1NSdPnsy0zUGDBmVaZ9q0aUaSCQgIMKdPn7arO3PmjLFYLDdNHFwrISHBuLq6Gk9PT3PgwIFbLn/gwAHj4uJiateunamuadOmRpKJj4+3la1du9ZIMqVKlTIpKSm33P6Nkis5OYc3k9vkynfffZdpnaioqFu+ZuPHj7eVHT9+3Li7u5sKFSqY8+fPZ1rn6NGjxsPDw9SvXz9Lx2Q9123atHFYb01COUq8nTp1yvj4+OQquWJNrl37WQIAoCAx5woAANnQrl077dmzR4sXL9bw4cPVoEEDubm56cyZM5o5c6YiIyO1c+dOu3WGDx8uSZnmhHA0JKhUqVIqX768Nm3apOeee0779u3LcawnTpzQ5s2bdccdd6hu3bqZ6i0Wi1q0aKErV67Y5ja5VseOHTOVWYdcNGrUSCVKlLCrCwwMVHBwsI4ePZql+FavXq0rV66oc+fOqlSp0i2Xr1Spkjp37qxt27bpl19+sZVv3bpVa9euVaNGjRQVFWUr/+677yRJAwYMkL+/f5Ziul5uz2Fec3d3dzjsxvq63Ow1u/Z1Wb16tdLS0tS5c2d5e3tnWicsLEx33HGHtm7dqgsXLtwyrhMnTkhSpveE1Y8//ihJ6t+/f6a6kJAQh3FnR3BwsKT/zYsEAEBBI7kCAEA2eXh4qFevXpo5c6Y2bNigkydPaubMmSpRooROnDihkSNH2i3fs2dPhYWFacGCBTp37pwkKT4+XvHx8WrWrJnq1Kljt/y8efMUGhqq1157TeHh4apcubJiY2P13//+N1txWidE3b17t92EoNf+WSfIPXXqVKb1HT0K2DqXy40eE+zn56fLly9nKb5Dhw5JksLDw7O0vOQ4UWX9/wcffDDX279ebs9hXgsLC5Orq2um8pu9Lta6S5cu2cqsxzV79uwbHteff/4pY4xOnz59y7iskxzfKIl15MgRSbphEu36SWqzKyAgQJJuOHE0AAD5jQltAQDIpaCgIA0fPlxly5ZVjx49FBcXp/Pnz8vHx0fS1d4GQ4cO1b///W998sknGjZsmN59911JmRMCktS2bVvt2bNHS5cu1fLly7V69Wp98MEH+uCDD9SnTx8tWrQoS3FlZGRIunpD3qlTp5su6+im92YTmWZ1ktO81rVrV1WoUEELFy7UtGnT5OHhofnz58vPz++mT6nJqdyew7x2q/Oe1dfFelyRkZGKiIi46bKenp633F5gYKAk6ezZs1naf16zJneCgoKcsn8AAEiuAACQR9q2bStJunLlis6cOWNLrkhXh/68+uqrmj17tu677z59/PHHmZ5sc62AgADdd999tkcP//bbb+rXr58+//xzffPNN+ratest4ylfvrykq09XmTt3bi6PLu9Zn760d+/eLK/j6uqqBx98UC+++KI++ugjBQQEKCkpScOGDcvUayIn279eYT+HOWU9rpYtW+qtt97K9fasT866US+XMmXKaOfOnTp48KBq166dqd76yOWcSkpKkpT1x6QDAJDXGBYEAEAWGWNuWr9nzx5JV4cNXf+4WOt8IevWrdPYsWOVnJysgQMH2iVgbubOO+/U3/72N0nSH3/8YSv38PCQJKWnp2dap3z58qpZs6a2bdumXbt2ZWk/Bal169ZydXXVihUrbEN4smLYsGFyc3PT7NmzbzgkSJLat28vSXbDsbIrP86hu7u7w9erILVp00aurq5aunSp0tLScr29OnXqyM3NLdN8Q1atWrWSJC1cuDBT3enTp7Vy5UqH693s/X2t7du3S7raEwcAAGcguQIAQBaNGzdOo0ePdtgTIiEhQQ8//LAkqXv37rabwmtZ5wuZOnWqJMcJgb/++ktz587V+fPn7covXryouLg4Sf/rkSFJZcuWlaQb3tSOGzdOGRkZ6tOnjzZt2pSpPjExMdNEuwWlbNmyuv/++3Xx4kXFxsYqMTHRrv7EiRNau3ZtpvXKlCmj7t27a+PGjVqzZo3q16+vJk2aZFquSZMmatOmjU6cOKGHHnpIqampdvUHDhzQ1q1bbxlnXp/DsmXL6vjx406dH6RcuXIaOnSoDhw4oAEDBuj48eOZltmzZ48+//zzLG3P19dXUVFROnr0qBISEjLVDxkyRJ6envroo49sEw1LUlpamp566qlMr43Vrd7fVuvWrZMkRUdHZyleAADyGsOCAADIonPnzmnatGmaNGmSqlevrtq1a8vLy0uHDx/W2rVrlZaWpmrVqumNN95wuL51vpBDhw5lerKN1enTpzVkyBCNGDFCjRo1Uvny5ZWamqpffvlFJ0+eVKNGjdS7d2/b8vfcc49eeuklPfPMM/r2229tPWZee+01hYSE6L777tOff/6pf//732rYsKEiIyMVHh4uY4z27t2rLVu2yM/Pz2GipyBMmzZNO3fuVFxcnCpVqqS77rpLAQEBOnjwoOLj4/XII4+oadOmmdYbPny4Fi9eLMn+aUvX+/DDD9WuXTstWLBAK1asUMuWLeXp6am9e/dq06ZNmjx5surVq3fTGPP6HHbv3l1vvfWWGjRooObNm8vLy0s1atTQ6NGjs7R+Xpk2bZoOHDigzz//XMuXL1dkZKQqVqyo1NRUbdu2TXv27FGPHj3Up0+fLG2vW7duWr9+vVavXq2BAwfa1VWpUkWTJ0/WyJEj1alTJ911110KCwvTb7/9pqSkJA0cOFAfffRRpm12795da9asUbt27dSmTRv5+vqqZMmSevXVV23LnDt3Tr///rtq1qxpezISAAAFzrlPggYAoOg4efKk+fDDD82gQYNMvXr1TEhIiHFzczPBwcGmRYsWZuLEiebcuXM33cagQYOMJPP22287rE9JSTGTJ082Xbt2NZUrVzZeXl4mJCTENGrUyEydOtWkpqZmWuejjz4yDRo0MN7e3kaSkWT2799vt8yaNWtMv379TNmyZY27u7sJCQkx9evXNyNHjjRr1qyxWzY2NtZIMnFxcZn2FRcXZySZ2NhYh/FXqlTJZPfy4tKlS2batGmmSZMmxs/Pz3h7e5vw8HAzZMgQs2HDBofrXLhwwbi7uxtvb2+TlJR00+2npKSYl156ydSvX994e3sbPz8/U7NmTTNy5Eize/fuLB9bds7hzZw7d86MHDnSVKhQwbi5uRlJJjo62lYfHR3t8DWUZCpVquRwmzl9zdLT0828efNM27ZtTXBwsHF3dzdly5Y1zZo1M//4xz/Mzp07s3xcf/31l3F1dTVdu3a94TJLliwxTZs2Nd7e3qZEiRKmR48eZvv27Wb8+PFGknn//fftlk9LSzNjx4414eHhxt3d3eE5+OCDD4wkM3ny5CzHCgBAXrMYc4sB5AAAIE+cP39e5cqVU3p6uo4cOXLDx9bi1hYsWKD77rtPsbGxxWqi2aKuV69eWrp0qQ4dOqSwsLAC2WenTp30008/6a+//lJISEiB7BMAgOsx5woAAAVk+vTpOnPmjGJjY0ms5EJaWppee+01SdKIESOcHA2u9fLLLysjI0OTJk0qkP3Fx8dr5cqVGjVqFIkVAIBT0XMFAIB8lJiYqGeffVbHjx/XN998Ix8fH23fvt32KFxk3VdffaUvvvhC69at059//qmePXtqyZIlzg4L1xk6dKg+/fRT7d+/3/aI5vzSs2dP/frrr9q7d6/8/PzydV8AANwMyRUAAPLRgQMHVKVKFXl4eKhevXqaNGmSWrdu7eywiqQJEyboH//4h0qUKKEuXbrorbfeUnBwsLPDAgAAILkCAAAAAACQG8y5AgAAAAAAkAskVwAAAAAAAHKB5AoAAAAAAEAukFwBAAAAAADIBZIrAAAAAAAAuUByBQAAAAAAIBdIrgAAAAAAAOQCyRUAAAAAAIBcILkCAAAAAACQC/8HHF8+fK46KeoAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "with pulse.build(backend, name='example') as program:\n", - " gaussian_pulse = library.gaussian(100, 0.5, 20)\n", - " with pulse.align_equispaced(2*gaussian_pulse.duration):\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(0))\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(1))\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(1))\n", - "\n", - "program.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `align_sequential`\n", - "\n", - "This alignment context does not schedule instructions in parallel. Each instruction will begin at the end of the previously added instruction." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.469228Z", - "iopub.status.busy": "2023-08-25T18:25:52.468728Z", - "iopub.status.idle": "2023-08-25T18:25:52.747166Z", - "shell.execute_reply": "2023-08-25T18:25:52.746473Z" - }, - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "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Dn1wJbgyY8YMExMTYx566CHbMQgKCnII8m3ZssVIMgULFkyzrYiICCPJ/PHHH7YyayAvrWDH888/bySZ99577459TUxMtO3rzQGcm3Xp0sVIMt98880d2zPGmHbt2tmCe9euXbOVf/PNN8bd3d3p9/BO3wkAAAAgO/BYkAuKFSump556SsnJyXr77bfvuHytWrVUo0YNh/K2bduqZ8+e2rdvn/7880+n67722msKCwuz/V61alV16NBBFy9eVKlSpTR48GBbnZeXl5577jlJ0urVq23lKSkpGjdunPz9/TVv3jyFhobarTN58mSFhobqww8/tHsEw9PTU1WqVFGVKlXuuI9WM2bM0PHjx/X888/r4YcftqurV6+eRo0apQsXLtil91ssFs2aNUsFCxbUq6++qk2bNqlfv346ffq0xowZo3r16tm14+HhoS5dutgek7AqUqSI3nrrLUnS119/7bR/zZo1sztmnp6eeuaZZyRJO3fu1NSpU+Xv72+r79evnwoXLqxff/1V165dc2jPzc1NkydPtlunfv36GjJkiFJTU/X+++/f9nhJmTtm2c06YWpCQkKWtfnyyy87PQ9c+Twz6r333pMkTZw4UcWKFbOVFy9eXP/+978lyWF+EqsXXnjBbk4TPz8/vfjii5Kkn376yWF567lz636lxy+//KJZs2bpiy++0O+//66QkBDNmDFDbdu2tVvuwoULtr6kxfrdPH/+fLrXc7ZOWqxtZVV7+/fv1/LlyxUYGKiJEyfKw+N/868/+OCDTh89AgAAAHILbwty0csvv6xp06Zp6tSpeumll+wu1Jy5cuWKli9frg0bNujUqVO6evWqJGnr1q2SpD179qhWrVoO67Vp08ahrEKFCnesO378uK0sLi5Op0+fVuvWrZ3209fXV3Xr1tXSpUu1Z88eWzClZMmS2rlz523361bfffedJNlNanmz5s2bS5I2bNhgV16qVCl98MEH6tWrl1q0aKGkpCQ1a9ZMf/vb39Lc1ubNm/Xdd9/p0KFDunjxoowxtou3PXv2OF3ndsesXLlyqly5sl2du7u7ypYtq40bN+r06dMOE6RGREQ4DT716dNHb7/9tn7++ec0+2+V2WOWncz/z+ljnfcnK3Tq1Om29Zn5PDPi8OHDOnz4sIoUKeL0e/DAAw8oODhYe/fu1YkTJ+yCkJLz7471+3Lz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sJFkFi9ebIwx5sqVKyYiIsJIMrNmzbJrZ/fu3cbf39/4+fmZnTt32spXr15t3N3djSRTt25d06dPH1OvXj0jyQwfPtzp/mfmmFlFRUUZSebAgQO2stjYWCPJPPDAA6Z06dImNDTU9OrVy7Rt29Z4enoaSWbkyJEObaX1uWbkmFlZP8833njDab+dsW6/e/fuJiYmxjz22GOma9euJiIiwnZMQ0NDzfLly52u//333xs3NzcjyTRu3Nh07drVlClTxhQuXNjExMRk+LtsTOY+z/j4eOPj42Pc3d1Nu3btzIABA8zjjz9u+55YP5+YmBi79VJSUkz79u2NJBMUFGS6du1qunTpYgoUKGAkmS5dupjr16/brePs87e63XfHeu44W88Za5+DgoJMy5YtTd++fU2HDh1MuXLljCTj5uZm3nnnHafrTpw40UgyHh4epn379qZz587G19fXSDLvvfee03V++eUX2zINGzY0vXr1MsWLFzeSTI8ePUxqaqrDOtbPMjY21qFuwYIFxs3NzVgsFhMdHW169Ohh++6++OKL6ToGVvv27TPFihUzkkxYWJjp3bu3ue+++4zFYjFDhgzJ1HcCAAAAyA4EVzLodsEVY4wZPXp0msGVlJQUM2HCBFO9enXj4+NjihUrZh5++GFz8ODBNC/6szq4YvX111+bjh07mqJFixpPT09TtGhRU7duXTNixAizceNGp21lJhZ3/PhxM2LECFOjRg3j5+dn/Pz8TFhYmOncubOZOXOmOX/+vDHGmJdeeslIMj179nTazgcffGC76L569aqt/NdffzUtW7Y0BQsWNAUKFDBNmjQxCxcuTHP/syu4EhMTY+Lj480jjzxiihQpYry9vU14eLjT7Rhz+881vcfMqnbt2sbDw8Ps27fPaXu32771x83NzQQHB5sqVaqY3r17m1mzZplLly7dto0lS5aY+vXrG29vbxMSEmJ69eplDhw4kKnvslVGP09jjFmxYoVp2rSpCQgIsO2P9aI/reCKMcZcu3bNTJo0yURGRtqOc7169cyUKVNMSkqKw/I5FVw5efKkee2110zLli1NqVKljLe3t/H19TWVKlUyAwYMcDg/b/XNN9+Y5s2bm4CAABMQEGCaN29uvv3229uu8+eff5ru3bubQoUKGR8fH1OjRg0zceJEhwCT1e2CK8YYs2bNGtOuXTsTHBxs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qnRpjRo9RqNGjc61vgAAAABATvDI7Q4489tvv+n8+fN6/fXXdf/99+d2d4BcERkZKUnatGmTWrZqZSu/cOGC9u7dKw8PD23evFn1GzSw1Z04cUInTpxQ23btcry/Vr//9ptSU1M1YsTLKlOmTK71AwAAAAByyl2ZuXLy5ElJUnBw8B2XvXjxYjb3BsgdRYsWVcmSJbVpU5xd+R9btsgYo/tbt3aos/5uDczkhrNnz0qSChQokGVtXrp0KcvaAgAAAICslqHgytixY2WxWLR3717169dPwcHBCgoKUv/+/R2CHCkpKXr99dcVFhYmb29vlStXTq+88oquXLly2220aNFCMTExkqT69evLYrGoX79+trqaNWtq48aNuu++++Tn56dXXnlFknTlyhWNGTNGFStWlLe3t0qXLq0RI0Y4bO/KlSt64YUXVKRIERUoUECdOnXS0aNHZbFYNHbs2IwcDiDbRUZGaufOnbp8+bKtbPPmzQoLC1Ozps30x9atSk1N/V/dps2yWCyKiIjUV199pUEDH1d0iyjVr1dX3bp20fz5X9i1/8zQoerYob3TbT/26CPq26e3XdnSJUvUp/dDatigvu5r3kwvjxihEydO2Orbt2+nqVPflyRFt4hSRHht2++S9MUX89Sta1fVr1dXre9vpTff/KeSkpLstvH44wPUvVtXbd++XQP691Ojhg00efJ7io+PV0R4bc2aNVPz5s1Txw7t1ahhAw1+8kmdOHFCxhh9+MEHatP6fjVsUF/PP/esEhMTM3jEAQAAACDjMvVYUK9evVS+fHm99dZbiouL08cff6yiRYvq7bffti0zcOBAzZo1Sz169NCwYcO0fv16vfXWW9qxY4cWL16cZtuvvvqqqlSpog8//FCvvfaaypcvr7CwMFv9mTNn1L59e/Xu3VuPPPKIihUrptTUVHXq1Elr1qzRE088oWrVqmnr1q165513tHv3bn311Vd2/Zo9e7b69u2rJk2aaOXKlerYsWNmDgOQ7SIi62jJkiXaunWr6tevL0navHmTwsMjFB4RoQvnz2vv3r2qXLny/9dtVvny5RUcHKwv589XWFiYolq0kIe7h1avXq03//lPpaYa9e59I2jStm1bjRz5qv7880/VrFnTtt1jx47pjz/+0Asvvmgr++ijD/X+lClq06atunbrpoSzCZo3b64G9O+neV/M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z0gyXbp0cbqMp6enkWSKFCliLly4YFe3detWY7FYjCSza9cul/oOZIU7nQNbtmwxpUqVMmXLljVff/21SUhIMEePHjXvvvuu8fX1NSEhIWbnzp1260yaNMlIMgEBAWbGjBnmzJkz5sCBA+bZZ581koyHh4eRZNatW5fduwfc1o4dO0zBggWNJPPuu++me71jx47ZJnv+9ddf77i89YUBFSpUcHiDHJCbEhISzE8//WTuv/9+I8m88cYbt10+PRPapiUlJcU0b97cSDJvvvlmhtYlcyUfsab7Xbx40Wm99fniAgUK5FifgKx2c1qrswltrWWrV69Osw0vLy9VrlxZb7zxht566y2tX79eo0ePzvrOArng2rVr6tmzp37//Xc1a9ZMn3/+udPlrOdSz549bZmNVjVr1lT9+vUlST/99FP2dhhw0bVr19SjRw8dO3ZMixYtUqdOnRQcHKySJUvqueee0xtvvKGzZ886jPPPPPOMnnnmGV24cEEDBgxQoUKFVL58eU2ePFlvvPGGChYsKEm2/wK5IT4+Xu3atVNCQoJefPFFPffcc+let3jx4ra/i5YvX37bZf/5z39q6tSpKlasmFasWOF0ThcgtwQHB6t58+a2xz5HjRql3377LVu25e7urpdfflnSjcyZjCC4ko+UKVNGknT06FGn9dbysmXL5lifgKxm/f76+fnZnpm/Wbly5SRJJ0+eTFd71lRZ3giB/CA1NVUxMTH673//q4iICH377bdpvgnIei5Zz5lbZfRcAnLLunXrtGfPHpUvX97ppP49e/aU5BgotFgseu+997Rp0yaNHTtWgwYN0quvvqqNGzdq+PDhOnv2rHx9fVWhQoUc2Q/gVmfPnlWbNm106NAh9e/fX+PHj89wG9Y5I44fP57mMtOmTdPIkSMVFBSk5cuXq2LFipnuM5CdPD099dBDD8kYk61vwE3PeeMME9rmI9bXpsXFxTmtt5bXrl07x/oEZDXra8YvXbqkK1euOMwjcfbsWUlK98RtISEhcnNz06lTp7K2o0AueOaZZzR37lxVrlxZK1asuO1ktJGRkdq8ebNt7pVbZfRcAnKL9eZRUFCQ03preVrf9YiICEVERNiV/fTTT7p+/bqaNm0qDw/+XEbOu3Dhgtq3b6/t27erW7du+uijjzL1Bjfr9/7WDEWrefPmaciQIfLz89PSpUsd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IJ2MMbet37t3r6Qbjw3d+rpY63whGzZs0MiRI5WYmKiHH37YLgBzO40aNdKjjz4qSfrzzz9t5V5eXpKklJQUh3VKlSqlqlWravv27dq9e3e6tpOTWrRoIXd3d61YscL2CE96DBw4UB4eHvroo4/SfCRIku6//35JsnscK6Oy4xh6eno6/bxyUnR0tNzd3bVkyRJdu3bN5fZq1KghDw8Ph/mGrJo3by5Jmj9/vkPd2bNn9d133zld73bf75vt2LFD0o1MHAAAcgPBFQAA0mnUqFF66aWXnGZCxMfH68knn5QkderUyXZReDPrfCHvvPOOJOcBgcOHD2vmzJm6ePGiXfnly5cVGxsr6X8ZGZJUokQJSUrzonbUqFFKTU1V9+7dtXnzZof6M2fOOEy0m1NKlCihxx57TJcvX1ZMTIzOnDljV3/y5EmtX7/eYb3ixYurU6dO2rRpk1avXq3atWurQYMGDss1aNBA0dHROnnypJ544gklJyfb1R88eFBbt269Yz+z+hiWKFFCf/31V67OD1KyZEkNGDBABw8eVJ8+ffTXX385LLN3714tXLgwXe35+/srMjJSx48fV3x8vEN9//795e3trTlz5tgmGpaka9eu6YUXXnD4bKzu9P222rBhgyQpKioqXf0FACCr8VgQAADpdOHCBU2aNEnjx49X5cqVVb16dfn4+Ojo0aNav369rl27pooVK+rdd991ur51vpAjR444vNnG6uzZs+rfv7+GDBmievXqqVSpUkpOTtbatWt16tQp1atXT926dbMt/+CDD+q1117T8OHD9f3339syZt5++20VKlRIffv21bZt2/Tmm2+qbt26ioiIUFhYmIwx2rdvn/744w8FBAQ4DfTkhEmTJmnXrl2KjY1V2bJldd999ykwMFCHDh1SXFycnnrqKTVs2NBhvcGDB2vRokWS7N+2dKvPPvtMrVq10ty5c7VixQo1a9ZM3t7e2rdvnzZv3qwJEyaoVq1at+1jVh/DTp06afLkyapTp46aNGkiHx8fValSRS+99FK61s8qkyZN0sGDB7Vw4UItX75cERERKlOmjJKTk7V9+3bt3btXnTt3Vvfu3dPVXseOHfXbb79p1apVevjhh+3qypcvrwkTJmjo0KFq27at7rvvPoWGhmrdunVKSEjQww8/rDlz5ji02alTJ61evVqtWrVSdHS0/P39VbhwYf3rX/+yLXPhwgX9/vvvqlq1qu3NSAAA5LjcfRM0AAB5x6lTp8xnn31mHnnkEVOrVi1TqFAh4+HhYUJCQkzTpk3NuHHjzIULF27bxiOPPGIkmQ8++MBpfVJSkpkwYYLp0KGDKVeunPHx8TGFChUy9erVM++8845JTk52WGfOnDmmTp06xtfX10gyksyBAwfsllm9erXp2bOnKVGihPH09DSFChUytWvXNkOHDjWrV6+2WzYmJsZIMrGxsQ7bio2NNZJMTEyM0/6XLVvWZPTPiytXrphJkyaZBg0amICAAOPr62vCwsJM//79zcaNG52uc+nSJePp6Wl8fX1NQkLCbdtPSkoyr732mqldu7bx9fU1AQEBpmrVqmbo0KFmz5496d63jBzD27lw4YIZOnSoKV26tPHw8DCSTFRUlK0+KirK6WcoyZQtW9Zpm5n9zFJSUsysWbNMy5YtTUhIiPH09DQlSpQwjRs3Nv/4xz/Mrl270r1fhw8fNu7u7qZDhw5pLrN48WLTsGFD4+vrawoWLGg6d+5sduzYYcaMGWMkmU8++cRu+WvXrpmRI0easLAw4+np6fQYfPrpp0aSmTBhQrr7CgBAVrMYc4cHyAEAQJa4ePGiSpYsqZSUFB07dizN19bizubOnau+ffsqJiYmX000m9d17dpVS5Ys0ZEjRxQaGpoj22zbtq3WrFmjw4cPq1ChQjmyTQAAbsWcKwAA5JApU6bo3LlziomJIbDigmvXruntt9+WJA0ZMiSXe4Obvf7660pNTdX48eNzZHtxcXH67rvvNGzYMAIrAIBcReYKAADZ6MyZM3r55Zf1119/admyZfLz89OOHTtsr8JF+n3zzTf66quvtGHDBm3btk1dunTR4sWLc7tbuMWAAQP0xRdf6MCBA7ZXNGeXLl266Ndff9W+ffsUEBCQrdsCAOB2CK4AAJCNDh48qPLly8vLy0u1atXS+PHj1aJFi9zuVp40duxY/eMf/1DBggXVvn17TZ48WSEhIbndLQAAAIIrAAAAAAAArmDOFQAAAAAAABcQXAEAAAAAAHABwRUAAAAAAAAXEFwBAAAAAABwAcEVAAAAAAAAFxBcAQAAAAAAcAHBFQAAAAAAABcQXAEAAAAAAHABwRUAAAAAAAAX/B+Eh6kexTVN6AAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "with pulse.build(backend, name='example') as program:\n", - " with pulse.align_sequential():\n", - " gaussian_pulse = library.gaussian(100, 0.5, 20)\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(0))\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(1))\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(1))\n", - "\n", - "program.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Phase and frequency offsets\n", - "\n", - "We can use the builder to help us temporarily offset the frequency or phase of our pulses on a channel." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:52.752118Z", - "iopub.status.busy": "2023-08-25T18:25:52.750937Z", - "iopub.status.idle": "2023-08-25T18:25:53.042345Z", - "shell.execute_reply": "2023-08-25T18:25:53.041586Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABF8AAADeCAYAAADrea8KAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABpmklEQVR4nO3dd1QU198G8GfpvQoCgqiogBUQCzbAihq7YiwJaNRgNInRnyUmRqNJjEZiTGKPNXaNvUVN0Ngb1liwgb2hdKTtff/w3YnrLrDALs3ncw4n8ZaZu7Oze2e+e+demRBCgIiIiIiIiIiIdEKvpBtARERERERERFSeMfhCRERERERERKRDDL4QEREREREREekQgy9ERERERERERDrE4AsRERERERERkQ4x+EJEREREREREpEMMvhARERERERER6RCDL0REREREREREOsTgCxERERERERGRDjH4UkAymQwymQw2NjZISEhQW+b777+HTCbD5MmTi7VtpdXz588xefJk+Pv7w9bWFqampqhatSrCwsJw7NixPOueOXMG7dq1g42NjXTsY2NjAQCpqan45JNP4ObmBgMDAx7zMu7AgQOQyWQIDw8v0f2//mdqagonJycEBATg008/xfHjx0ukbdoWHh4OmUyGAwcOlHRTNPb48WMsXrwY3bt3h6urK4yMjGBjY4PAwEAsX74cQohc66anp+Orr75CzZo1YWJiAhcXFwwaNAj379/Pc5/Lli1Do0aNYGFhATs7O3Ts2BFHjx4tVPtzcnIwa9Ys1K1bF6ampnBwcEBoaCiuXLlSqO3lZvLkyZDJZFi2bJlWt0tERERERcPgSyElJibixx9/LOlmlHp//fUXqlevjq+//hqxsbFo0aIFunbtCisrK6xYsQJNmzbFyJEjIZfLVeomJyejS5cu2L9/P/z8/PD+++8jLCwMFhYWAIDPP/8cv/zyC0xMTBAaGoqwsDD4+Pjo/DWVdJCAdKtixYoICwtDWFgYevbsCT8/P9y5cwc///wzAgIC0L59ezx69Kikm5mnKlWqQCaTlXQztGr06NEYPHgwduzYATc3N/To0QN169bF4cOHER4ejtDQUOTk5KjUe/nyJVq1aoWpU6ciJSUFXbt2hZubG5YuXQpfX1/cunVL7f5GjhyJgQMH4tKlS2jTpg0aNWqEffv2oWXLltiyZUuB2i6X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dWusWbMGf/75J5o3bw5jY2PcvHkT586dQ2RkJOrWrZtnG7V9DLt06YJffvkFfn5+aNq0KUxMTODp6YkxY8ZoVF9bZs+ejdjYWPzxxx/Ys2cPfHx8ULlyZaSmpuLy5cu4ceMGunbtip49e2q0vU6dOuHUqVM4cOAA+vfvr5RXtWpVREZGYsSIEWjfvj1atmwJJycnHD9+HC9evED//v2xatUqlW126dIFBw8eROvWrREcHAxzc3NUqFAB33//vVQmJSUFp0+fhpeXl7SyExERUalTsitdExERlR1Pnz4Vv//+uxgwYICoW7eusLe3FwYGBsLOzk40a9ZMzJgxQ6SkpOS5jQEDBggAYsGCBWrzk5KSRGRkpOjYsaOoUqWKMDExEfb29sLf31/MmjVLpKamqtRZtWqV8PPzE6ampgKAACBu376tVObgwYOid+/ewsXFRRgaGgp7e3tRr149MWLECHHw4EGlsmFhYQKAiIqKUtlXVFSUACDCwsLUtt/d3V0U9PIiIyNDzJ49WzRq1EhYWFgIU1NT4eHhIQYOHCjOnDmjtk56erowNDQUpqam4sWLF3luPykpSUyZMkXUq1dPmJqaCgsLC+Hl5SVGjBghrl+/rvFrK8gxzEtKSooYMWKEcHNzEwYGBgKACAwMlPIDAwPVvocAhLu7u9ptFvY9y87OFsuXLxetWrUSdnZ2wtDQULi4uIiAgADx9ddfi2vXrmn8uu7cuSP09fVFx44dcy2zefNm0bhxY2FqaipsbW1F165dxZUrV8SkSZMEALF06VKl8llZWeLLL78UHh4ewtDQUO0xWLFihQAgIiMjNW4rERFRcZMJkc8D7ERERKQVaWlpqFSpErKzs/HgwYNcl+Wl/K1Zswb9+vVDWFhYuZoIt6zr3r07duzYgbt378LJyalY9tm+fXscPnwYd+7cgb29fbHsk4iIqKA45wsREVExmTNnDhISEhAWFsbASxFkZWVh+vTpAIDhw4eXcGvodVOnToVcLsfMmTOLZX/R0dHYu3cvRo8ezcALERGVahz5QkREpEPx8fEYN24cHj9+jF27dsHMzAxXrlyRlvolzW3btg1btmzByZMn8e+//6Jbt27YvHlzSTeL3jBo0CCsW7cOt2/flpag1pVu3brh2LFjuHnzJiwsLHS6LyIioqJg8IWIiEiHYmNjUbVqVRgZGaFu3bqYOXMmgoKCSrpZZdLkyZPx9ddfw9bWFh06dMAvv/wCOzu7km4WERERUb4YfCEiIiIiIiIi0iHO+UJEREREREREpEMMvhARERERERER6RCDL0REREREREREOsTgCxERERERERGRDjH4QkRERERERESkQwy+EBERERERERHpEIMvREREREREREQ6xOALEREREREREZEOMfhCRERERERERKRD/wc3+L80agOX3QAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "with pulse.build(backend, name='Offset example') as program:\n", - " with pulse.phase_offset(3.14, pulse.drive_channel(0)):\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(0))\n", - " with pulse.frequency_offset(10e6, pulse.drive_channel(0)):\n", - " pulse.play(gaussian_pulse, pulse.drive_channel(0))\n", - "\n", - "program.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We encourage you to visit the [Pulse API reference](https://qiskit.org/documentation/apidoc/pulse.html) to learn more." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:53.048078Z", - "iopub.status.busy": "2023-08-25T18:25:53.046626Z", - "iopub.status.idle": "2023-08-25T18:25:53.164179Z", - "shell.execute_reply": "2023-08-25T18:25:53.163471Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:53 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "vscode": { - "interpreter": { - "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" - } - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "a792e5d2a7454e788af2c1d5c0f14412": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_fad7c88f98264f2c9096ece533a5f0d9", - "placeholder": "​", - "style": "IPY_MODEL_ce7e1c543dbd4aeab11ba34450bf7bdb", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "ce7e1c543dbd4aeab11ba34450bf7bdb": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "fad7c88f98264f2c9096ece533a5f0d9": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/circuits_advanced/07_pulse_scheduler.ipynb b/docs/tutorials/circuits_advanced/07_pulse_scheduler.ipynb deleted file mode 100644 index a3c8e57b4656..000000000000 --- a/docs/tutorials/circuits_advanced/07_pulse_scheduler.ipynb +++ /dev/null @@ -1,460 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Using the Scheduler\n", - "\n", - "The scheduler will translate a `QuantumCircuit` into a Pulse `Schedule`, using gate and measurement _calibrations_: an optimized pulse-level description of an operation on particular qubits.\n", - "\n", - "Backends that are OpenPulse enabled will typically have calibrations defined for measurements and for each of its basis gates. Calibrations can also be defined or updated by the user.\n", - "\n", - "## Basic usage\n", - "\n", - "To start, build a quantum circuit as you would normally. For our example below, we create a simple Bell state." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:54.959167Z", - "iopub.status.busy": "2023-08-25T18:25:54.956853Z", - "iopub.status.idle": "2023-08-25T18:25:56.085455Z", - "shell.execute_reply": "2023-08-25T18:25:56.084693Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXEAAADuCAYAAADPwDeGAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAdm0lEQVR4nO3de3SU5b328W/OJxKSAJpAAgEBBQIBAmwTqRoKchaworZU0IqKxQqtJSjuqnRZEKGu97VuKgjVasuhKrUWEKhFhVJAAgRBzmhsEjJsA4EwCRGSyf5jmpSQCWQmM5m5J9dnLVYyz+F+fgPDNfdzP6eA6urqakRExEiB3i5ARERcpxAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMFuztAqS+6mq4WOXtKpwTGgQBAd6uwn9UV1dTXl7u7TKcEhkZSYA+BM1OIe6DLlbB7NXersI5C+6FMH2a3Ka8vJxWrVp5uwynWK1WoqKivF1Gi6PhFBERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXaSHatGlD586d6dKlCwkJCU6vP23aNJKTkz1QmTSF7nYh4qeSkpKYPHkyN998M+np6bRv377O/LNnz7Jnzx527drFypUr2bdvX4NtPf3008ybN48TJ06QlZVFfn6+p8uXRlJPXMTPDB48mDVr1pCXl8evfvUrxo4dWy/AAWJjYxkyZAizZ88mNzeXbdu2ce+999ZbribAAW644QZGjhzp8fcgjef3IV5cXEx2djZdu3YlPDyc5ORkZsyYQVlZGQ899BABAQG8+uqr3i5TPMhmgwMFsGI7vP4JvLkVNu6Hcxe8XZl7tWrVit/+9rds3bqVCRMmEBQUVDuvpKSEv//976xYsYI//vGPrFu3joKCgjrrZ2ZmsmrVKjZt2kTHjh2BugEOkJ2dzdKlS5vnDUmj+PVwSm5uLiNHjsRisRAVFUXPnj05efIkr7zyCidOnODMmTMA9O3b17uFekjBwU94b14Wg7+/kPTRP3e4zP//YQApfUcz7udrm7m65rHzBGz4HEquuDV37r/sQZ7WEe4eCFFh3qnPXfr378+aNWvo1KlT7bTCwkKWLl3KihUrOH78uMP1EhISGDduHD/+8Y/p06cPAMOGDePAgQOsXbuW73//+7XLZmdns3DhQs++EXGa3/bEi4uLGTt2LBaLhSeffJKioiL27NmDxWJhwYIFrFu3jl27dhEQEFD74RX/smE/rNxRP8Br2Kph79fw/zZCqcG98szMTD7++OPaALdarUyfPp2UlBR++ctfNhjgABaLhSVLlpCWlsaoUaNqx7qjo6MV4Ibw2xB/4oknKCgo4PHHH2fRokVER0fXzsvOziYtLY3KykpSUlKIiYnxYqXiCXvy7D3wxvjmPCz71B7qpunduzfr16+v/Qz/85//pHfv3ixevJjKykqn2vrwww9JTU1l7969dab/4Q9/UID7ML8M8UOHDrF69Wratm3L/PnzHS6Tnp4OQFpaWp3pX331FXfeeSfR0dHExcUxefJkTp8+7fGaxX2qq+FvB5xb51+n4ajFM/V4SmhoKCtWrKB169YAbNy4kaFDh5KXl+dym9OnT6dfv351pt155506tdCH+WWIr1y5EpvNxqRJkxp8xFVERARQN8TPnz9PVlYWBQUFrFy5kqVLl7J161bGjBmDzWZrlto9ofJiORfOFzv844++/AaKzjm/3j+Our8WT3r22WdJTU0FYO/evdx1111cuOD6uNCVBzF3794NQExMDMuWLWtaseIxfnlgc/PmzQBkZWU1uEzNkfnLQ3zp0qUUFhayZcuW2qPzSUlJZGZm8sEHHzB+/HjPFe1BO957jh3vPeftMprN4ZOurXfopL0Xb8Kzfrt27crs2bMBuHjxIpMnT27Sg5UdnYWyZMkSDhw4QHJyMnfccQcTJ07knXfeaXLt4l5+GeJff/01QJ0j9ZerrKxk27ZtQN0QX7t2LYMHD64NcICMjAy6dOnCX//6V5dDfMCAAVgsjd9XDwqJYMILx1zaliOpWY/Q7b8mOpz35xeHuWUb3bt1o+qSbxwd7DvuBbpmPuD0elU26NT5BmyV37q/KCdda8/vscceIzjY/t93/vz5HDjg5PjRZRwFeM0Y+LRp01i3bh0AP/nJT64a4t26dSMw0C937ptFQkICOTk5Tq/nlyFeVlYG0OCu5erVqykuLiY6OprOnTvXTj948CATJ9YPu169enHw4EGX67FYLBQWFjZ6+eCwSJe35UhsQjc6pg51a5tXOll0kspvXe8JulPKmf91aT1bVSX5X3/p5mrcLyIiggcffBCwf8ZfeeUVl9u6WoADrF+/ngMHDpCamsp3vvMdevfuzf79+x22VVRU5HId4jq/DPGEhARKSkrYs2cPGRkZdeYVFRUxa9YsAPr06UPAZfvOJSUlxMbG1msvPj6eI0eONKkeZwSFRLi8LW9pn9jeZ3ril86ecGm9koJ9dOjQwc3VuMZmszUYiiNGjCAuLg6AVatW1V7v4KxrBXiNxYsXs3jxYgB+8IMf8PTTTztsLzExUT3xJnDlfjbgpyE+dOhQDh06xIIFCxg2bBjdu3cHYNeuXdx///0UF9sP6DXXRT7O7iJ9WwmzV3uoGA85euwYYT7yaaqywdz3nT/3+4n70nl7TsG1F2wGZWVlDR6UHzBgQO3vf/nLX1xqv7EBXrONmhC/fNtXOnbsGFFRUS7VI67zy6/N7Oxs2rRpQ35+Pr169aJ3795069aNQYMG0aVLF4YMGQLUP70wLi6Os2fP1mvvzJkzxMfHN0fp4gZBgTC4u3PrxERAX8eHUHxOzemx4HwHAZwLcICTJ0/W7hX079/f6e2JZ/lliCclJbF161ZGjx5NeHg4eXl5xMfHs2TJEtatW8fRo/Zzya4M8R49ejgc+z548CA9evRoltrFPb7bE1KTGrdsWDBMvQ1Cgq69rC+46aabADh9+rRTx1rA+QCvkZubC9iHFq+//nqntime5SM7wO7Xo0cP1q6tfz8Qq9VKXl4egYGBtefY1hgzZgxz5syhoKCApCR7AuzcuZMTJ07oijXDBAXCA4PhvRzYcRwauhizTSt48DuQZNCOlsViITAw0KkzngBmzZrlUoAD5OfnU1hYyIULF2rPihHfEFBdXW3gxcau27lzJzfffDM33ngjhw8frjOvtLSU3r1707ZtW+bOnUtFRQXZ2dm0a9eO7du3N9tBGxPHxBfci8+MiV/ptBW2H4fP8+GbUnugBwfCg7dCj0TwxWNxVxsTd9Xw4cN5//33CQ8P98i9UKxWq8bEvcAHP76eVXN61JVDKWC/Mm3z5s0kJiZy3333MXXqVDIzM1m7dq2OuhusTSsY0xfmjLWPfYP9roW9OvhmgHvKxo0bGT9+PE8++aT2LP2Ij/adPOdqIQ72m947GoYR8QcbN25k48aN3i5D3KgF9UPsrhXiIiImaXE98Zr7qoiI+IMW1xMXEfEnCnEREYMpxEVEDKYQFxExmEJcRMRgCnEREYMpxEVEDKYQFxExmEJcRMRgCnEREYMpxEVEDNbi7p1igtAg+/25TRJqyFNxTBEZGYnVanVbewuXrKK0rJyYqEhmPXpfvdfuEBkZ6ZZ2xDkKcR8UEOC7D1iQ5hEQEODWByyEhoUTeqmK0LBwoqKi6r0Wc2k4RUTEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYApxERGDKcRFRAymEBcRMZhCXETEYHp+jIj4pOrqasrLy71dRqNFRkYSEBDQ7NtViIuITyovL6dVq1beLqPRrFarVx51p+EUERGDKcRFRAymEBcRMZhCXETEYApxaTFs1VBdbf+95qeI6XR2ivitU+fg83woOAP5Z+BM2X/mlVbAK5sgKR5uuA56dYDgIO/VKuIqhbj4FVs17M+HfxyFY6euvuyX39j/bDkC0eGQ0RVu6Q6tI5qnVhF3UIiL3zhthVU7rh3ejpyvgE0H7IE+IR0GdQEvXLch4jSFuPiFz76Ed3fBxcqmtVNxCVbugH3/gh9mQmSYe+oT8RQd2BTjfXwIVmxveoBf7uBJePUjsFa4r00RT1CIi9H+cRT+ssczbZ88C69ttvfORUJCQkhISPB2GfVoOEWMVXAG1uR4eBsl8Ofd8P2bPbsd8YyIiAgGDBhAeno66enpJCYmEhoaysWLFzl58iS7d+9m9+7d5OTkUFHR8G5XSEgI77zzDqmpqWRlZZGfn9+M7+LqFOJipMoq+xCKzcnzvX82AmIioPQCvLyhcevsPAFpydCzg/N1ind0796dxx57jAceeIDY2NgGl7v//vsBOHPmDL/73e947bXXOHHiRJ1lagJ83LhxAKxbt46+fftis9k8Vr8zWsRwSnFxMdnZ2XTt2pXw8HCSk5OZMWMGZWVlPPTQQwQEBPDqq696u0xxwqeH7cMdzoqJgNhI+09n/Okz+xeH+LbY2FjeeOMNjhw5wsyZM68a4JeLj4/n5z//OcePH+f1118nJiYGqB/g5eXlzJw502cCHFpATzw3N5eRI0disViIioqiZ8+enDx5kldeeYUTJ05w5swZAPr27evdQqXRqmyw9WjzbvNsuf3Cof4pzbtdabwRI0awbNkyOnT4zy7ThQsXeOedd9iyZQu7d+/m+PHjVFRUEB4eTvfu3UlPT+e2227je9/7HuHh4QBMnTqV4cOH88gjjzBt2rQ6AT527Fg2b97slffXEL8O8eLiYsaOHYvFYuHJJ5/kueeeIzo6GoCXXnqJ2bNnExwcTEBAAH369PFytdJYBwvtodrc/nFUIe6rHn30URYvXkxgoH1w4dy5c7zwwgssX76ckpKSestbrVb27NnDnj17eP3115kxYwZTp07lmWeeITo6muTkZNavX1/7kAdfDXDw8+GUJ554goKCAh5//HEWLVpUG+AA2dnZpKWlUVlZSUpKSu3uk/i+nK+8s90vv7FfUCS+ZerUqbz22mu1Af7hhx/Sq1cvFi1a5DDAHTl9+jQLFiwgNTWVjz76CKA2wL/99lufDXDw4xA/dOgQq1evpm3btsyfP9/hMunp6QCkpaXVTqsJ/UGDBhEWFuaVxy3J1X192nvbzvfitqW+zMxMlixZUvt6wYIFjBo1isLCQpfaKyoqoqysrM604OBgSktLm1SnJ/ltiK9cuRKbzcakSZMafMRTRIT96NblIX78+HHee+89EhISGDhwYLPUKo13vsI7Qyk18s94b9tSV0REBG+88UZtD/zXv/41Tz31lMvtXXkQ89Il+wUCQUFBvPHGG4SGhja9aA/w2xCv2fXJyspqcJmCggKgbojfeuutFBUV8cEHHzB06FDPFilOs5z18vbPeXf78h9z586le/fuAGzfvp3s7GyX23J0Fsro0aPZvXs3AKmpqfziF79oetEe4LcHNr/++msAOnXq5HB+ZWUl27ZtA+qGeM23ujsNGDAAi8Xi9nZbosQeQ7nlgTcdzqs5B/xqYsL/8/P5CQ0v19B55B9v+Sdzf3hP44r1IRMenElUqxiKLEUkJSXVe+2LrnYaX+vWrZk+fToAFRUVPPjggy6f9ucowGvGwC0WCzk5OYSGhvLEE0/w4osv1htuqdGtW7cm5UdCQgI5Oc5fvea3IV7zF33hwgWH81evXk1xcTHR0dF07tzZo7VYLBaXx+ikrtB2xQ3OqzkHvDECAxu/7OW+ragw8t/SVlVV+7OwsLDea9NMmTKFyEj7P+CyZcs4cuSIS+1cLcAB9u/fz1tvvcXUqVOJiYlh0qRJLF261GFbRUVFLtXQVH4b4gkJCZSUlLBnzx4yMjLqzCsqKmLWrFkA9OnTx+MHL33xfgumah3dcFe71PH3dR0x4fYAt9nsD4Zwtq2ggKo65yGbIjAoqPZnhw4d6r32RTabrcFgfPjhh2t/X7x4sUvtXyvAL29/6tSptdttKMQTExOb3BN3hd+G+NChQzl06BALFixg2LBhtWNnu3bt4v7776e42N6ja46LfFzZRRLHrBXw3+85nteYy+ifn2DvgZdWwPN/dn779989jD/9qsD5Fb1s3v/8kVJrGYkJiRQUFNR77YvKysocnpQQFxdHamoqADt37uTQoUNOt93YAAfYu3cv+/btIy0tjf79+xMVFeVwSOXYsWNERUU5XUtT+e2BzezsbNq0aUN+fj69evWid+/edOvWjUGDBtGlSxeGDBkC1B0PF9/XKty1YRB3SW7jvW2LXf/+/Wt/37Fjh9PrOxPgV24nMDDQ567u9tsQT0pKYuvWrYwePZrw8HDy8vKIj49nyZIlrFu3jqNH7ddtK8TN06mt97bdMd572xa7fv361f5ec/ZIY7kS4Fdu5/IvEV/gt8MpAD169GDt2rX1plutVvLy8ggMDKzdLRNzDOxsf/JOc7vhOoh3fMmBNKP4+P98kzpzS1hXA/zK7cTFxTlRref5dYg35IsvvqC6upru3bvXHuG+3LvvvgvAwYMH67xOSUlhwIABzVeoONSzPcRFQkkzX/QzuHvzbk8cW7p0KZs2bSIiIoLPP/+80eulpaUxfPhwwPl7oeTk5DBy5EgqKir48ssvXarbU1pkiO/fvx9oeChl4sSJDl9PmTKFN99806O1ybUFBsKtN3nuiT6OxEVCn+Tm2540LC8vj7y8PKfXy8nJYfz48axYsYKJEyc6dS+U4uJiNmxo5A3om5lC3IHqaiefNCDN7tYbYXee/ek+zeHemyHIb48gtRwbN24kJSWF8+fPe7sUt2mRH8trhbj4vqBA+IELwVp6wX7vlcacU14joyvclOjcdsR3+VOAQwvtifvqLSXFOe3j4O6BsHpn49dp7CPZaiTHwzjfOhlBpI4W2RMX/5HRFSake6btpDh4NAvCQzzTvog7tMieuPiX226CqDB45zP4ttI9baYmwaQMiPDNu4+K1FKIi18Y0Nl+HveqHXCkCTeMjAyFuwZAegroeSBiAoW4+I24KJg2BL4otD8P87ATN5VrHQGZ3ex/osM9V6OIuynExa8EBNiHQlKT4JvzsD/f/jSegjNQbIWas0cjQu1j3knx9h58j/Y6hVDMpBAXv9UuGob0rDutymYP+kANlYifUIhLi6LetvgbfaRFRAymEBcRMZhCXETEYApxERGD6cCmiPikyMhIrFarW9pauGQVpWXlxERFMuvR+xqc1hSOnk3QHBTiIuKTAgIC3Pbg4dCwcEIvVREaFl7bpqNpJtJwioiIwRTiIiIGU4iLiBhMIS4iYjCFuIiIwRTiIiIGU4iLiBhMIS4iYjCFuIiIwRTiIiIGU4iLiBhMIS4iYjCFuIiIwRTiIiIGU4iLiBhMIS4iYjCFuIiIwRTiIiIGU4iLiBhMIS4iYjCFuIiIwRTiIiIGU4j7gIULF5KRkUFcXByxsbEMHjyYDRs2eLsskatav349ffv2JSwsjJSUFF5++WVvl9SstmzZwrhx4+jUqRMBAQG88MILXqlDIe4DNm/ezI9+9CM+/vhjPvvsMzIzMxkzZgzbtm3zdmkiDuXk5DBu3DhGjhxJbm4uzz//PHPmzOG1117zdmnNxmq10rNnT1566SUSEhK8Vkew17YstT788MM6r1966SU2bNjAmjVruOWWW7xUlUjDXn75ZQYOHMj8+fMB6NGjB1988QUvvvgi06ZN83J1zWPUqFGMGjUKgNmzZ3utDoW4D7LZbJSWlhIVFeXtUsQw3168xNeFp+pNr6yqqv159KuCeq8vd33bOFpHX/2zt23bNh566KE600aMGMGiRYsoKCggKSmpKW+jSf5VeIqKi5fqTHP0fhv6O4gICyW5/XXNVG3TKcR90Lx58zh79iyPPPKIt0sRw4SEBLP1s30cyyt0OL/8QgW/+9P6Bl/HxrRi5o/uvuZ2ioqK6g0h1LwuKiryaoifOXeeVX/d7HDele/X0bQfjh9GskcrdC+NifuYxYsXM2/ePN59912v/kcQMwUGBHD3qNuJCA9zaf2Jo24nPCzUzVU1r749u9Lnpi4urds/tTupN3Z2c0WepRD3IYsWLWLWrFl88MEHDB061NvliKFaR0cxfpjzx1IGD+zNDZ3aN2rZxMRELBZLnWmnTp2qnedt4+8YTEyrSKfWiY1pxZ1DMz1UkecoxH3Es88+y9y5c1m/fr0CXJosrWdX0nrc0Ojlr28bx/BbBzZ6+VtuuYWNGzfWmbZhwwY6derkE3uQkRHh3D3q9kYvHwDcM9rMvRCFuA+YOXMmCxcu5O233+bGG2/EYrFgsVg4d+6ct0sTg427YzAxra59cDwoMJB7xmQREtz4Q2Q//elP+eyzz3jmmWc4fPgwv//97/nNb37DU0891ZSS3ap75yQy+vdq1LKDB/ahS8fG7YXUsFqt5Obmkpuby8WLF7FYLOTm5nL8+HFXynVZQHV1dXWzblHqCQgIcDh9ypQpvPnmm81bjPiVY18VsPyKA3lXGn7rQLIy+jnd9rp165gzZw6HDx8mISGBGTNm8LOf/czVUj3i4qVKfvPme3xzpuEO0fVt43h8ygSnvsQAPvnkE7KysupNv+222/jkk0+cLdVlCnHDfJVfRFJCO0JCdGKRNM4HH23jn7u/cDivU4frefQHYwkM9N+d8vyi/+W3b/8Fm4OoCwoMZPqUCbS/ro0XKnMP//2X80PnreUs/9N6Xlq6inOlVm+XI4YYcdt/0S4+tt700JBg7hmd5dcBDpCceB1DMvs7nDfsOwOMDnBQiBvl0537qKysIi4mmphrXIwhUiM0JJh7x2QRGFh32G7MdzNpExfjpaqaV1ZGP5IT29WZlpKUwK2D+nipIvdRiF+mqqqKt99+mzvuuIN27doRFhZGx44dGTFiBMuWLaPq31d4ecN5azk7cg8CMHRweoPj6CKOJCW247uZ6bWve3TtyMA+N3qxouYVFFRz8DYIgNDQECaOvt0v9kLMfwduUlpayrBhw5g8eTJ/+9vfCA0NJS0tDZvNxqZNm3j44Yc5f/681+qr6YV3bH893VI6eK0OMdftGX1JTryOqIhw7hpxa4vrCLSLj2VU1s0AjB2SQZtY/9gL0YHNf5s4cWLtVZJvvfVWnaPOp06dYvny5cyYMcOl+5n85vdrOG+94HJt1dXVnC8rB+znvwYHBbnclrRsVTYbNpvN6TMx/EV1dTUXL1USGhLsc19i0a0i+MmUu5xeTyEO7N69mwEDBhAcHMzevXtJTU11a/vz/uePlFrL3NqmiPiXmFZRzJk+yen1WubX8RXef/99AEaPHu32AAf7N6yr1AsXaRlczQmFOHDwoP2AYUZGhkfad2UXqcbav2/nHzn76dj+eh774Z0+twsoIt6lEMd+UBOgdevWHmnf1THxy3vhxSXnmL94hbtLExEf4eqYuEIciImxH6X21L1KzlsvNHlMvPxChZuqERF/ohAHevXqxZo1a9i+fbtH2ndlrEtj4SIti6tj4jo7Bdi7dy/9+/cnJCSE3Nxcevbs6e2SNBYuIo2ii32Afv36cc8993Dp0iVGjhzJp59+Wmf+qVOnmD9/PmVlzXOaoK7OFJHGUk/830pLSxk3blztLSQ7dOhA+/btKSoqorCwkOrqakpKSoiNjfV4LeqFi0hjqSf+bzExMXz00UcsX76c22+/nfLycvbt20dgYCDDhw9n+fLlREdHN0straIiCA8LVS9cRK5JPXEfVfHtRcJCQxTiInJVCnEREYNpOEVExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQMphAXETGYQlxExGAKcRERgynERUQM9n8wwEoOGPYewwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit import QuantumCircuit\n", - "\n", - "circ = QuantumCircuit(2, 2)\n", - "circ.h(0)\n", - "circ.cx(0, 1)\n", - "circ.measure([0, 1], [0, 1])\n", - "\n", - "circ.draw(\"mpl\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We'll use the mocked backend, `FakeHanoi`, to demonstrate how to use the scheduler. It contains default calibrations for measurement and for its basis gates. The Hadamard operation is not one of those basis gates, so we use the transpiler to compile our input circuit to an equivalent circuit in terms of the basis gates of the device." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.090685Z", - "iopub.status.busy": "2023-08-25T18:25:56.089142Z", - "iopub.status.idle": "2023-08-25T18:25:56.791627Z", - "shell.execute_reply": "2023-08-25T18:25:56.790710Z" - } - }, - "outputs": [], - "source": [ - "from qiskit import transpile, schedule as build_schedule\n", - "from qiskit.providers.fake_provider import FakeHanoi\n", - "\n", - "backend = FakeHanoi()\n", - "\n", - "transpiled_circ = transpile(circ, backend) # Undefined Hadamard is replaced by U1\n", - "schedule = build_schedule(transpiled_circ, backend)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's see how our schedule `schedule` built from the circuit `transpiled_circ` looks." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:56.796423Z", - "iopub.status.busy": "2023-08-25T18:25:56.795622Z", - "iopub.status.idle": "2023-08-25T18:25:57.375669Z", - "shell.execute_reply": "2023-08-25T18:25:57.374824Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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tadJRnu02FODBg0kSRcvXrQqP336tI4cOSJ3d3f17NnTpt2AAQMkST/88IP9BynJx8dHL7/8siTp/ffftzp2u30y0lq2kbqvzMyZM3Xs2DENGjRIZcqUkYODg9asWSNJOnHihGbOnKlWrVqpbNmycnFxUYUKFfTII4/o2LFjafYnSUFBQVbX9uZlZRnt8XL48GENHjxYvr6+cnFxUfny5fXII4/ov//+s6l78z15/fp1PfHEE/L19ZWrq6vq1q2rxYsXZ/BuZk9MTIzGjBmjWrVq6bnnnsuwrqura6b6LFmy5B21yen79I8//lB4eLj8/f3VqFGjO+4v1eLFi9WwYUO5u7urbNmyGj58uC5fvmxTL7P3EQAAAAoHgpds8vX11ZgxYxQZGam33norU23Wrl2rKVOm6MqVK6pfv74eeOABlStXTqtXr1abNm30yy+/pNt2woQJeu655+Tn56cuXbooJCREU6ZM0cyZM/XHH3+oXbt2unjxorp27SpfX199//33uueeexQbG2vVj9ls1uDBg/Xwww9r9+7datiwoXr06KHo6Gi99NJL6tSpk02b1P1MsrrB7LZt21S/fn198MEHSkxMVK9evdSmTRuFh4dr5syZWdpjYfbs2Ro6dKhcXFx0//33q27dupJSNuMcOHCgHn74Yf3+++9q0KCBHnjgAVWoUEHr1q3TBx98kKUxZ0bDhg3Vv39/SVKZMmUs+5kMGzZMbdu2zfHzZUbqe1m2bFmr8v3790uS6tatm+bsjMaNG0uSDhw4YOcR3hAYGCgHBwedPHlS58+fz5E+//vvPzVr1ky7du1Sp06d1LVrV8vrXbhwoV5++WVFR0erWbNm6t27t4oVK6bPP/9czZo1s3rtAQEBGjZsmKSUWUk3X9v77rvvtuPYtGmTmjZtqi+++EK+vr7q37+/fHx89Pnnn6tp06batm1bmu3CwsLUqlUrff/992rXrp3atGmjo0ePatSoUVq4cKFN/dSgMjtBX+rP3ieffCIXF5cM6zZv3lze3t767bfftHXrVqtjv//+u37++WdVq1ZN7dq1s5RXrVpV/v7++u+///TFF19YtTly5IiWL1+u4sWL64EHHrCU5/R9mtpfars77U9KCdtHjRqlw4cPq3379mrfvr02bNigFi1a6Pr161Z17/Q+AgAAQAGTuyud8h9JhqOjo2EYhnH5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sJSvWbNaycnJ6t69u1X9MU9OUnJyotZ9O9/mHD9/v1TRURHq99A4FfN00cGD/yosLEwODg46cfy4Nm3aqDoNWmroY8+r070P6uihPZo2YYDOBR236sfFxVXtOvfRhrXLZBhGll+rq6urevXqrc+Xf56t9rgzt7vX6jZso1r1WljK9+z4RWZzsho07Zjlc3GtAQAAABR0WQpeZs6cKZPJpBMnTmj48OHy9vaWl5eXRowYYROAJCUladasWfL395erq6sqV66sqVOnKj4+PsNzdOzYUcOGDZMkNWvWTCaTScOHD7ccq1u3rv7++2+1b99eRYoU0dSpUyVJ8fHxmjFjhgICAuTq6qqKFStq8uTJNueLj4/XhAkTVLp0aXl6eqp37946f/68TCaTZs6cmZW3464qUbKknhk/Xrt379b336+1lL/22qtycnLSc5OnWNX/fu1adejQUa6urlbl/lX9dE/3h7X/7606dmSvpfxc0DHt3L5BbTs/IN8KVeXp4azt27bLyclJnTp11rDhI7Thp5/15qtT1KnbAD0wcIymv/65zMnJ+uGbhTbjbdn2PgVfvajDB3baHMuMbvfeq0sXL2r3rl3Zao/su9299s6bM63q/73jV9Wq10LOzi7ZOh/XGgAAAEBB5pSdRoGBgapSpYpmz56tvXv3auHChfLx8dEbb7xhqfPoo49q2bJlGjBggCZNmqSdO3dq9uzZOnLkiFavXp1u39OmTVONGjU0f/58vfzyy6pSpYr8/f0tx0NCQtS9e3cNGjRIQ4YMUZkyZWQ2m9W7d29t375do0ePVq1atfTvv//q3Xff1bFjx7RmzRqrcS1fvlwPP/ywWrdurd9++009e/bMzttw1/Xr118/fP+D3p0zR+3bd9COv/7SH3/8oSlTnleZMmUs9S6cP69jx45pyNChVu1fffUVrVp5Y/nPknn/szlHkxZdJEmeRZy1adNGJSUlqUXLFmrYsKFN3bLl/FS+UoAunD9lc6xKQB0V9fTS37s2q06DlmmePy3r1m9Q+fLlVbt2bXl5eWnzls1q3qJFhm2Q8257rx07L0m6HnxZly+cVttOD1i1X/P1PO3cts6q7IVbzsG1BgAAAFAYZCt4adSokRYtWmT5PiQkRIsWLbIEL/v379eyZcv06KOPasGCBZKksWPHysfHR2+//bY2b96sTp06pdl3165d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alnKr1y5orCwMPn5+eXo+fz9/bV//37dc889GYY9qeM6efKk1SyX//77L0fHAwAAbq/zPffolVdm6cCBA3rjzbdybRw1atTQnt27FRUVZbXB7r///ptyvGbN3BoagELkxx9TnmbUulVrVShfQZLk5OSkli1bZqmf48ePKygoSLNeeUW9evW2lKdu2nur+3v11meffaatW7fqj+3bVbx4cbVu3TqbrwJAWrK1x8vt9Pj/Ndpz5861Kk+dkZLTTxEKDAzUhQsXLBv53iw2Ntby5ILu3VPWfr///vtWdW4dpyTFxMTo6NGjCg4OztGxAgCAFEWKFNHUadM05okn1KFDh1wbR9cuXZWcnKxvv/3GUpaQkKC1a9eqXr16NsuVACCn7dq5Uwvmz1f58uXVo2dPlShZUk2bNtO333yT5gbf169fT7cvR8eUj3g3z+wzDENfrFiRZv3q1aurevXqWv3dd9q0aaPuu6+7nJzs8vd5oNCyy09UgwYNNGzYMM2fP19hYWHq0KGDdu3apWXLlqlv377pPtEou4YOHaqVK1dqzJgx2rx5s9q0aaPk5GQdPXpUK1eu1M8//6ymTZuqYcOGeuihh/TRRx8pPDxcrVu31qZNm3TixAmbPnft2qVOnTppxowZNpsEAwCAnNG7d5/cHoLq1a+vrt266YP339f169dVsWIl/fDD97p08aJmzpyZ28MDUMD8sX27zpw+raTkZF0PCdGuXbu0Y8df8vX11XvvvW/Z2PuFqVM1YvgwPTigv/r166/yFcrresh17T+wX1evXNHKVd+k2X/lylVUsWJFvTtnjq5evaqiHkW1ceNGRURGpFlfku6/v5fmzHlHktQjh/9IDsBOwYskLVy4UFWrVtXSpUu1evVqlS1bVi+88IJmzJiR4+dycHDQmjVr9O677+qzzz7T6tWrVaRIEVWtWlXPPPOMqlevbqm7ePFilS5dWitWrNCaNWvUuXNnrVu3jn1gAAAoxF555VXNm/eh1v34oyIiIlStWnW9//4HatKkaW4PDUAB89FH8yRJzs7O8vLyUkBANT333GT16dvXakNbf39/ffHlV/rkk4/1/fdrFRYWphIlSqhmzZoa/fjj6fbv7Oys997/QG+88boWL1okV1dXdercWYMGPaTABwek2aZHz5567725qlChguXpbwByjsm4dUfcQspkMmnGjBlp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f15XXHGFXn31VY0dO1YvvfSS/vznP7vU9fLLL2vIkCF65plnFBgYqJEjR5bnaQAAAOXQqFFjde7SRSu+/NLRtnbtWqWnp2vosGHlOmdqappu+/ut6tipk+64404FBgbpgfvv11crVuiB++9T3379dNvttysrK0v33HO3MjIy3PVwAMAtRo0aJUn64Yf8UHrv3r264YYJ2r//gCbfdJPuuvsehYaG6s477tC333xT7Lm+/u/Xys7O1p/GjdP9Dzyo3r1768MPPtAjjzzs2Ofyyy9XSEiIli//wuX45V8sV48ePdSgQQM3PkKg5irXHC/dunXT22+/7fg+MTFRb7/9tmbNmiVJ2rp1q+bPn68pU6Zo7ty5kqRbbrlF9evX1/PPP69Vq1Zp0KBBhZ778ssv17Fjx/Tmm29q+PDh6tGjh9P2kydPas6cOU5hzPvvv6+VK1dqzZo16tu3r6O9Y8eOmjZtmtatW6fevXtr69atev/993XLLbfotddekyRNnz5d48eP1y+//FKepwIAAJTD8OEj9Oors5Wdne34j/+FF16o+vXrl+t8Z86c1tPPPOPoMdOzV0+NHTNGDz74gObPX6BOnTtLklq0aKlb/jZNK1euZPJcAFVKgwaxiqhVS0ePHJEkPffsLDWMjdXC//vAMffVn//8Z02aNFEvv/yyBl96aZHnuv2OOxQSEuL4/pprrlHTps306quv6MSJE2rYsKHCw8M1cNAgffnlCt1x513y88v/m/xvO3dq//59mvj7qAMAFVeuHi/Tpk1z+r5fv35KTExUamqqJGn58uWSpLvOGyd99913S5K++MI1VS2t4OBgTZ482anto48+0gUXXKB27dopISHB8TV48GBJ0qpVq5zquu2225yOv+OOO8pdDwAAKLshQ4YoJydH3333nTIyMvT9d99p+IiyDzMqEBYWpmHDhju+b968hWrVqqUWLVo4QhdJ6tSpkyTp2NGj5S8eADwkLDRUGZmZSklJ0caNGzVkyFBlZGQoKSlJSUlJSk5OVu9evXX48KFihwGdG7pkZWYqKSlJXbp2kTFGv/32m2PbFVdcoTNnTmvTpo2OtuXLv1BISAiLnABuVK4eL82aNXP6vnbt2pLyVwWIjIzUoUOH5Ofnp1atWjntFxsbq+joaB06dKic5UqNGzd2We1gz5492rlzp+rVq1foMQWT9RbUFR8f77S9bdu25a4HAACUXUxMjC655BJ9uXy5srOzZLPZdNlll5f7fA0aNJDFYnFqi6hVS7GxsU5ttWrVkiTHH4sAoCrJzMpSTEyMDh8+LGOMXnvtX3rttX8Vum/S2bNFDgU6ceKEXn/9Na1Zvdrl9116eprj3z179lK9evW0/IvluuSSnrLb7fryyxUaOHAgS1sDblSu4MXf37/QdmOM0/fn/wfIHUJDQ13a7Ha7OnXqpBdffLHQY5o2ber2OgAAQMUMHz5CTzzxuBITE9Snb19FRkaW+1wFXeRd20v3fxYA8LZTp04qPS1NTZs1kzF2SdKNEyeqd+8+he7f9Lw/hhew2Wya9tepSk1N1eTJN6l5ixYKDQ3V6dOn9Nijj8pu/+P3n7+/v4YPH64lS5booYcf1s8//6QzZ05rxMhR7n+AQA1WruClJHFxcbLb7dqzZ48uuOACR/upU6eUnJysuLg4t14vPj5eW7du1aWXXlps2FNQ1759+5x6uezatcut9QAAgJINvvRS/eMfT+qXX37RrGef83Y5AOBVn3+ev5pR71691aRxE0lSQECAevbsWabz7NmzR4cOHdKT//iHrrhitKO9YNLe8426YrQWLFigNWvW6H9r16p27drq3bt3OR8FgMKUa46Xkoz4fYz2yy+/7NRe0CPF3asIjRs3TseOHXNM5HuurKwsx8oFw4fnj/1+5ZVXnPY5v05JyszM1G+//aaEhAS31goAAPKFhYXpoYcf1rS//U0DBgzwdjkA4DUbN2zQ3DffVOPGjTVi5EjF1KmjHj0u0seLF+vMmTMu+589e7bIc/n759/induzzxij/1u4sND927RpozZt2mjpkiX65puVGjZsuAICPPL3eaDG8sg7qkuXLpo4caLefPNNJScna8CAAdq4caPmz5+vsWPHFrmiUXndcMMNWrRokaZNm6ZVq1apT58+stls+u2337Ro0SJ99dVX6tGjh7p27arrrrtOr7/+ulJSUtS7d29988032rt3r8s5N27cqEGDBmnGjBkukwQDAAD3GD26aqws9OEHHygtLU2nz+TPC/fdmtU6/fvEldded51jbhgAqKj/rV2rgwcOyGqz6WxiojZu3Kj1639Qw4YNNXv2KwoODpYkPfjQQ5o8aaL+dM3Vuuqqq9W4SWOdTTyrrb9s1elTp7Too8WFnr958xZq2rSp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uWazWBTRvI/8g4K9XQnKILp5N0IXAAAAACgHnw9eJOn222+XJD3zzDPasGGD07aUlBTddNNNstvtuuqqqxzDhgqO8/f315w5c7R+/XpH+549e/TUU08pICDAcW64jyWsuaIaxnm7DJRSeEw9BcZ08HYZAAAAAOCTqkXwMmrUKN13331KTk5W79691bt3b40fP16jRo1SXFycVq5cqY4dO+r11193Oq5t27Z67rnnlJOTo379+mnEiBEaO3asunTposTERL344otq1aqVlx5VNeYXqND67RUUFuHtSlACi5+/ImNbygSzkg0AAAAAlIfFFCz3Uw2sWLFCc+bM0YYNG5SQkKCwsDC1a9dOV199taZPn17kxLifffaZnnvuOf3000+SpG7duum+++7TqFGjJOWPEYuKitLRY8eZ48Vd8s7Kemylzuz/zduVoBi16jdSZIt+soe39XYpAAAAAFCi1NRUNWncSCkpKVXm/r1aBS+eQvDiGf5nV+nM3q3KSa96kx9B8vMPUGzbjjL1LpX8Wc0LAAAAQNVXFYOXajHUCL7JHtxYterFersMFCE8pq78QuoSugAAAABABRC8wGtMcCOFREQqKJQb+yrH4qeIOg1kD27o7UoAAAAAwKcRvMB7AiKkgAhF1GXi1qomLDpG/kFBMkH0SAIAAACAiiB4gVeZoAYKjYyWX0Cgt0vBOSJi6kn+kZJ/qLdLAQAAAACfRvACr7IHx8ri56fwmHreLgW/CwqLUFBYuOzB9HYBAAAAgIoieIF3/d6rIrx2XUkWb1cD5U+qK+X3RgIAAAAAVAzBC7zLYpEJrKuAoCCF1KoaS33VZH7+AQqNiskfYhQQ4e1yAAAAAMDnVdngZcWKFeratatCQkJksViUnJzs7ZLgISY4v2dFWO26Xq4EoVG15efnR28XAAAAAHCTKhm8JCYmaty4cQoNDdVrr72m9957T+HhLDlcXZmAGMnip9BaUfLzD/B2OaX20Te79Jenv5Ldbir1ul/8b59ufPwL5Vptbj93WO06kiQTWMft5/ZV7777jsaOGS273e7tUkrlo0WLNGzoEOXm5nq7FAAAAACSquRd7qZNm5SWlqYnn3xSl112mbfLgaf5BcgExshiEhQaFaOMs6fddurjCela/O0u/bTrtBJTsxTg76cWDaPUr2sTDe/VUsFB/vp640G9+MGPf5TjZ1HtWsHq1qaBJo7oqLrRriv7ZGTn6aNvd2nK6M7y8yvf3DR2u9F1j32mawa11Z8ubeuy/YOvd2rB8u2Ki43UnPuHONovv7i5Fn61Q1+u268x/VuX69qFCQgKUXBYhGTxlwnyrd5HR44c0bx339X69T/ozJkzCgwMVKvWrTVkyBBdffU1CgkJ0SeffKIZjz3qOMbf318xMXXUs1dP3Xrr39WggWsvn/T0dM17913dedfd8vOr3Jzabrfr0sGDdOPEiZo8+SaX7XPnvqnX/vUvxcfH6+MlSx3to8eM0Zw5b2jxRx/p+vHjK7NkAAAAAIWoksHL6dP5N97R0dEl7puZmamwsDAPVwRPM4ExsuQmKCzafcHLxu0n9M/56xUY4KdLe8QprmGkrDa7tu9P1Nuf/aJDJ1N1+58vdOx/w/D2io0JV67Vrt8OJurrTYe0/UCC5tw3REGB/k7n/u+Gg7LZ7RrYvWm569t1+KxSM3J1cXvX1YPOJGfqPyt/U0iQv8u2oEB/XXpRnJas3qPR/VrJYnHPpMSh0TGSJBMQJVlcr1tVfffdd7rv3nsUGBioUVdcoVatWikvz6qff9qil158Ufv27dNjj81w7H/LLdPVuHFj5eTmaNsvv+jTTz/Vzz/9pMUfL1FwcLDTuZctWyqbzabhw4dX9sPSr79uU1JSkvr16++y7dSpk3r7rbcUGuoaCgYHB+uKK0brvfff03XXX++21wcAAACA8inTn3Bnzpwpi8WivXv3atKkSYqOjlZUVJQmT56szMxMp32tVquefPJJxcfHKzg4WM2bN9dDDz2knJycYq8xcOBATZw4UZJ00UUXyWKxaNKkSY5tHTt21ObNm9W/f3+FhYXpoYcekiTl5ORoxowZatWqlYKDg9W0aVPdd999LtfLycnRnXfeqXr16qlWrVoaPXq0jh49KovFopkzZ5bl6YAbmaD6kqTg8Aj5BwWXsHfJTiZm6Jn3Nqh+7TD9+/4hmnZVVw3v1VJX9G2lB268RP++f6jiYp0n8+3RLlaDe8RpWM8WuuPaHrp6YBudSMjQ+l+Pu5z/640H1bNDI5dApiw27Typ+rXDFNcwymXbW5/+onZxMWrdtHahx/bv2lSnkzK1de+Zcl//fOEFwcvvPwtfcOzoUT1w/31q2LChlixdpvvvf0BXX32Nrr32Wj0z61ktWbpM8fHxTsf06dtXI0eN0lVXXa0ZMx/XjTdO1JEjR7R69WqX83/6yScaMGCgSyBTGdZ+v1YNGzVSq1atXLa9+MKL6ty5s9p36FDosUOGDtWJ48e1aeNGT5cJAAAAoATl6vEybtw4tWjRQk8//bS2bNmit956S/Xr19esWbMc+0yZMkXz58/XNddco7vvvlsbNmzQ008/rZ07d2rp0qVFnvvhhx9W27Zt9eabb+qJJ55QixYtnG6cEhMTNXz4cF177bWaMGGCGjRoILvdrtGjR2vt2rWaOnWqLrjgAm3btk0vvfSSdu/erWXLljnV9f777+v6669X79699e2332rkyJHleRrgTv61JL8gyZ6rsKgYpZ05UaHTffTtLmXlWHXHtRcqJsq1V0CjehEaO6D4YTodW9bVR9/u0onEDKf2k4kZOnA8RVeed/y/Fm/RF//bX+w55z06XA1i8ucr2rTjhC5u39Bln237zmjt1mP6192X6Y0lPxV6ntZNa6tWWJDWbzuurq0rHpQEhYYrIDhEUn7vI18xb967yszM1IyZj6tevXou25s1a6bx4ycUe45u3bvr3Xff0dGjR5zajx09qt27d2vCDTe4pdannvqHPlq0qNh9vlj+pRo3bixJ+v7779SvXz+XfTZv/lErV36tD//zHz3zzDOFnqd9+/aKiorSqtWrdPEll1S8eAAAAADlVq7gpVu3bnr77bcd3ycmJurtt992BC9bt27V/PnzNWXKFM2dO1eSdMstt6h+/fp6/vnntWrVKg0aNKjQc19++eU6duyY3nzzTQ0fPlw9evRw2n7y5EnNmTNHf/3rXx1t77//vlauXKk1a9aob9++jvaOHTtq2rRpWrdunXr37q2tW7fq/fff1y233KLXXntNkjR9+nSNHz9ev/zyS3meCriLxZI/3CjnpMKiKx68bNh+QrF1wtW+RfnnKjmVlB+4RIQGOrXvOJgoSWrVxLk3Sp/OjbX3SJJOJ2VpyuhOjvb3VuxQUIC/rhvSzhG6nE3N1r5jybphmHOPBZvd6I0lP2vYJS3UopFrT5hztWoSrR0HEsv34M5TMMxIfoFSgO8s671mzRo1adJEXbt2Lfc5jh/P79EUed5y5j9v3SpJuuCCC8p97nNddtll2rljh06ePKk777zL0f7GG68rKChYf5n6F0fokpCQoN9++01/u2W60zlsNptmPfOMrrzy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/fZb/fLLL0WOqwbK67333tOuXbvUunVrtW7dWpL7PwMWLlwoSZowYUKR+7zxxhtat26dFixY4HgfAu70yy+/6IUXXtDkyZMd8zIWpTzvgXMnwa1du7bLMXXq1JGUH+rs3r3bMZ9GwXGFHXPucQWfFUBFvP3220pMTJSfn5/atGmjsWPHqlmzZh6/bkn3Ae7+DCB4qUYOHz4sSYX+Mj63/dChQ5VWE+AuJb2+w8PDFR0draSkJKWlpalWrVrFnm/fvn36/PPPJUmjR48udJ+EhAQ98sgjTm133XWX5s+fr5EjR5b1IQClNnv2bEnSsGHDHD1OKvoeWLduncuqF506ddLHH3/suLktwOcJKtNzzz2n7du3KyMjQzt37tT27dvVqFEjffDBB/L395fk3s+AH374Qfv27VOdOnU0fPjwQvc5duyYHnzwQQ0aNEg33HBDBR8h4Mput2vKlCmKjo7Ws88+W+L+5XkPxMTEyN/fXzabTYcOHVK7du2cjjlw4IDj34cOHXIEL/Xq1XO0FabgOD4D4A7/+Mc/nL6/55579OijjxY68b+7lHQf4InPAIYaVSMFS4+GhYUVuj08PFySlJaWVmk1Ae5S0utbKv1r3Gq1atKkScrJydGf//xnXXjhhS773HjjjVqxYoWOHTum9PR0/fTTT7rhhhuUmJioq666Sps2barAowGKtnz5cr399tsKDAzUk08+6Wgv73sgKipK9957r9avX6/ExEQlJibqm2++Uc+ePbVt2zYNGTJEKSkpTufh8wSV6auvvtL8+fO1ePFibd++XXFxcfrggw+cfje78zPgvffekyRde+21RQ7tuPXWW5Wdna033nijTI8FKK1XX31VmzZt0nPPPefoQVKc8rwHQkJCdNFFF0nKX4L6fO+8847j3+e+b/r37y9J+uCDD5Sbm+t0zI8//uhY7p3PAFRE//799d5772nfvn3KzMzUrl279NRTTykgIECPPfaY449Q7laa+wBPfAYQvACocW677TatXbtWLVu21Ouvv17oPvPnz9fQoUPVqFEjhYeHq2vXrlqwYIEeeugh5ebmuvSEAdzht99+04QJE2SM0XPPPecYf1wR3bp107PPPqtLLrlEMTExiomJ0eDBg7V27VpH1/ai3gdAZVi5cqWMMUpKStJ3332n1q1ba8CAAXrqqafcfq28vDwtWrRIkor8K+aSJUu0bNkyPfDAA2rbtq3bawAOHz6sRx55RAMGDNCkSZM8eq0HHnhAkvTCCy/o+eef18mTJ3X8+HE99dRTmjNnjgIC8gdAnDvsdPz48WrSpIkOHz6s0aNH69dff1VaWpr++9//6uqrry70GKCsnnjiCU2YMEEtW7ZUaGio2rRpo4ceekjLli2TJM2cOVNZWVluv25J9wGe+gzg3VKNFMw7kZmZWej2gvGaJQ3BAKqikl7fUule40899ZTeeOMNNWjQQF999VWZx2zed9998vf31+rVq13+CgRUxLFjxzRs2DAlJSXprrvu0u233+603V3vgQL+/v66//77JeX3OCjLtfg8gSdER0erX79+Wr58uS688EI9+uijjt6F7nr9f/nll0pMTFTr1q11ySWXuGxPTU3V3//+d7Vu3VoPPfRQRR4OUKTp06crNzdXc+bMKfUx5X0PjBkzRrNmzZIxRvfee68aNmyoxo0b65FHHtHNN9/sWJDj3PlcIiIi9Pnnn6tJkyb66quv1KlTJ0VGRmro0KEKCgrS3Xff7XIM4C5DhgxRjx49lJycrA0bNrj13CXdB3jyM4A5XqqRgkmIjh49Wuj2gva4uLhKqwlwl5Je3xkZGUpOTlbt2rWL/E/3nDlz9MgjjygqKkorVqxQq1atylxHVFSU6tevrxMnTigxMVENGzYs8zmA8509e1ZDhgzRoUOHNHnyZD3//PMu+7jjPXC+grldTpw44XKtn376ic8TeEVgYKD+/Oc/a/Pmzfrss8900UUXue31X7CaUVGT6m7ZskXHjx9X8+bNNXToUKdtJ0+elJS/6tfAgQMVGxurDz/8sMyPD/j8888VHR2tadOmObVnZ2dLyg/iCyYu//DDDxUbG1uh98B9992nK6+8UosXL9bBgwcVFRWlkSNHasCAAY45Yzp06OB0TJcuXbRr1y4tWrRIW7Zskc1mU/fu3XXttdfq6aefLvQYwF1at26tH3/80eX/JxVRmvsAT34GELxUIwVd0rds2VLo9oL2zp07V1pNgLu0bdtWwcHBOnPmjI4dO6bGjRs7bS/p9f3hhx9q+vTpCgsL0xdffKGuXbuWqw673a7U1FRJf4ylBioiPT1dw4cP144dO3TVVVdp7ty5slgsLvtV9D1QmKSkJEmur+UuXbrok08+4fMEXlO3bl1J0pkzZyS55/WfmprqWGq3uNWMJOngwYNFrjBz8uRJnTx5kuARFZKcnKw1a9YUui07O9uxrSCMqeh7oHXr1nrwwQed2g4fPqxjx46pVatWLueT8ueTmTRpkstwqIKJ2lnVDp5S1P9Pyqus9wGe+AxgqFE10qdPH0VFRWnfvn36+eefXbYvXrxYknTFFVdUcmVAxYWGhmrw4MGSpI8++shle3Gv7+XLl+vGG29UQECAli5dqj59+pS7jhUrVigjI0Px8fGKjIws93kAScrJydGYMWO0ceNGDR061GkVl/NV5D1QlI8//liS67LRBat2ffbZZ8rJyXHadurUKX3//feqXbt2hd5LQHEKbjrj4+Mluef1v3jxYmVnZ6tPnz5q2bJlofsMHDhQxphCv959911J0s033yxjTLFL/wLFKeo1VrBaUHx8vKOtefPmkjzzGfDqq69KkqZOnVrqY3755RetWbNGHTp04DMAHnHmzBl9//33klz/f1IeZbkP8OhngEG18vDDDxtJpnfv3iY9Pd3R/sILLxhJZsCAAd4rDihBcHCwKe7X0tdff20kmTp16pjdu3c72tetW2eCg4NNdHS0SUpKcjpm7dq1JjQ01AQEBJilS5eWqo4PPvjAbNy40aV99erVplGjRkaSefHFF0t1LqAoVqvVXHnllUaS6devn8nIyCjxmPK8B1566SVz+PBhpza73W7mzJljAgICjMViMT/++KPLtfr06WMkmdtvv93RlpeXZ6666iojycyYMaNMjxc419q1a82XX35pbDabU3tubq555ZVXjJ+fnwkNDXV67Zbn9X+uQYMGGUlmzpw55ar53XffNZLMzTffXK7jgZIcOHDASDLx8fGFbi/PeyA9Pd3s2LHD5Vxz5swx/v7+pm3btiY7O9tl+08//WTy8vKc2nbs2GFatWplLBaL+fbbb8vxCIF8//vf/8zSpUuN1Wp1aj9w4IDj/x+jR48u9hxDhw41ksyqVauK3Kc89wFFqehnAEONqplHHnlEK1eu1Lp169S6dWv169dPhw4d0oYNG1SvXj2nZeMAb/viiy+clsstmKy2Z8+ejrZHH33U8df3yy67TLfffrtmz56trl276vLLL1dubq6+/vprRxIdHR3tdI1Ro0YpKytLLVq00LJlyxwzpZ+rb9++mjJliuP7FStWaP78+WrTpo06dOigwMBA7d6929GT7Nprr3WZ+BQoq3/9619aunSppPxhFbfcckuh+z3//POOYRfleQ+8/PLLuueee9S9e3e1aNFC2dnZ2rZtmw4cOCA/Pz+98sorhS6l+O6776pXr16aPXu2vv32W7Vv316bNm3S/v371bt3b5cu60BZ7NmzR5MnT1bdunV14YUXqk6dOkpISNC2bdt04sQJhYSEaN68eWratKnjmPK8/gscPXpUa9asUVBQkMaNG1dJjxJwr/K8B86cOaP27durQ4cOat26tQIDA7V582bt379fzZs315dffqng4GCXa91xxx3asWOHunTponr16unIkSP64YcfZLFY9O9//1uDBg2qpEeN6mj37t2aPHmyYmNj1b17d0VHR+vQoUPavHmzsrOz1aFDB82dO9fpmBMnTujKK690fP/bb79Jkm655RZHL/SRI0fq0UcfdexTnvsAj6lQ7IMqKTMz0zz66KMmPj7eBAUFmdjYWDNp0iRz5MgRb5cGOClIjov7evfddws97sILLzRhYWEmOjraDBs2zPzvf/8r9BolnV+SmThxotMxy5cvN+PHjzft2rUz0dHRJiAgwNSvX98MHz7cfPTRRx54JlATzZgxo1SvzwMHDrgcW5b3wCuvvGJGjRplWrRoYcLDw01QUJCJi4szEyZMKLRn17kOHz5sJk2aZGJjY01QUJBp1aqVefTRR01WVpY7ngLUYPv37zcPPfSQ6dOnj2nYsKEJDAw04eHhpkOHDubvf/+72bNnT5HHluX1X2DWrFlGkrnyyivLXTM9XuBpJfV4KVCW90BKSoqZNm2aad++valVq5YJCwszHTp0MDNmzDBpaWlFXmPu3LlmwIABpl69eiYwMNA0atTIXH/99eann36qyEMEjDH5vaf+9re/me7du5t69eqZgIAAExUVZXr27GleeOEFk5mZ6XJMwfujLP+nL899QFEq+hlg+b0gAAAAAAAAuBmT6wIAAAAAAHgIwQsAAAAAAICHELwAAAAAAAB4CMELAAAAAACAhxC8AAAAAAAAeAjBCwAAAAAAgIcQvAAAAAAAAHgIwQsAAAAAAICHELwAAFBGq1at0tVXX63GjRsrKChItWvXVtu2bfWnP/1J//rXv5SSkuLtElEOq1evlsVi0aRJk7xax8CBA2WxWHTw4EGv1lFeN910k8LDw3X69OlSHzNz5kxZLBbNmzevTNcaO3asGjRooPT09DJWCQBA5SF4AQCgDJ544gkNHjxYS5YsUVRUlEaNGqUhQ4YoNDRUS5Ys0d///nft3Lmz0uqZNGmSLBaLVq9eXWnXRMVYLBY1b97c22V4xLZt2zR//nxNnz5d9evXr/D5mjdvLovFUuT2xx57TKdPn9azzz5b4WsBAOApAd4uAAAAX7F582bNnDlTgYGBWrRokcaOHeu0/eTJk3r//fcVHR3tlfpQPSxYsECZmZlq3Lixt0sps0ceeUT+/v665557KuV63bt319ChQ/XCCy/o9ttvV506dSrlugAAlAU9XgAAKKUlS5bIGKNx48a5hC6SFBsbq3vuuUft2rWr/OJQbTRr1kzt2rVTYGCgt0spkyNHjujzzz/X0KFD3dLbpbQmTJigzMxMzZ8/v9KuCQBAWRC8AABQSmfOnJEk1atXr1T75+TkqG7dugoLC1NycnKh+6xbt04Wi0UDBgxwtBljtHDhQvXt21cNGjRQSEiImjZtqssuu0yvvfaaYz+LxeK42Rw0aJAsFovj6/z5QVasWKGRI0eqXr16Cg4OVsuWLXXXXXcpMTHRpaZzhy+tXLlS/fv3V61atVS/fn395S9/ccxhc/r0af31r39V48aNFRISoosvvrhcQ57y8vI0Z84c9e3bV9HR0QoNDVWrVq00efJkbd68WZK0ePFiWSwWXX/99UWeZ+rUqbJYLHr33Xed2jMyMjRr1iz16NFDkZGRCg8PV7t27TR9+nTt3r271HWW5TkszLx58xzDZg4dOuT08xo4cKBjv6LmeCkYomS1WvXkk0+qVatWCg0N1QUXXOD0mL/99lsNGjRIkZGRql27tm688cYia7RarXrjjTfUq1cvRUZGKjQ0VF27dtXLL78sq9Va6udGkt555x3Z7XZdd911Re7z6aefqlevXgoLC1OdOnV09dVXF/ozKJhv59ChQ47HXvB1/jCtsWPHKjQ0VHPnzi1TvQAAVBaGGgEAUEpNmzaVJH388cd68MEHS/yrfnBwsCZOnKgXX3xRCxcu1PTp0132KbhZnDp1qqPtvvvu0/PPP6/g4GD1799fdevW1cmTJ/XLL79o7969jvNMnDhRa9eu1b59+zR06FDFxsY6zhEREeH49wMPPKBZs2YpKChIF110kRo2bKitW7fqpZde0qeffqr//e9/atCggUttS5cu1WuvvaZevXpp2LBhWr9+vd566y3t2bNHixcvVq9evWSz2dSvXz8dPHhQGzZs0LBhw7Rp0yZ16tSpVM9pRkaGRowYoe+++07h4eGO8OXgwYNauHChoqKidOGFF2rMmDGKjY3VkiVLlJiY6DKkJD09XR988IEiIyP15z//2dF+4sQJXX755dq+fbtq166tgQMHKjg4WPv379ecOXPUunVrtWnTpsQ6y/scnqtVq1aaOHGi5s+fr/DwcF1zzTWObWXpJTVu3DhHuBIfH681a9bopptukiTVqlVL1113nXr27KmhQ4fqhx9+0HvvvacDBw7ou+++c5ovJSsrSyNHjtSqVasUExOjnj17KiQkRBs2bNCdd96pVatWaenSpfLzK93f6T7//HNJcgqRzjVnzhz97W9/k8ViUb9+/dSwYUOtX79eF198sa644gqnfWNjYzVx4kQtXrxYGRkZmjhxomNb3bp1nfaNiIhQjx499P3332v//v1q2bJlqeoFAKDSGAAAUCr79u0zoaGhRpKpVauWmThxopk7d67ZsmWLsVqthR6za9cuY7FYTJcuXVy2paSkmLCwMFO7dm2TlZVljDEmKyvLBAcHm1q1apn9+/c77Z+Xl2e+++47p7aJEycaSWbVqlWFXn/RokVGkunYsaPZs2ePo91ut5vHHnvMSDJ//vOfCz2nn5+f+fzzzx3tqamppmPHjkaSad++vZkwYYLJzc11bH/kkUeMJHPjjTcWWkthbr75ZiPJ9O/f35w+fdpp28mTJ8369esd3z/00ENGknnppZdczjN37lwjyfztb39zar/00kuNJDNu3DiTlpbmtO3AgQNm69atju9XrVplJJmJEyc67Vee57A4kkxcXFyR2wcMGGAkmQMHDrgcV1DHuc/Vt99+aySZhg0bmjp16jj9zFJSUkyHDh2MJPPtt986ne+WW25x1J6cnOxoT01NNSNGjDCSzBtvvFGqx5SWlmb8/f1No0aNCt1+8OBBExISYgIDA82KFSsc7bm5uWb8+PGOx/buu+86HRcXF2dK89/Vu+++20gy77zzTqnqBQCgMhG8AABQBitXrjRNmzZ13CgWfEVHR5u//e1v5vjx4y7HDB482EgyGzdudGp/4403jCRz2223OdpOnTplJJmuXbuWqp6SgpcuXboYSWbbtm0u2+x2u+natavx9/c3Z86ccTnnhAkTXI6ZPXu2kWQi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", - "text/plain": [ - "
" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "schedule.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That covers the basics! We used the transpiler to rewrite the circuit in terms of the basis gates, and then used the backend's default calibrations to schedule the transpiled circuit. Next we will go through scheduling with calibrations we will build ourselves.\n", - "\n", - "## Scheduling with custom gate definitions\n", - "\n", - "If your input circuit has calibrations defined, it will use those calibrations when scheduling your circuit." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.379467Z", - "iopub.status.busy": "2023-08-25T18:25:57.378963Z", - "iopub.status.idle": "2023-08-25T18:25:57.768449Z", - "shell.execute_reply": "2023-08-25T18:25:57.767516Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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F5UoUULe3t6qXLmyBg8erK+++sqy365du246j8qoUaNkMplshg7dOMfLzJkzVaVKFUnS7t27rebJuNkcLfbUsGFDSdKFCxes2k+dOqVDhw7Jx8dHvXr1sjlu0KBBktLn1cgPpUqVsgzPe/PNN622ZfX+ZzCZTDbDNa6fW+Po0aMaMmSISpcuLTc3N61fv16SdPz4cc2cOVMtW7ZUmTJl5OnpqQoVKuj+++/X0aNHMz2fJJ05c8bqs+3QoYNlv5vN8fLXX39p2LBhKlu2rDw9PVW+fHndf//9OnLkiM2+19+T165d00MPPaSyZcvKy8tL9erV0/Lly2/ybtrf1q1bZTab1bZtW6vQTpK8vLzUu3dvpaWlacuWLZke/+677+q7777TG2+8oWLFit30WhnzuGTck9fL6X2akJCgr7/+Os/Ol+H7779X586dLd9B3bp10969e232y5hPZ/fu3ZKkKlWqWN1LAAAAKFwIXnKpbNmyGjdunGJiYvTaa69l65gNGzZo2rRpunz5sho0aKD+/furXLlyWrdunVq3bq3t27dneezkyZP1xBNPKCQkRJ07d1Z4eLimTZummTNn6vvvv1fbtm114cIFdenSRWXLltXGjRt11113KSEhweo8ZrNZw4YN09ChQ/XTTz+pUaNG6tmzp+Li4vTcc8+pY8eONsdcPylnTnz77bdq0KCB3nrrLaWkpKh3795q3bq1oqKiNHPmTJ08eTLb55o9e7ZGjBghT09P3X333apXr56k9Elj7733Xg0dOlTffPONGjZsqP79+6tChQravHmz3nrrrRzVnB2NGjXSwIEDJaXPlzFy5EjLT8bcJvkt4728cYLUAwcOSJLq1auXae+MO+64Q5L0+++/27nCfw0ePFhubm46ceKEzp07lyfnPHLkiJo1a6Z9+/apY8eO6tKli+X1Ll26VM8//7zi4uLUrFkz9enTRwEBAXr//ffVrFkzq9ceGhqqkSNHSkrvlXT9Z5vZvCg3+uqrr9S0aVN98MEHKlu2rAYOHKhSpUrp/fffV9OmTfXtt99melxkZKRatmypjRs3qm3btmrdurUOHz6sMWPGaOnSpTb7ZwSVuQn6Ll++rBkzZmjs2LF64okn9Mknnyg5OTnTfTPun4z75EY3u38uXryoJ598UnfddZeGDx9+05oiIyP1999/S5IaN25ss71ixYoq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cuSIzZwsN7NmzRqNHTtWDRo0UI0aNWQ2m/XXX39Zwpgnnngi0wma582bpz179ujTTz9VrVq11LRpU/355586ePCgqlevrjlz5tz265HShwe99tpreuyxx9S2bVt16dJFnp6e2r59uxISEvTmm28qNDTU6pibfTd17txZEydO1Pz589WoUSN16dJFycnJ2rFjhwzD0IoVKxQUFJSt2vz9/bVs2TINHDhQo0aN0qJFi1S1alUdOHBAx44d03/+8x8tWbLE5rg+ffpo1apVGjp0qLp27arAwEBJ0tKlS3P25gAAAMC1GcgRSYa7u3uW25999llDkiHJmDFjhtW21NRU44033jDq1KljeHt7G6VLlzaGDRtmnD592pgxY4YhyVixYoXVMSEhIUZWH1NWxxiGYZw6dcqQZLRv3z7TYzds2GD06tXLKFWqlFGkSBGjVKlSRpMmTYypU6cav/zyS6bnys3tcvLkSWPcuHFG5cqVDU9PT6N48eJGkyZNjOeff96Ijo6+5Wtp3769Ick4depUltdIS0szVq5cabRr184IDAw0vLy8jMqVKxuDBw82du7cabP/ihUrjHr16hmenp5G6dKljQcffNAICwszRo4caUiyOSarz+Dy5cvGiBEjjDJlyhju7u6GJGPkyJHZfm8yXtvNfq6XUV9OjsmQce/VrVvX8Pb2NkqUKGEMGjTI+PPPPzPd/7PPPjMkGQ888EC2X8+KFStsavHx8THKli1rtGvXzpg6dapx8ODBm54jMjLSGDdunFG6dGnDy8vLqFu3rrFo0SLDMNJ/90JCQjK95o2/a9eLj483nn76aaN69eqGl5eXUbFiRWPcuHE3/cxjY2ONRx55xKhYsaLh4eFh87t0s/vy4MGDxn333WeULl3aKFKkiFG2bFlj+PDhxuHDh2323blz503vm6zqy/h9ycn9tnr1amPQoEFGaGioERAQYBQpUsQoV66cMWDAAGPHjh03PTY8PNx49NFHjYoVKxqenp5GxYoVjQkTJhgRERHZvr5h3Pr70zAMY+PGjUbbtm0NPz8/w8/Pz2jbtq2xadOmTPfNznfTihUrjCZNmhhFixY1goKCjO7duxvff/99jurO8M033xgdO3Y0fH19jYCAAOOuu+4yfvjhh5veh3PnzjXq1KljeHl55fp7FAAAAK7NZBj5sJwHAJcyYcIEvfPOOzp6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MQ0bNpQk7d+/P9PtGe0NGjSwtH2xdRs9XgAAAAAAcFJxcXFO12nCZBiG4egicspkMsnd3V2p180U/+2336p///4ymUz6+eefLT1VLl26pLJlyyokJESnT5+27H/x4kVVqFBBHh4eOnv2rEqVKmXZlpSUpIoVK+ratWu6cOGCvL29FRgYqHPnLygggKX+AAAAAABwRtHR0apQvpyioqKc5vnd5Vc1ytC2bVuFhYXp6tWrVsODslK2bFndd999Sk5O1vjx461CnKlTp+rq1asaPny4VSADAAAAAACQEwViqFFuzZs3T3v27NGnn36qWrVqqWnTpvrzzz918OBBVa9eXXPmzHF0iQAAAAAAwIUVmB4vuVGyZEnt27dPjz76qJKTk7Vu3TpFRUVpwoQJ2rdvn4oXL+7oEgEAAAAAgAtzyTle8lt0dDRzvAAAAAAA4OSY4wUAAAAAAKAQIXgBAAAAAACwE4IXAAAAAAAAOyF4AQAAAAAAsBOCFwAAAAAAADsheAEAAAAAALATghcAAAAAAAA7IXgBAAAAAACwE6cNXrZu3apGjRrJ29tbJpNJkZGRji4JAAAAAJzeTz/9pEYNG+inn35ydCkAJHk4uoDMhIeHa/Dgwapbt64WLFggLy8v+fr6OrosAADgIBs2bNCMZ5+RJK1YsVKN77jDarthGOrerasuX76stm3b6a2338632pKTk7VwwQJt3vy5oqOjVb16dT38yKNq2bJlvtUAwDVd/90mSZ6engoMDFRoaHW1bdtWffv14zkIKACcMnj56aefFBMTo1mzZqlz586OLgcAADgJLy8vbflii03w8vPPP+vy5cvy9PTM95qefeb/9OWXX2rosGGqVClEGzdu0KOPPKwlS5ba1AkAmRk//mGVL19eKampCg8P088//azXXntV769+X/Pnv6kaNWo4ukQAt8EphxpduXJFkhQUFHTLfePj4+1cDQAAcBZt2rTRlzt2KDU11ar9iy+2qE6dOipRsmS+1vPHH39o69atenTCBD322BQNGjRIS5YsVdmyZTV33tx8rQWA62rdpo163X23+vXrpzFjHtSid97Ru+8uVsS1a5o4cYISExMdXSKA25Cj4GXmzJkymUw6fvy4Ro0apaCgIAUGBmr06NE2AUhqaqpmzZqlatWqycvLS5UrV9b06dOVlJR002t06NBBI0eOlCQ1a9ZMJpNJo0aNsmyrV6+efvnlF7Vr105FixbV9OnTJUlJSUmaMWOGQkND5eXlpYoVK2rq1Kk210tKStLkyZMVHBwsf39/9enTR+fOnZPJZNLMmTNz8nYAAIB81r1HD0VGRmrPnh8tbSkpKfpyxw716NEzx+cbM+YBDRzQX0ePHtWYB0arRfM71fvuXtqxY7uk9J40w4cNVfM7m6lvn97as2eP1fFffrlD7u7uGjhwkKXNy8tL/fr31+8HDujSpUu5fKUACrs7mzfXf8aO1cULF7R58+eW9lOnTunxKY+pXds2urNZUw29b4h27dp5y/Pt3/+LHn98irp366pmTZuoW9cueu21V61CnfXr16tRwwY6fOiQzfFLly7RHY0b6fLly3nzAoFCJFc9XgYPHqyYmBjNnj1bgwcP1sqVK/Xcc89Z7fPggw/q2Wef1R133KG5c+eqffv2mj17toYMGXLTcz/99NMaO3asJOn555/X+++/r//+97+W7eHh4erRo4caNWqkefPmqWPHjjKbzerTp49ef/119e7dW2+99Zb69eunuXPn6t5777Wpa968eeratatefvllFSlSRL169crN2wAAAPJZuXLl1aBhQ2394gtL23fffafY2Fh16949V+eMjo7RhEcfUb369TVp0mQVKeKpJ6dN07atW/XktKlq07atJkycqISEBD3++BTFxcVZjj18+LBCQkLk5+dndc569epJko4cPpyrmgBAku6++25J0o8/pofNx48f14gRw3Xy5CmNfuABPTblcfn4+GjypEn6+quvbnquHdt3KDExUfcMHqxpTz6lVq1a6X8ffqj/+7+nLft06dJF3t7e2rJls83xWzZvUdOmTVW6dOk8fIVA4ZCrOV4aN26sZcuWWf4dHh6uZcuW6ZVXXpEkHThwQKtWrdKDDz6oJUuWSJLGjx+vUqVK6fXXX9fOnTvVsWPHTM/dpUsXnT9/XosXL1aPHj3UtGlTq+2XLl3SO++8YxXGrF69Wl9++aV2796tNm3aWNrr1auncePG6YcfflCrVq104MABrV69WuPHj9eCBQskSQ8//LCGDRum33//PTdvBQAAyGc9evTUW2/OV2JiouUBoUmTJipVqlSuznf16hXNfvllS4+ZFi1bqF/fvnrqqSe1atV7qt+ggSSpSpWqGv/QOH355Zfq27evJCns6lWVzGR4U8mSwf+c+2quagIASSpduoz8/P117uxZSdJrr76ismXKaM0HH1rmtLr33ns1atRIzZs3T53uuivLc02cNEne3t6Wfw8aNEgVK1bSW2+9qYsXL6ps2bLy9fVVh44d9cUXWzVp8mNyc0v/O/3hQ4d08uQJjfxnJAKAnMlVj5dx48ZZ/btt27YKDw9XdHS0JGnLli2SpMcee8xqvylTpkiSNm+2TVCzy8vLS6NHj7Zq+/jjj1W7dm3VqlVLYWFhlp9OnTpJknbu3GlV14QJE6yOnzRpUq7rAQAA+atr165KSkrSN998o7i4OH37zTfq0TPnw4wyFC1aVN2797D8u3LlKvL391eVKlUsoYsk1a9fX5J0/tw5S1tSUpKKZDKhr5eXlyQpMYl5GQDcnqI+PoqLj1dUVJT27dunrl27KS4uThEREYqIiFBkZKRatWylv/8+c9NhQNeHLgnx8YqIiFDDRg1lGIYOX9c7r3fv3rp69Yp++mmfpW3Lls3y9vZm4RMgl3LV46VSpUpW/y5WrJgkKSIiQgEBATpz5ozc3NwUGhpqtV+ZMmUUFBSkM2fO5LJcqXz58jYrFhw7dkyHDh1ScHBwpsdkTNabUVe1atWsttesWTPX9QAAgPxVvHhxNW/eXF9s2aLExASlpaWpc+cuuT5f6dKlZTKZrNr8/P1VpkwZqzZ/f39JsvyhSUoPWFKSk23OmTHHnLeXt802AMiJ+IQEFS9eXH///bcMw9CCBW9rwYK3M9034tq1LIcCXbx4UQsXLtDuXbusvsckKTY2xvLfLVq0VHBwsLZs3qLmzVvIbDbriy+2qkOHDixtDeRSroIXd3f3TNsNw7D6943/IyYv+Pj42LSZzWbVr19fc+bMyfSYihUr5nkdAADAcXr06Knnn39O4eFhat2mjQICAnJ9royu9Lbtt/7fOyWDg3X1nz/wXC8sLH2IUVZ/FAKA7Lh8+ZJiY2JUsVIlGYZZknT/yJFq1ap1pvtXvOEP5BnS0tI07r9jFR0drdGjH1DlKlXk4+OjK1cu69lnnpHZ/O/3mru7u3r06KHPPvtM059+Wr/99quuXr2inr3uzvsXCBQSuQpebiUkJERms1nHjh1T7dq1Le2XL19WZGSkQkJC8vR61apV04EDB3TXXXfdNOzJqOvEiRNWvVyOHDmSp/UAAAD76nTXXXrhhVn6/fff9cqrrzmsjpo1a+rnn35SbGys1QS7f/zxR/r2WrUcVRqAAuDzz9NXM2rVspUqlK8gSfLw8FCLFi1ydJ5jx47pzJkzmvXCC+rdu4+lPWPS3hvd3buP3nvvPe3evVvff/edihUrplatWuXyVQDI1Rwvt9Lzn3HW8+bNs2rP6JGS16sIDR48WOfPn7dM5Hu9hIQEy+oDPXqkj99+8803rfa5sU5Jio+P1+HDhxUWFpantQIAgNtXtGhRTX/6aY176CG1b9/eYXV06dxFaWlp+vTTTyxtycnJ2rBhg+rXr28zXAkAsmvf3r1asnixypcvr569eql4iRJq2rSZPv3kk0wn7r527VqW53J3T3/su77HnmEY+mDNmkz3r1GjhmrUqKF1n32mr776Ut2795CHh13+Zg8UCnb57WnYsKFGjhypxYsXKzIyUu3bt9e+ffu0atUq9evXL8sVjXJrxIgRWrt2rcaNG6edO3eqdevWSktL0+HDh7V27Vpt27ZNTZs2VaNGjXTfffdp4cKFioqKUqtWrfTVV1/p+PHjNufct2+fOnbsqBkzZthMEgwAAByvT5++ji5B9Rs0UJeuXfXWm2/q2rVrqlixkjZt2qiLFy5o5syZji4PgIv4/rvvdPrUKaWmpelaeLj27dunPXt+VNmyZTV//puWCbufmj5do0eN1D2DBmrAgIEqX6G8roVf04HfD+jK5cta+/EnmZ6/cuUqqlixoubOmaMrV67Iz9dPX375paJjojPdX5Luvru35sx5Q5LUM4//cA4UNnaLLZcuXaqqVatq5cqVWrduncqUKaOnnnpKM2bMyPNrubm5af369Zo7d67ee+89rVu3TkWLFlXVqlU1ceJE1ahRw7Lv8uXLFRwcrDVr1mj9+vXq1KmTNm/ezDwwAAAgV1544UUtWPC2Nn/+uaKjo1W9eg29+eZbatKkqaNLA+AiFi5cIEkqUqSIAgMDFRpaXU88MVV9+/WzmtC2WrVq+uDD/+mddxZp48YNioyMVPHixVWrVi2N/e9/szx/kSJFNP/Nt/TKKy9r+bJl8vLyUsdOnTRkyH0afM+gTI/p2auX5s+fpwoVKlhWdQOQOybjxhlxCymTyaQZM2Zk+tep6OhoBQYG6tz5C7c1eR8AAAAAuIKIiAh16XyXxo79701DHcDZREdHq0L5coqKinKa53e7zPECAAAAAHBdGzduUFpamnrdzWpGwO1ihiQAAFDgREVFKSUlJcvtbm5uKl68eD5WBACuYd/evTp58qSWLlmijh07qXz58o4uCXB5BC8AAKDAeeyxyfrl55+z3F62XDl98cXWfKwIAFzDu4vf1YHfflOjRo017cknHV0OUCAQvPyDqW4AACg4pkx5XNHRWa/WkbFCCADA2rJlyx1dAlDgELwAAIACp06dOo4uAQAAQBKT6wIAAAAAANgNwQsAAAAAAICdELwAAAAAAADYCcELAAAAAACAnRC8AAAAAAAA2AnBCwAAAAAAgJ0QvAAAAAAAANgJwQsAAAAAAICdELwAAAAAAADYCcELAAAAAACAnRC8AAAAAAAA2AnBCwAAAAAAgJ0QvAAAAAAAANgJwQtc1p9n3B1dAgAAAAAAN0XwAgAAAAAAYCcEL3BJ9HYBAAAAALgCghe4NAIYAAAAAIAzI3gBAAAAAACwE4IXAAAAAAAAOyF4AQAAAAAAsBOCF7gc5nUBAAAAALgKghcAAAAAAAA7IXgBAAAAAACwE4IXAAAAAAAAOyF4AQAAAAAAsBOCF7g8JtsFAAAAADgrghcAAAAAAAA7IXgBAAAAAACwE4IXAAAAAAAAOyF4AQAAAAAAsBOCF7gUJtIFAAAAALgSghcAAAAAAAA7IXgBAAAAAACwE4IXAAAAAAAAOyF4QYHA3C8AAAAAAGdE8AIAAAAAAGAnBC8AAAAAAAB2QvACAAAAAABgJwQvAAAAAAAAdkLwAgAAAAAAYCcELwAAAAAAAHZC8AKXwZLRAAAAAABXQ/ACAAAAAABgJwQvAAAAAAAAdkLwAgAAAAAAYCcELwAAAAAAAHZC8AIAAAAAAGAnBC8AAAAAAAB2QvCCAoPlpgEAAAAAzobgBQAAAAAAwE4IXgAAAAAAAOyE4AUAAAAAAMBOCF4AAAAAAADshOAFAAAAAADATghe4BJYsQgAAAAA4IoIXgAAAAAAAOyE4AUAAAAAAMBOCF4AAAAAAADshOAFAAAAAADATgheUKAwCS8AAAAAwJkQvAAAAAAAANgJwQsAAAAAAICdELwAAAAAAADYCcELAAAAAACAnRC8AAAAAAAA2AnBC5weKxUBAAAAAFwVwQsAAAAAAICdeDi6AMDVpJml6HiTklIkw3B0NfnP3U0q6m3Iz9vRlQAAAACA8yN4AbLJMKSIWJMuR7rJbHZ0NQ4WJfl4GSpX3CxvT0cXAwAAAADOi6FGQDaYDel8uJsuXiN0yZCQZNLJy+6Kjjc5uhQAAAAAcFoEL8AtGP+ELlFxBAw3MszS2TA3whcAAAAAyALBC3ALlyJMiiZ0yZohnQtzU3ySowsBAAAAAOdD8IICJy+Xn46INelaDL8mt2IY0tmrbkpNc3QlAAAAAOBceKIEspCYLF2M4Fcku1LTTDoXxvsFAAAAANfjKQnIhNmQzoW7yWAi3RyJSzQpLJphWQAAAACQgeAFyMTVSJOSkgkQcuNKpJsSkx1dBQAAAAA4B4IX4AbxSVIY87rkWsYqUGbD0ZUAAAAAgOPxdAmnlpcT5WaH+Z/QQIQGtyUx2aRwhhwBAAAAAMELcL2rUSYlpxAY5IWrUQw5AgAAAACCF+AfCclSWDS/EnnFMKQL19xk0HsIAAAAQCHGUyYKpJwOUWKIkX0kJJl0LYYeRAAAAAAKL4IXQFJYNKsY2cvlKDclpTi6CgAAAABwDIIXFHqJyVJYFL8K9mKYpQvhvL8AAAAACicPRxdwO0wm6x4KHh4eCgwMVNmyZdWkSRP17t1bffv2lYdH5i/zl19+0Y4dO7Rv3z7t27dP58+flyQZTEpRaJgN6Vw485DYW3ySSWHRJpUM4I0GAAAAULi4dPCSYeTIkZIks9msqKgoHT16VO+9955WrVql0NBQrVmzRnfeeafNcbNmzdKGDRvyu1w4kSuRDDHKL1ci3eTnnSZvT0dXAgAAAAD5p0AELytXrrRpO3HihKZPn661a9eqY8eO+v7779WoUSOrfVq2bKkGDRqoWbNmatasmSpXrqykpKT8KRq3lNMJcnMqNkEKZxWjfGMY0tkwN1UrY5YbbzsAAACAQqJABC+ZqVatmj766CP5+/tr2bJleuCBB7R//36rfaZNm+ag6uBoyanSuXD7BjuwlZxi0oVrbqpQ0uzoUgAAAAAgXxT4vzu/8cYb8vX11a+//qrvvvvO0eXACZjN0rkwN6WlObqSwikqzqTwaIZ3AQAAACgcCnzwEhgYqB49ekiSdu7c6eBq4GiGIZ2/5qaEJB78HelSpJti4h1dBQAAAADYX4EPXiRZ5nY5dOiQYwtBvrpxjhjDkC5GmBQdR+jicIZ0NtxdcYmOLgQAAAAA7KtQBC8lS5aUJEVERDi4EjiKYUiXIkyKiCkUt7xLMMzS31cJXwAAAAAUbAV2ct3rGYYhSTKZ6OngqlJS0mRI8iyS8wlxzeb04UX0dHE+ZrN05oq7ypcwK9DXcHQ5Vr749pyjS3BqPdpWcHQJAAAAgEsoFMFLWFiYJKl48eK3dZ4//vhDvr6+eVESsuHwBX9di05SVEyKklPSZ8L1LOKuUiW8FVzMK1tBWnxYlMLj/ZSSxgpGzuzEScnfK1GB3gm63Xz0+wMxeVMUbmrByVN5cp7WDf3z5DwAAACAJMXFxTm6BBuFInj59ddfJUl16tS5rfP06N4tL8pBXnArKnlXktw8s9jBkFKjpKSLkpFy+9fza3D75yjIYn93dAUW7kV8VLJiE3n5BDm6FNxCTPgpRVz+K30sIAAAAFBAFfjgJSoqStu2bZMkdezY8bbO9cXWbfR4cSKpqYZOXkjU1YhUq3ZfbzdVLuelIP8Cf3sjC4YhXYv3VXxKVsEcHMkkqVjROPl6Jju6FAAAABQwcXFxTtdposA/mU6ZMkVxcXFq1qyZWrZseVvnql+/vgICAvKoMuSFppKiY1N05VqCDEMqHuipEkHeji4LTsAwpMuRJoVHM6GyMzG5SZVKpsnPx9GVAAAAoCCKjo52dAk2CmzwcvLkST311FNau3atfH19tWzZMkeXBDsJ8CuiAL8iji4DTsZkksoUM2QYZl1jNSunYDIRugAAAKDwKRDBy6hRoyRJZrNZ0dHROnr0qA4fPizDMFS9enV98MEHql+/vs1xmzdv1qxZsyz/Tk5O7/beokULS9szzzyjtm3b2vcFALCbMsUMpZkNRbGqlWOZpPIlzIQuAAAAKHQKRPCyatUqSZKHh4cCAgJUrlw53X///erbt6/69Okjd/fMV7S5evWq9u7da9N+fdvVq1ftUzSAfGEySeVKmJWc6qaEJMIXRykV6HxLhgMAAAD5wWQYLCdxK9HR0QoMDNS58xeY4wVwUSmp0slL7kpNc3QlhU9AUUMVg82OLgMAAACFQHR0tCqUL6eoqCineX5n4gMAhUIRD6l8ybT0JXWQbzyLGCpXgtAFAAAAhRfBC4BCw89bKhlACJBfTCapQgmz3Pn/NAAAACjE+J/DAAqV4EBD3p6MsMwPJQPN8vFydBUAAACAYxG8AChU3ExShZJmmRhyZFc+XoaCAwi4AAAAAIIXAIWOVxEpOJAhR/ZiMknlihNuAQAAABLBC4BCqmSAIR8vemTYQ3CgWd6ejq4CAAAAcA4ELwAKJXpl2Ie3p6GSDDECAAAALDwcXUBWtm7dqieffFKHDx9WUlKSIiIiFBQU5OiyAItGDRvY/Rq/Hfjd7tcozLw901c5uhr1bwY9rE9du193zcY/7X4NRzCZpHIlCLMAAACA6zll8BIeHq7Bgwerbt26WrBggby8vOTr6+vospBPVqxYrg3r1+uzdevl5pZ/nbI+XrtWy5Yt1cZNn8vT89bjJAhFCoaSgYaiEwwlJaenBQU1FMkPJQPM8mGIEQAAAGDFKYca/fTTT4qJidGsWbM0ZswYDR8+XEWKFHF0WciF6U89pTubNdWZ06dtti1ftkyNGjbQN7t3W9piY2O1csUKjRr9QK5DF7PZrI4d2mvFiuU6ePCgZr/0kgb0768Wze9U925d9cQTj2daT5++fZWSkqJPPv44V9e1lxUrlqtf3z4ym/N3MtiP165V925dlZycnK/XzW9uJql8CbOUz700Nn26TI8/dHe+f6659eUXH2nCA3cpJSXz+8HL01DJQIYYAQAAADdyyuDlypUrkpStoUXx8fF2rga34/HHH5e3t7deeGGWVfv5c+e0ePG76ty5s9q1b29pX79+ndLS0tSjR49cX/PgwT8UERGhtm3baeWK5frqqy/VvHlzTZ02TQMHDtL+X37RkCH36vixY1bHeXl5qXfvPnp/9fsyjJw9QJ49e1aznn9evXr20J3Nmqp1q5YaOfJ+rVmzWomJiZKkDRs2qFHDBpafJnc0VpfOnfXMM/+ny5cvZ3revA6iMrNkyWI1athAAwf0t2p31iDKHnz+GXJ0o8sX/9ayBTM16T/dNGpgY425907NnDpMWze+r+Sk9M9191frNKxPXcvPiH4N9Miojnpn3nRdC8/8c42Pj9Xnny1T74Fj8rVXl5R+P4wb3kabPl2W6fb1a9/VsD51Ne2Rvlbt7e7qp9TUFH21da3NMSaTVL64WW4MMQIAAABs5Oh/8c+cOVMmk0nHjx/XqFGjFBQUpMDAQI0ePdomAElNTdWsWbNUrVo1eXl5qXLlypo+fbqSkpJueo0OHTpo5MiRkqRmzZrJZDJp1KhRlm316tXTL7/8onbt2qlo0aKaPn26JCkpKUkzZsxQaGiovLy8VLFiRU2dOtXmeklJSZo8ebKCg4Pl7++vPn366Ny5czKZTJo5c2ZO3g5kQ/ESJTRx0iT99NNP2rhxg6X9pZdelIeHh56YOs1q/40bNqh9+w7y8vLK9TW/+/Y7lS1XTqGhoRo+4n59sXWbpj35pAYMGKj/jB2r5StWKi0tTcuX2wYRXbt108ULF/TTvn3Zvt4333yjewYN1Pbt29SufXtNe/JJPTphosqWKaO5c+bo1Vdfsdp//PiH9eKLL+np//s/tWnTWls2b9aDYx7I9Hcjr4OoG12+fEnLli6Vj4+PzbbbCaJcUXCgIW/Pf1/nrz/t1pOP9tee77bpjjs76P6x03Xv/ZNUMrisPlj5ut5bMtvq+EFDH9FDk1/WA+OfVcMmbfX9rs/1wlMjlZxs+7nu3vGZ0tLS1LJdT7u/rhudOPqHYqIj1LhZe5tt4WGXtPHjJfLytr0fPD291LZTX32xYZXN/VAy0Cyf3P/KAgAAAAVaruZ4GTx4sKpUqaLZs2dr//79Wrp0qUqVKqVXXvn3AfPBBx/UqlWrNGjQIE2ZMkV79+7V7NmzdejQIa1bty7Lcz/99NOqWbOmFi9erOeff15VqlRRtWrVLNvDw8PVo0cPDRkyRMOHD1fp0qVlNpvVp08ffffddxo7dqxq166tP/74Q3PnztXRo0e1fv16q7pWr16toUOHqlWrVvr666/Vq1ev3LwNyKYBAwZq08ZNmjtnjtq1a689P/6o77//XtOmPanSpUtb9jt/7pyOHj2q4SNGWB3/4osv6OO1tn9lv97mLV+ofPnykqRvv/1Gbdu2lSQ1atTIZt+QkBBVq1ZNp06dtNlWp04dBQYGaueunbqzefNbvrbz587pyWlTVbZsWS1eslTBwcGWbUOGDNHff/+tb7/9xuqY1m3aqG7d9AlcBwwYqKCgYlqxYrl27dqlbt26We2b10HUjea8MUcNGjRQmtmsyIgIm+1du3XTypUr9NO+fdl6P1yZm0mqUNKsE5fcdfnCOb39+uMqWaqcpr+wXMWK//u5du01VJcunNFvP1t/rg2btFXV6vUkSR27DpJ/QJA2fbpM+/ftVIs23a32/eardbrjzo7y9Mz/tOLAL9+oZKlyqlDJ9n74YMXrCq3ZQGazWTHRtvdDizbd9flny/XX73tVt2ELSVJRL0PBrGIEAAAAZClXwUvjxo21bNm/3dTDw8O1bNkyS/By4MABrVq1Sg8++KCWLFkiSRo/frxKlSql119/XTt37lTHjh0zPXeXLl10/vx5LV68WD169FDTpk2ttl+6dEnvvPOO/vvf/1raVq9erS+//FK7d+9WmzZtLO316tXTuHHj9MMPP6hVq1Y6cOCAVq9erfHjx2vBggWSpIcffljDhg3T778zUaq9mEwm/d8zz2jIvYP14osv6Nf9+1Wnbl3dO2SI1X6/HTggSapdu7ZVe+fOnXXor7906dIlTZ78mKV90aKF8vT00n/G/scSuoSFhenw4cN6aPzDWdZjGIbCw8NVrZrtg6ck1apdWwd++y1br23lyhWKj4/XjJnPWYUuGSpVqqRhw4bf9ByN77hDK1Ys17lzZ63aMwuiNmzYoBnPPnPT8z33/Cz17fvvMJHrg6jr/fLLz/ryyx3630cf6eWXX870XDkNolydV5H0JaaXvb1ciQnx+s+jz1uFLhnKlAtR9z4jMjnDv2rWaaJNny7T5YvWn+uVS+f09+mj6tF35G3Xu/urdVo8//9uus/YiS+o/V3/DiP79edv1Kipbe+nQwd/1r7vt+vFeZ/ovcUvZXquKqF15ecfqF/27VTdhi3k4Z4eVrGKEQAAAJC1XE0uMG7cOKt/t23bVuHh4YqOjpYkbdmyRZL02GOPWe03ZcoUSdLmzZtzc1lJ6cMfRo8ebdX28ccfq3bt2qpVq5bCwsIsP506dZIk7dy506quCRMmWB0/adKkXNeD7AkNDdX9I0dqx/btioiI0DPPPGMzt8XpU6ckSeXLV7Bqb968hVJSUlWzVi31uvtuy09UVJQaN26sHj3+Ha7x3bffysvLS3feeWeWtWzZvFlXrlyx6V2SoUL5Cjp50rY3TGZ2796tChUqZNqzJrsuXLggSQrwD7BqzyyIatKkicY8+KAk6b6hQ/Xiiy/pxRdf0sCBgySlD2Nq0qSJZf+MIKpNG+vgJS0tTa+8/LL69x+g6tVr3LS+nARRBUGQr6EDP+9UqTIVVaN241yf5+qV85IkXz/rz/XY4d8kSZWr1cn1uTPUrttUfe8ZK0nq1nu4Hpr8sh6a/LI6dbtHUvrwp9p1/w2vIyOu6szJQ2rUxDp4Mael6b3FL6pD14GqVPnm90PlqnV09NCvMpmkCiXTVMQp18YDAAAAnEeugpdKlSpZ/btYsWKSpIh/hiqcOXNGbm5uNkMbypQpo6CgIJ05cyY3l5UklS9f3map32PHjunPP/9UcHCw1U+NGukPEBmT9WbUdf3QJUmqWbNmrutB9hULSr9PgoODFRpa3WZ7ZFSkPDw8VLRoUav2tLQ0nT59yup+unTpkmJiYmzuse+++1ZNmzWTt7d3pjWcOnVKs2e/pAYNG6p3nz6Z7hMQEKDExEQlJCTc9PXExsbqypUrCq1u+1puflyMIiIidPnyJX355Q69+84ieXp6ql1764fhzIKoChUqqEWLlpKkO+64wxJCNWjYUFL6MKYKFf7dP6sg6uOPP9aFixc1/uGsewZZrpmDIKogiI2NVVjYFVWtmrPPNT4+VjHREQoPu6R9P2zXuv8tUpEinjZzqVw4l/5elipd/rZrLVWmouo1Sr8fata5Q2069labjr0VWiv9fmjYpK1Klalo2f+3n79VkSJeqtvAuvfSl1s/UtjVi7pn2KPZuGYFnf/7hMqXMMs3818zAAAAANfJ1d8q3d3dM22/ccJFkx36n2c2CajZbFb9+vU1Z86cTI+pWLFipu3IP5cuXdKiRQsVGhqq48ePa+WKFfrP2LHZOvbs2b+VlJRkFZgd+2dFouuDl5SUFO3Zs0eP3tCjKUNYWJgefeRh+fn56fXX37jlfXyr+zcuLlaS5FvUN1uvI8N/b3jd5cqV04svzVbp0mWs2rMKonIisyAqMjJSixYu0Nj/jFXx4sVveY7rg6jMfv8KmozPtUSxogryMxQZm73vsdnPjLH6d3Cp8nrosZdVoqT15xobEyl3dw95+1jfN2azWampKdm6VpEinrn6fv3tl29Up8Gd8vT6936IiY7Upx+8rX6Dxykg8Nb3g69fgJKTE+XpFi+p4N8PAAAAwO2ySyfxkJAQmc1mHTt2zGqYxOXLlxUZGamQkJA8vV61atV04MAB3XXXXTd9GMmo68SJE1a9XI4cOZKn9cDWy7PT54xYsHChXn/9dS1dukQ9eva06p0RFBik1NRUxcXFydf334fS48ePS5JVL5ljR49KkqpdF7z89uuvio2NtRlWI0kxMTF6ePx4xcTEaPmKlSpVqlSWtUbHRMvb2zvLXjMZfH39JElx8XE33e9GT02frpCQyoqNjdH69eu1/5dfbHpx5YWsgqgFb7+lgMBA3Td0aLbOk90gqqC4/nMtX8IsH0+TLke6yWy72rSVUeP+T2XLVVZ8fIx2f7lOh//8WUWKZP9zPfznz3rx6dG33lHSaws3qVyFqtk+tySlpqbo4G8/6t77J1m1f7z6Tfn6Barb3be+H7w8DQX6pr8RheV+AAAAAG6XXYKXnj17avr06Zo3b57effddS3tGj5S8XkVo8ODB2rJli5YsWaKxN/QmSEhIkNlslq+vr3r06KHp06frzTfftEyuK0nz5s2zOWd8fLz+/vtvlSxZ0i4PxYXJ1199pV27dunxJ55Q6dJl9MQTU/XjDz9o9ksvasHCRZb9KlepIkk6f/68ZZiYlB68uLm5qco/2yXp2PFjKl68uFWPjW+//UZVq1azTLSbISkpSRMnPKozZ07r3cVLbIaa3ej8+fOqWvXWD7V+fn4KDi6lE/8EQ9lVr159y6pGHTt20uhRI/XUk09qw8aNVr1bsgqisiuzIOrMmTP69NNP9cQTUy1D8CQpOSlJqampOn/+vPz8/BQYGGjZlt0gqqC48XMt7m8o0DdN0fEmJaVI5hsW8PH1Tm9o1KieatRM/1w7d+6oJybcr0Vzpmrxe5vk4/Pv51qyZKDS0lLl5R6jotf1lqpdq7ImT5uVrRorVywhX7/0AMS/aPr1/XwMFfM3W9Xk7/tv24Fff1FCfKzat29jaTt/7ox2bv9YYx+eKnPyZSUnp5/fnJYkw0hVctxZFS3qp6CgQPl6GfL1luLjCtf9AAAAANwuuwQvDRs21MiRI7V48WJFRkaqffv22rdvn1atWqV+/fpluaJRbo0YMUJr167VuHHjtHPnTrVu3VppaWk6fPiw1q5dq23btqlp06Zq1KiR7rvvPi1cuFBRUVFq1aqVvvrqK0uPiuvt27dPHTt21IwZM2wmCUb2xcXF6ZVXXlatWrV0333pf1EvVaqUxo9/WK+++oq2b9+url27Skq/byTprz//tAleypcvbzXM5fSpUzYBynfffae27aznSUlLS9O0qU/o999/19x58y3XuJnDhw6pZ8/shYPt2rXTp59+ogMHDmTr3Ddyd3fXoxMm6j8PjtH/PvxQD4z5d7hKVkFUdmUWRF25ckVms1mvvPKyXnnFdiWjXj17aOiwYZo6dZqlLbtBVEFy4+fq7iYV88t8yeSgf7KT4ABD5Ypn7OOmKY+lf667t35g9bnWr11F/5Nkjj+nchX+/VzLFS+heqH/rkZ1a+nXOu+f/n+L+f17/cxq+t9vu1W1ajU1rlPu32NPXJbZbNY7b72sd96yvR9G39ed+wEAAAC4TXZbj2Lp0qWqWrWqVq5cqXXr1qlMmTJ66qmnNGPGjDy/lpubm9avX6+5c+fqvffe07p161S0aFFVrVpVEydOtHpoXb58uYKDg7VmzRqtX79enTp10ubNm5kHxk7efvstXb16VW/MmWs1p8q9Q4Zo06aNeu3VV9W6dWv5+vqqQoUKCg0N1Z69e9Sv/7/L3544ftwmZAkLC5e3t7eio6MVEBCg8+fO6eTJk5r+tPXSum+88bp27dql9u3bKzoqSps//9xqe6+777b6919//aWoqCh16NghW69v1OjR2rJls56bOVNLli5ViRIlrLafPXtW33yz+6ZLSjdr1kz16tXTmjWrNWz4cHl5eUnKOojKjIdH+q+yOS3N0pZZEBUaGqo5c+fZHL/g7bcUHx+vJ6ZOU8WK1qtK5SSIKiic5XPNLe4HAAAAwHnkKHiZOXOmZs6cadM+atQojRo1yvrEHh569tln9eyzz+a4qMzOJ0m7du3K8pgiRYpo6tSpmjp16k3P7e3trfnz52v+/Pk33a9Dhw6WuS0ylslGzvz1119a+9FHGjz4XtWrV89qm7u7u57+v2d0/4jhevvttzRt2pOSpL79+mvRwgVKTEyUt7e3UlJSdPbsWXW66y6r49u0aa3NmzfruZkz9Macufr2u+/k5+9vs6xzxvw9u3fv1u7du21qvDF42bF9u8qWLas772xus29mKlasqNkvv6xpU6eqf7++urt3b4WGhiolJUUHfjugHTu2q0+fW/diGDlqlJ54/HFt3LBB9wweLElZBlGZyQgGPvjwA91rNiu4ZMlMg6hixYpZllm/3po1qyXJZltOg6iCwlk+19zifgAAAACch916vAB16tTRL/t/zXJ7vXr1tP/X36za+vXrp6VLFuuLLVvUf8AAFSlSRD//st/m2Bkzn9OMmc9Z/v3tt9+oZcuWlr/0Z1i2bHm2601OTtamTRs1+oEHcjRxaIcOHbX240+0atVK7dq5Ux+vXStPT09Vr15DU6Y8rgEDB97yHHfd1VkVK1bUe++t0oCBAy29g24MorJyxx13qEmTJtq+bZu8vbxUs1btTIOonMppEFWQOMPnmlvcDwAAAIDzMBk3rgFdSJlMJs2YMSPTHj3R0dEKDAzUufMXFBAQkP/FFTIrli/Xhg3r9dm69XJzc8veMSuW6447muRqnpUMH69dq2XLlmrjps+dZkLlmJgY3d2rpyZNmqz+AwZk+7iHHx6vokWL6rXXXs/1tZOTk9WzR3eNfuCBmw6pQc7l9nPNLe4HAAAAFBbR0dGqUL6coqKinOb5PXtPtUA+Gv3AA1q/YWO2QxdJGj36gdsKXSTpnsGDtXXbdqcJXSTJ399fo0aN1qpVK2W+1XrG12natKmGDx9xW9fesH69PDw8dM89g2/rPLCV2881t7gfAAAAAMehx0s20OMFAAAAAADnR48XAAAAAACAQoTgBQAAAAAAwE4IXgAAAAAAAOyE4AUAAAAAAMBOCF4AAAAAAADshOAFAAAAAADATgheAAAAAAAA7ITgBQAAAAAAwE4IXgAAAAAAAOyE4AUAAAAAAMBOCF4AAAAAAADshOAFAAAAAADATgheAAAAAAAA7ITgBQAAAAAAwE4IXgAAAAAAAOyE4AUAAAAAAMBOCF4AAAAAAADshOAFAAAAAADATgheAAAAAAAA7ITgBQAAAAAAwE4IXgAAAAAAAOyE4AUAAAAAAMBOCF4AAAAAAADshOAFAAAAAADATgheAAAAAAAA7ITgBQAAAAAAwE4IXgAAAAAAAOyE4AUAAAAAAMBOCF4AAAAAAADshOAFAAAAAADATgheAAAAAAAA7ITgBQAAAAAAwE48HF0AAAB5LersBcVdDXd0GQAKIJPJJL8ywfIvW9rRpQAAXATBCwCgQLl2/LTCjp5wdBkACrCYi5dlTk1TYMVyji4FAOACGGoEACgwkqJjFH7spKPLAFAIXP3rqFISEh1dBgDABRC8AAAKjLAjJ2QYhqPLAFAImNPSdO34KUeXAQBwAQQvAIACISk6hnldAOSr6POXlJqY5OgyAABOjuAFAFAgRJ294OgSABQyhtnMdw8A4JYIXgAALs+clqaYC5cdXQaAQij6wiWGOAIAborgBQDg8uLDriktJcXRZQAohFLi4pUYGe3oMgAATozgBQDg8mIuXnF0CQAKsdjLfAcBALJG8AIAcGmG2ax4JtUF4ECEvwCAmyF4AQC4tPjwCIYZAXCo1IREJUXHOLoMAICTIngBALi0+PBrji4BABQXxncRACBzBC8AAJcWd4VhRgAcj+8iAEBWCF4AAC4rNTFJybFxji4DAJQUFS1zWpqjywAAOCGCFwCAy6JrPwBnYU5LU8K1SEeXAQBwQgQvAACXlXAtwtElAIBFQjjfSQAAWwQvAACXZBiGEiKiHF0GAFjE0+MFAJAJghcAgEtKTUhUSly8o8sAAIukqGiZU1MdXQYAwMkQvAAAXFJCJL1dADgXwzCUGBnt6DIAAE6G4AUA4JISGWYEwAkxBBIAcCOCFwCAS4pnEksATojvJgDAjQheAAAuJy05mfldADil5JhYGWazo8sAADgRghcAgMtJjIqRYRiOLgMAbKSlpCg5Ns7RZQAAnAjBCwDA5TB5JQBnlsB3FADgOgQvAACXk3At0tElAECW+I4CAFyP4AUA4FIMs1lJ0TGOLgMAspQYRY8XAMC/CF4AAC4lOS5eaSkpji4DALKUGp+g1KQkR5cBAHASBC8AAJeSGEVvFwDOzTAMJUXHOroMAICTIHgBALiUJLrwA3ABTAIOAMhA8AIAcCkJEZGOLgEAbonvKgBABoIXAIDLMKemKjkmztFlAMAtJUXHyjCbHV0GAMAJELwAAFxGUnSsDMNwdBkAcEtpyclKSUh0dBkAACdA8AIAcBks0QrAlfCdBQCQCF4AAC6EFY0AuJIkvrMAACJ4AQC4EP56DMCVsLIRAEAieAEAuIi05GSlxMU7ugwAyLbEqGgm2AUAELwAAFwDw4wAuBrDbFYSK7EBQKFH8AIAcAl02QfgihgiCQAgeAEAuAQeXgC4oiS+uwCg0CN4AQC4BIIXAK6I7y4AAMELAMDppSQkKi0p2dFlAECOJf9/e/cdHXWVhnH8mfSeEFqoAUOTIkWkd5VeLSiCBhQRy6qoy9pBXdtagGVVFAuIiAURURAUpagIKAoiIr2EUBPSezJ3/4gZGSadJJNkvp9zcg751Xcmk5B5cu97k1Nlzc52dhkAACcieAEAVHrp8QnOLgEASsVYrTQHBwAXR/ACAKj0aKwLoCrjZxgAuDaCFwBApcebFgBVGX1eAMC1EbwAACo1Y7UqIynZ2WUAQKkRvACAayN4AQBUapnJKTSmBFClZaelKzs9w9llAACchOAFAFCp0ZQSQHWQnsjPMgBwVQQvAIBKLe1snLNLAIALxs8yAHBdBC8AgEotjca6AKqB9HhGvACAqyJ4AQBUWjmZmcpOTXN2GQBwwTISk2SsVmeXAQBwAoIXAECllZ6QJGOMs8sAgAtmzc5WZnKKs8sAADgBwQsAoNJKj0twdgkAUGaYOgkArongBQBQaaURvACoRtLOxju7BACAExC8AAAqJWO1KoPlVwFUI+nxhMkA4IoIXgAAlVJmcopysrKcXQYAlJms1DRlp2c4uwwAQAUjeAEAVEqpDMkHUA2lxcU7uwQAQAUjeAEAVEr0QgBQHfGzDQBcD8ELAKBSohcCgOqIpuEA4HoIXgAAlU5mcgp9EABUS5lJycrJzHR2GQCACkTwAgCodOjvAqC6MsYw6gUAXAzBCwCg0kmn+SSAaowGuwDgWgheAACVijFGqbFxzi4DAMpNasxZZ5cAAKhABC8AgEolKyWV/i4AqrXMpBT6vACACyF4AQBUKin8JRhANcfIPgBwLQQvAIBKhSH4AFwBP+sAwHUQvAAAKg1jtSqNFY0AuICUmLMyxji7DABABSB4AQBUGmln42XNznZ2GQBQ7rLT0pWVkursMgAAFcDD2QUAAJAn5UzsBZ1vcqwyKZkymTmSlb8kAygnbha5eXtIfp6yuJf+75jJp2IUGuBfhoUBACojghcAQKVgjClV8GIkmcR0mbhUWZMz/9oCAOXLKkmyyC3AW241/WQJ8C7xNVLOxCo0IrysSwMAVDJVcqqRxWKxffz4448FHvfRRx/ZjmvSpInD/g0bNuiJJ57QsGHDVLt27QKPAwCUv6yUVGUmp5ToHJOaqZyDscqJipM1OUOELgAqlpE1OV3ZR84q59BZmfSSTZVMj4tnWWkAcAFVfsTL4sWL1b1793z3vffee4Wee88992jHjh3lURYAoISST54p9rEmx8h6KlHWOPojAKgcrKkZsh6IkVstf7nVCZTFUvQ5xhgln4pRcKP65V8gAMBpquSIF0lyd3dXu3bt9OGHHyo7n0aMsbGxWr16tTp16lTgNQYOHKh///vfWrNmjXbt2lWe5QIAipB08nSxjjOpmco5EEPoAqASMrLGJCvnYEyxR78knyp+6AwAqJqqbPAiSePHj1dMTIzWrFnjsO/DDz9UVlaWJkyYUOD5//nPf/TII49o4MCBCg0NLc9SAQCFyExJVUZiUqHHGEk5Z1KUfeisTBYrHwGovEx6lrIPxsh6tuiAODXmLNONAKCaq9LByw033CCLxZLvlKL33ntPAQEBGjVqlBMqAwCURPKJwke7mCyrrEfOyno6UfRxAVAlGKOcEwnKiYqXybEWfJjVquRTMRVYGACgolXp4KVRo0bq06ePVqxYoeTkZNv2gwcP6scff9SYMWPk5+fnxAoBAEUxxijxxKmC9ydnKPvAmb+a5wJA1WJNTFPOgRiZ1IJHtSRGn6zAigAAFa1KBy+SNGHCBKWmpmrZsmW2bYsXL7btAwBUbunxicpMSnbYbnKMco4nKvvIWamQvxYDQGVnsnKUfeisck4lyeQzaC/tbJwyU+hbBQDVVZUPXq655hp5e3vbwhYpN3ipV6+eLr/8cidWBgAojsRjxx22meSMvxrolmx5aQCovP5qvHvgjExqlsPexGMnnFATAKAiVPngJSQkRMOGDdM333yjkydP6qefftKePXt0/fXXy93d3dnlAQAKkZOVpaTjf08zMpnZyjkap+wjNNAFUD2ZjGxlH4pRTnS8TFaObXti9AkZK6P7AKA68nB2AWVhwoQJWrZsmT744AMdOnTItq2s7dy5U/7+/mV+XQBwVSnHTij5SLSUkSO3xEy5JWfSOxeAazgpaY9kDfSWNdhL8nTTqax0+dat5ezKAKBKS0mpfCOmq0XwMnToUIWEhOjdd9/V8ePHdfHFF6tTp05lfp8hgweV+TUBwJU1tfiqrnuAAt28JVmcXQ4AOIlRQk66TuQkK0rpzi4GAFDGqkXw4u3trWuvvVbz58+XJN19993lcp8vV69hxAsAAAAAAJVUSkpKpRs0US2CF0m68cYbtWzZMlksFo0fP75c7tGuXTsFBQWVy7UBAAAAAMCFSUxMdHYJDqpN8NK7d2/FxMQ4uwwAAAAAAACbahO8lMabb76pN998U5KUlZW7rN+JEyfUrVs32zGvvvqqmjVr5pT6AAAAAABA1ebSwcuxY8e0ZcsWu22ZmZl22yrjMCUAAAAAAFA1WIwxLNxZhMTERAUHB+tY9HF6vAAAAAAAUEklJiaqYYP6SkhIqDTv392cXQAAAAAAAEB1RfACAAAAAABQTgheAAAAAAAAygnBCwAAAAAAQDkheAEAAAAAACgnBC8AAAAAAADlhOAFAAAAAACgnBC8AAAAAAAAlJNKG7ysXr1aHTp0kI+PjywWi+Lj451dEgAAAABUej/99JM6tL9EP/30k7NLASDJw9kF5Cc2NlZjx45VmzZt9Morr8jb21v+/v7OLgsAADjJZ599phmPPyZJeuedBerYqZPdfmOMBg8aqFOnTql37z6a+7//VUhdqampWrDgHf2+c6d+//13JSYm6oknn9KoUaMq5P4AqrZzf7ZJkpeXl4KDg9WsWXP17t1bo0aP5n0QUA1UyuDlp59+UlJSkp566ildccUVzi4HAABUEt7e3lr15SqH4OXnn3/WqVOn5OXlVaH1xMXF6Y3XX1e9evXUokVL/fwzf10GUHJ33HGnGjRooKzsbMXGxujnn37WCy/8R4veW6Q5c/6rFi1aOLtEABegUk41On36tCQpJCSkyGNTU1PLuRoAAFBZ9OrVS2u//lrZ2dl227/8cpVat26tmrVqVWg9tWvX1tpvvtWXq9do2n33Vei9AVQfPXv10rDhwzV69GjdcstkvTZvnl5//Q3FnT2re+65W+np6c4uEcAFKFHwMnPmTFksFu3fv18TJ05USEiIgoODNWnSJIcAJDs7W0899ZQiIiLk7e2tJk2a6OGHH1ZGRkah9+jXr58iIyMlSZdddpksFosmTpxo29e2bVtt27ZNffr0kZ+fnx5++GFJUkZGhmbMmKFmzZrJ29tbjRo10vTp0x3ul5GRoWnTpql27doKDAzUyJEjdezYMVksFs2cObMkTwcAAKhgg4cMUXx8vDZv/tG2LSsrS2u//lpDhgwt8fVuueVmXX3VGO3du1e33DxJ3bp20Yjhw/T1119Jyh1JM2H8Dera5TKNGjlCmzdvtjvfy8tLtSo47AHgGrp07apbp0zRiePHtXLlF7bthw4d0gP336c+vXupy2WddcO467V+/boir/fLL9v0wAP3a/Cggbqs86UaNPBKvfDCf+xCneXLl6tD+0v05+7dDue/+eZ8derYQadOnSqbBwi4kFKNeBk7dqySkpL07LPPauzYsVqwYIGeeOIJu2MmT56sxx9/XJ06ddKsWbPUt29fPfvss7r++usLvfYjjzyiKVOmSJKefPJJLVq0SLfddpttf2xsrIYMGaIOHTpo9uzZ6t+/v6xWq0aOHKkXX3xRI0aM0Ny5czV69GjNmjVL1113nUNds2fP1sCBA/Xcc8/J09NTw4YNK83TAAAAKlj9+g10Sfv2Wv3ll7Zt33//vZKTkzVo8OBSXTMxMUl3/+MutW3XTvfeO02enl568F//0prVq/Xgv6arV+/euvuee5SWlqYHHrhfKSkpZfVwAKBQw4cPlyT9+GNu2Lx//37deOMEHTx4SJNuvln33f+AfH19Ne3ee/XtN98Ueq2vv/pa6enpunbsWP3rwYfUo0cPfbBkiR599BHbMVdeeaV8fHy0atVKh/NXrVylzp07q27dumX4CAHXUKoeLx07dtRbb71l+zw2NlZvvfWWnn/+eUnSjh07tHDhQk2ePFnz58+XJN1xxx2qU6eOXnzxRa1bt079+/fP99pXXnmloqOj9cYbb2jIkCHq3Lmz3f6TJ09q3rx5dmHMe++9p7Vr12rDhg3q1auXbXvbtm01depUbdq0ST169NCOHTv03nvv6Y477tArr7wiSbrzzjs1fvx4/fbbb6V5KgAAQAUbMmSo5v53jtLT021vEC699FLVqVOnVNc7c+a0nn3uOduImW7du2n0qFF66KEHtXDhu2p3ySWSpKZNL9Idt0/V2rVraZ4LoELUrRumgMBAHYuKkiS98J/nVS8sTIvfX2LraXXddddp4sRIzZ49WwMuv7zAa91z773y8fGxfX7NNdeoUaPGmjv3vzpx4oTq1asnf39/9evfX19+uVr3TrtPbm65f6f/c/duHTx4QJF/zUQAUDKlGvEydepUu8979+6t2NhYJSYmSpJWrVolSbrvvLnO999/vyRp5UrHBLW4vL29NWnSJLttH3/8sS6++GK1atVKMTExto8BAwZIktatW2dX19133213/r333lvqegAAQMUaOHCgMjIytHHjRqWkpOi7jRs1ZGjJpxnl8fPz0+DBQ2yfN2nSVIGBgWratKktdJGkdu3aSZKijx0rffEAUEJ+vr5KSU1VQkKCtm7dqoEDByklJUVxcXGKi4tTfHy8enTvoaNHjxQ6Dejc0CUtNVVxcXFq36G9jDH6888/bftGjBihM2dO66efttq2rVq1Uj4+Pix8ApRSqUa8NG7c2O7zGjVqSMrt7B8UFKQjR47Izc1NzZo1szsuLCxMISEhOnLkSCnLlRo0aOCwYsG+ffu0e/du1a5dO99z8pr15tUVERFht79ly5alrgcAAFSs0NBQde3aVV+uWqX09DTl5OToiiuuLPX16tatK4vFYrctIDBQYWFhdtsCAwMlyfaHJgCoCKlpaQoNDdXRo0dljNErr/xPr7zyv3yPjTt7tsCpQCdOnNCrr76iDevXO/wcS05Osv27W7fuql27tlatXKWuXbvJarXqyy9Xq1+/fixtDZRSqYIXd3f3fLcbY+w+P/+XmLLg6+vrsM1qtapdu3Z6+eWX8z2nUaNGZV4HAABwniFDhurJJ59QbGyMevbqpaCgoFJfK28oveP24v2+AwDl5dSpk0pOSlKjxo1ljFWSdFNkpHr06Jnv8Y3O+wN5npycHE29bYoSExM1adLNatK0qXx9fXX69Ck9/thjslr//rnm7u6uIUOGaNmyZXr4kUe0ffuvOnPmtIYOG172DxBwEaUKXooSHh4uq9Wqffv26eKLL7ZtP3XqlOLj4xUeHl6m94uIiNCOHTt0+eWXFxr25NV14MABu1Eue/bsKdN6AABA+Rpw+eX697+f0m+//abn//OCs8sBgHLxxRe5qxn16N5DDRs0lCR5eHioW7duJbrOvn37dOTIET31739rxIiRtu15TXvPN3zESL377rvasGGDfvj+e9WoUUM9evQo5aMAUKoeL0UZ+tc869mzZ9ttzxuRUtarCI0dO1bR0dG2Rr7nSktLs60+MGRI7vzt//73v3bHnF+nJKWmpurPP/9UTExMmdYKAAAunJ+fnx5+5BFNvf129e3b19nlAECZ27pli+a/8YYaNGigocOGKbRmTXXufJk+WbpUZ86ccTj+7NmzBV7L3T33bd+5I/aMMXp/8eJ8j2/RooVatGihT5ct0zffrNXgwUPk4VEuf7MHXEK5fPe0b99ekZGReuONNxQfH6++fftq69atWrhwoUaPHl3gikaldeONN+qjjz7S1KlTtW7dOvXs2VM5OTn6888/9dFHH2nNmjXq3LmzOnTooHHjxunVV19VQkKCevTooW+++Ub79+93uObWrVvVv39/zZgxw6FJMAAAcL6RIyvHykIfLFmipKQknT6T21Nu44b1Ov1Xg8vrx42z9YYBgIL88P33OnzokLJzcnQ2NlZbt27V5s0/ql69epoz57/y9vaWJD308MOaNDFS115zta666mo1aNhAZ2PPasdvO3T61Cl99PHSfK/fpElTNWrUSLNeflmnT59WgH+A1q5dq8SkgntWDR8+Qi+//JIkaWgZ/+EccDXlFlu++eabuuiii7RgwQJ9+umnCgsL00MPPaQZM2aU+b3c3Ny0fPlyzZo1S++++64+/fRT+fn56aKLLtI999yjFi1a2I59++23Vbt2bS1evFjLly/XgAEDtHLlSvrAAACAUln47kKdOH7c9vk333yjb775RlLumxWCFwBFefXVVyRJnp6eCg4OVrNmzfXPf07XqNGj7RraRkRE6P0lH2jevNe0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ZfBy+vRpSVJISEiRx6ampsrPz7X/co4SsrjJeIbIkhkj35AaSo45dUGX27rrhJ5ZuFmeHm66vHO4wusFKTvHql0HY/XW57/pyMlE3XPdpbbjbxzSWmGh/srMturPw7H6+qcj2nUoRvOmD5SXp33fma+2HFaO1ap+nRqVur49R88qMSVTXVqHOew7E5+qD9f+KR8vx343Xp7uuvyycC1bv08jezeTxVK64OdceaOMjEflH220ceNGTf/nA/L09NTwESPUrFkzZWVla/uvv2jWyy/rwIEDevzxGbbj77jjTjVo0EAZmRna+dtvWrFihbb/+quWfrJM3t7edtdevvxT5eTkaMiQIRX9sPT77zsVFxen3r37OOw7deqk3nrzTfn6OoaA3t7eGjFipBa9t0jjbrihTF4PAAAAgCso0Z9aZ86cKYvFov3792vixIkKCQlRcHCwJk2apNTUVLtjs7Oz9dRTTykiIkLe3t5q0qSJHn74YWVkZBR6j379+ikyMlKSdNlll8lisWjixIm2fW3bttW2bdvUp08f+fn56eGHH5YkZWRkaMaMGWrWrJm8vb3VqFEjTZ8+3eF+GRkZmjZtmmrXrq3AwECNHDlSx44dk8Vi0cyZM0vydKAKM565jV3zRmCU1snYFD23aIvq1PDT6/8aqKlXddCQ7hdpRK9mevCmrnr9X4MUHhZkd07nVmEa0Dlcg7s11b3Xd9bV/VroREyKNv9+3OH6X289rG5t6jsEMiXx0+6TqlPDT+H1gh32vbniN7UKD1XzRvn3u+nToZFOx6Vqx/4zpb5/Hk8fP3n6+OYGX5W8sW70sWN68F/TVa9ePS37dLn+9a8HdfXV1+j666/Xc8//R8s+Xa6IiAi7c3r26qVhw4frqquu1oyZT+immyIVFRWl9evXO1x/xWefqW/ffg6BTEX4/rvvVa9+fTVr1sxh38svvaxLLrlErdu0yffcgYMG6cTx4/pp69byLhMAAACoNko14mXs2LFq2rSpnn32Wf3yyy968803VadOHT3//PO2YyZPnqyFCxfqmmuu0f33368tW7bo2Wef1e7du/Xpp58WeO1HHnlELVu21BtvvKEnn3xSTZs2tXuDExsbqyFDhuj666/XhAkTVLduXVmtVo0cOVLff/+9pkyZoosvvlg7d+7UrFmztHfvXi1fvtyurvfee0833HCDevTo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bd9zkydPvqDHCkhF/3xevXq17djSvv4lKScnR3PnztWll14qf39/BQcHq0+fPlq2bFm+x8fHx+v999/XuHHj1LRpU3l5eSkwMFBdu3bVnDlziv09cz6PUp2FSis9PV0DBgzQ5s2bVa9ePY0aNUqHDx/WO++8oy+++EKbN2/WRRdd5OwyAUnSU089pc8++6xE59x7772aM2eOfH19NXDgQKWnp+vrr7/WV199paVLl2r06NH5nhcREaFevXrluz0/06ZN0+zZs+Xh4aHevXurdu3a2r9/v1asWKEVK1bo9ddf15QpU0pUO3Cu999/X7feeqsk6eKLL9bIkSOVmJioTZs2acaMGVqyZIk2bNigOnXq2M755JNPNG3atBLdZ9++fbrppptKdE6HDh0UGRmZ777o6GitXbtWfn5+6tSpU4muC5zrmWeeUUpKii655BK1a9dOkrRr1y4tWrRIH3zwgZYtW6bhw4fbji/N63/w4MFq0qRJvvu2bdum33//Xb179853v7+/v6655pp897m7u5eoDqAo999/v2JiYgrcf/DgQfXp00eSFBYWpgEDBsjd3V1bt27V66+/rvfff1+rVq1y+F1n48aNeuWVVxQeHq7WrVurdu3aOnPmjH744Qdt3rxZn3zyib766it5eXkVeO+MjAx+50G5ufrqqxUQEOCwvUGDBrZ/l/b1n5OTo9GjR+uLL75QQECAevXqJavVqk2bNunqq6/WjBkzHP4Q9eKLL+rpp5+WxWJRhw4d1LVrV9v3zNatW7V06VKtWbNGfn5+JXugBtXKI488YiSZ7t27m6SkJNv2l156yUgyffv2dV5xwHmee+4589hjj5kVK1aYEydOGG9vb1PYj6Wvv/7aSDI1a9Y0e/futW3ftGmT8fLyMiEhISYuLs7unHXr1hlJJjIysth17dixw0gyISEhZteuXXb7lixZYiwWi/H397f7HgNKasGCBWbKlCnmjz/+sNt+/Phx07FjRyPJjBs3zm7fZ599Zu69916zePFis3fvXjNw4EAjyaxbt67A++zfv9/ccsstZt68eWbbtm3mySefNJLMjBkzSlX39OnTjSQzfvz4Up0P5Pn+++9NWlqaw/ZXXnnFSDJ169Y1WVlZtu2lef0XpkuXLkaSmT9/vsM+SSY8PLxU1wVKau3atUaSmTJlipFkIiIiHI7Zv3+/ufLKK80333xjrFarbXt6erqZOHGikWQaN25sMjMz7c47cOCAOXDggMP1Tp48adq2bWskmblz5xZa36OPPmosFouZPHmykWRuueWWUj5S4G99+/Y1ksyhQ4eKPLa0r/8XX3zRSDJNmjQx+/fvt23fvXu3qV+/vpFkNm3aZHfOM888Y6ZPn26OHDlit33v3r2mcePGRpJ56KGHSvx4CV6qkYyMDBMcHGwkmV9++cVh/yWXXGIkmZ9//tkJ1QFFKyp4GTJkiJFkZs2a5bDv7rvvNpLMiy++aLe9NMHL3LlzjSRz22235bs/73tpy5Ytxb4mUBKbNm0ykoy3t7fJyMgo8LhBgwaV+I3ns88+W+rgxWq1mkaNGhlJZvXq1SU+HyiuiIgII8ns2LGjwGNK8/rPs3fvXiPJ+Pj4mPj4eIf9BC+oKKmpqSYiIsK0bt3a9rrML3gp6hp57wHWr19f7PMWLVpkJJkxY8YUeMzvv/9uvLy8zOTJk80777xD8IIyU5LgpTCFvf7z/i9ZvHixw3lvvPGGkWRGjx5d7Hu9//77tiCnpOjxUo388MMPSkhIUEREhDp27OiwP2+47Oeff17RpQEXLC0tTd9++60k5Tv0uyxf397e3sU6rmbNmhd8LyA/7du3l5Q7vDs2NtbJ1fxt/fr1ioqKUlhYmK644gpnl4NqzNPTU5IKnf5wId577z1J0ogRIxQcHFwu9wCK44knntDBgwc1b9482+u+pHx9fdWiRQtJ0vHjx4t9XlHfZ8YYTZkyRcHBwXr++edLVRtQ3gp6/SckJOjAgQOScvvJnK9///6SpDVr1igjI6NY98r7/awk32d56PFSjezYsUOSCpxzn7f9t99+q7CagLKyZ88eZWRkqHbt2mrYsKHD/qJe3/v27dNDDz2k2NhY1apVS7169dLgwYPzbarYv39/eXh46MMPP9Tdd9+t1q1b2/Z98MEH2rlzp/r27VtgfxjgQh08eFBS7i/FoaGhTq7mb3lvVseNG0ePC5SbRYsWac+ePWrevLmaN29eLvdYvHixJGnChAkFHpOSkqKnn35aR48elZ+fnzp27Kirrroq314EQGn89ttveumllzRp0iT17t1bhw8fLtV1rFarjhw5Iim3/0VxxMXF6aWXXpIkDRs2LN9jXnvtNW3atEnvvvtupfq/CNXLW2+9pdjYWLm5ualFixYaPXq0GjduXOzzC3r9p6Sk2P5do0YNh/Py/oCalpamvXv32nqNFSbv97Pifp+di+ClGjl69Kgk5fum9NzteS9MoCop6vXt7++vkJAQxcXFKSkpSYGBgXb7N23a5LDyUbt27fTJJ584/GLfrFkzzZo1S/fcc4/at2+v3r17q06dOtq3b59+/fVXjRgxQm+//XYZPjrA3pw5cyTlNgYt7gis8paenq5PPvlEknTjjTc6uRpUJy+88IJ27dqllJQU7d69W7t27VL9+vW1ZMmScgn4fvzxRx04cEA1a9bUkCFDCjwuJiZGjz76qN22++67TwsXLizwjSpQXFarVZMnT1ZISIj+85//XNC1lixZotOnT6t27drq0aNHvsfs27dPTz/9tKxWq06dOqVNmzYpOTlZU6dO1fjx4x2Oj46O1kMPPaT+/fvzMx/l6t///rfd5w888IAee+wxPfbYY8U6v6DXf2hoqNzd3ZWTk6MjR46oVatWdueduxLSkSNHihW85P1+NmrUqGLVdi6mGlUjeUuPFtRh2d/fX5KUlJRUYTUBZaWo17eU/2s8ODhY//znP7V582bFxsYqNjZW33zzjbp166adO3dq4MCBSkhIcLjWXXfdpffff19eXl5at26dPvzwQ/3yyy8KCwvTlVdeyV9+UG5WrVqlt956S56ennrqqaecXY7NihUrlJCQoDZt2uQ7nRUorTVr1mjhwoVaunSpdu3apfDwcC1ZskSXXnppudxv0aJFkqTrr7++wKkdN910k1avXq3o6GglJyfr119/1Y033qjY2FhdddVV+umnn8qlNriOuXPn6qefftILL7xwQVOXo6KidO+990qSnnzyyQLD+lOnTmnhwoVatGiRvvrqKyUnJ+vuu+/W888/n+/o37vuukvp6el67bXXSl0bUJg+ffpo0aJFOnDggFJTU7Vnzx49/fTT8vDw0OOPP24LOQpT2Ovfx8dHl112mSRpwYIFDuee+0fU4rw/njdvntauXauQkBA9+OCDRR7voMRdYVBp3XrrrUaSeeSRR/Ldv2/fPiPJNG/evIIrA4qnsOa6ixcvNpJMz549Czy/QYMGRpKJjo4u8l7Z2dmmd+/eRpJ55pln7PZZrVZzzz33GElm6tSpZu/evSY5Odls2bLFds4dd9xRsgcHFMPu3btNjRo1jCQze/bsIo+vyOa6I0aMMJLMc889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fqZNmzZmxowZJikpKd/aIiMji3WvkmLECwAAAAAAQDlhjCQAAAAAAEA5IXgBAAAAAAAoJwQvAAAAAAAA5YTgBQAAAAAAoJwQvAAAAAAAAJQTghcAAAAAAIByQvACAAAAAABQTgheAAAAAAAAygnBCwAAJbRu3TpdffXVatCggby8vFSjRg21bNlS1157rf73v/8pISHB2SWiFNavXy+LxaKJEyc6tY5+/frJYrHo8OHDTq2jtG6++Wb5+/vr9OnTxT5n5syZslgsWrBgQYnuNXr0aNWtW1fJycklrBIAgIpD8AIAQAk8+eSTGjBggJYtW6bg4GANHz5cAwcOlK+vr5YtW6Z//OMf2r17d4XVM3HiRFksFq1fv77C7okLY7FY1KRJE2eXUS527typhQsX6s4771SdOnUu+HpNmjSRxWIpcP/jjz+u06dP6z//+c8F3wsAgPLi4ewCAACoKrZt26aZM2fK09NTH330kUaPHm23/+TJk3rvvfcUEhLilPpQPbz77rtKTU1VgwYNnF1KiT366KNyd3fXAw88UCH369SpkwYNGqSXXnpJ99xzj2rWrFkh9wUAoCQY8QIAQDEtW7ZMxhiNHTvWIXSRpLCwMD3wwANq1apVxReHaqNx48Zq1aqVPD09nV1KiURFRemLL77QoEGDymS0S3FNmDBBqampWrhwYYXdEwCAkiB4AQCgmM6cOSNJql27drGOz8jIUK1ateTn56f4+Ph8j9m0aZMsFov69u1r22aM0eLFi9WrVy/VrVtXPj4+atSoka644gq98sortuMsFovtzWb//v1lsVhsH+f3B1m9erWGDRum2rVry9vbWxdddJHuu+8+xcbGOtR07vSltWvXqk+fPgoMDFSdOnV066232nrYnD59WrfddpsaNGggHx8fdenSpVRTnrKysjRv3jz16tVLISEh8vX1VbNmzTRp0iRt27ZNkrR06VJZLBbdcMMNBV5nypQpslgseuedd+y2p6Sk6Pnnn1fnzp0VFBQkf39/tWrVSnfeeaf27t1b7DpL8hzmZ8GCBbZpM0eOHLH7evXr1892XEE9XvKmKGVnZ+upp55Ss2bN5Ovrq4svvtjuMX/77bfq37+/goKCVKNGDd10000F1pidna3XXntN3bt3V1BQkHx9fdWhQwfNnj1b2dnZxX5uJOntt9+W1WrVuHHjCjxmxYoV6t69u/z8/FSzZk1dffXV+X4N8vrtHDlyxPbY8z7On6Y1evRo+fr6av78+SWqFwCAisJUIwAAiqlRo0aSpE8++UQPPfRQkX/V9/b2VmRkpF5++WUtXrxYd955p8MxeW8Wp0yZYts2ffp0vfjii/L29lafPn1Uq1YtnTx5Ur/99pv2799vu05kZKS+//57HThwQIMGDVJYWJjtGgEBAbZ/P/jgg3r++efl5eWlyy67TPXq1dOOHTs0a9YsrVixQj/88IPq1q3rUNunn36qV155Rd27d9fgwYO1efNmvfnmm9q3b5+WLl2q7t27KycnR71799bhw4e1ZcsWDR48WD/99JPatWtXrOc0JSVFQ4cO1caNG+Xv728LXw4fPqzFixcrODhYl156qUaNGqWwsDAtW7ZMsbGxDlNKkpOTtWTJEgUFBem6666zbT9x4oSuvPJK7dq1SzVq1FC/fv3k7e2tgwcPat68eWrevLlatGhRZJ2lfQ7P1axZM0VGRmrhwoXy9/fXNddcY9tXklFSY8eOtYUrERER2rBhg26++WZJUmBgoMaNG6du3bpp0KBB+vHHH7Vo0SIdOnRIGzdutOuXkpaWpmHDhmndunUKDQ1Vt27d5OPjoy1btmjatGlat26dPv30U7m5Fe/vdF988YUk2YVI55o3b55uv/12WSwW9e7dW/Xq1dPmzZvVpUsXjRgxwu7YsLAwRUZGaunSpUpJSVFkZKRtX61ateyODQgIUOfOnfXdd9/p4MGDuuiii4pVLwAAFcYAAIBiOXDggPH19TWSTGBgoImMjDTz5883v/zyi8nOzs73nD179hiLxWLat2/vsC8hIcH4+fmZGjVqmLS0NGOMMWlpacbb29sEBgaagwcP2h2flZVlNm7caLctMjLSSDLr1q3L9/4fffSRkWTatm1r9u3bZ9tutVrN448/biSZ6667Lt9rurm5mS+++MK2PTEx0bRt29ZIMq1btzYTJkwwmZmZtv2PPvqokWRuuummfGvJzy233GIkmT59+pjTp0/b7Tt58qTZvHmz7fOHH37YSDKzZs1yuM78+fONJHP77bfbbb/88suNJDN27FiTlJRkt+/QoUNmx44dts/XrVtnJJnIyEi740rzHBZGkgkPDy9wf9++fY0kc+jQIYfz8uo497n69ttvjSRTr149U7NmTbuvWUJCgmnTpo2RZL799lu7691xxx222uPj423bExMTzdChQ40k89prrxXrMSUlJRl3d3dTv379fPcfPnzY+Pj4GE9PT7N69Wrb9szMTDN+/HjbY3vnnXfszgsPDzfF+XX1/vvvN5LM22+/Xax6AQCoSAQvAACUwNq1a02jRo1sbxTzPkJCQsztt99ujh8/7nDOgAEDjCSzdetWu+2vvfaakWTuvvtu27ZTp04ZSaZDhw7Fqqeo4KV9+/ZGktm5c6fDPqvVajp06GDc3d3NmTNnHK45YcIEh3PmzJljJJmgoCBz9uxZu33x8fHGYrEUGiqcKzo62ri7uxtvb29z+PDhIo8/fPiwcXNzM61bt3bY17VrVyPJ/PLLL7ZtW7ZsMZJMnTp1TGJiYpHXLyh4Kc1zWJgLDV7Wrl3rcE7Hjh2L/JrNmDHDtu3UqVPG09PTNGrUyKSmpjqcc+LECePl5WUuueSSYj2mvOe6f//++e7PC6jyC+ViYmKMn5/fBQUvecHbud9LAABUFvR4AQCgBC6//HLt379fy5Yt09SpU9WpUyd5eHgoPj5er732mjp06KA9e/bYnTN16lRJcuhBkd80ozp16qhhw4bavn27HnzwQR08eLDUtZ4+fVo7duxQ8+bN1bZtW4f9FotFPXv2VE5Ojq2XyrkGDhzosC1vGkfnzp1Vo0YNu33BwcEKDQ3ViRMnilXf+vXrlZOTo8GDBys8PLzI48PDwzV48GD98ccf2rRpk237zp07tWXLFnXu3FkdO3a0bV+7dq0kady4cQoMDCxWTee70OewrHl6euY7lSfv61LY1+zcr8v69euVlZWlwYMHy9fX1+GcsLAwNW/eXDt37lRaWlqRdZ0+fVqSHF4Teb777jtJ0vXXX++wr2bNmvnWXRKhoaGS/u7DBABAZULwAgBACXl5eWnMmDF67bXXtG3bNp05c0avvfaaatSoodOnT+uuu+6yO3706NEKCwvTkiVLlJycLEn65Zdf9Msvv6h79+5q06aN3fELFy5U7dq19fzzzysiIkJNmjRRZGSkvvzyyxLVmdecdd++fXbNSc/9yGvWGxMT43B+fssZ5/WOKWip44CAAGVmZharvqioKElSREREsY6X8g+x8v596623XvD1z3ehz2FZCwsLk7u7u8P2wr4uefsyMjJs2/Ie1/z58wt8XLt27ZIxRmfPni2yrryGywUFXMePH5ekAgO28xvmllRQUJAkFdjEGgAAZ6K5LgAAFygkJERTp05V/fr1NWrUKK1bt06pqany8/OTlDtK4eabb9YzzzyjDz74QJMnT9abb74pyTEskKQBAwZo//79+uKLL7R69WqtX79e7777rt59911dffXVWrp0abHqslqtknLfrA8aNKjQY/N7Q1xYU9XiNlwta0OHDlWjRo300Ucfac6cOfLy8tJ7772ngICAQlfTKa0LfQ7LWlHPe3G/LnmPq0OHDmrfvn2hx3p7exd5veDgYElSUlJSse5f1vKCn5CQEKfcHwCAwhC8AABQRgYMGCBJysnJUXx8vC14kXKnEz333HOaP3++brjhBr3//vsOK/CcKygoSDfccINt+eTNmzfr2muv1SeffKJVq1Zp6NChRdbTsGFDSbmrwCxYsOACH13Zy1sl6sCBA8U+x93dXbfeeqsef/xxLV68WEFBQYqLi9PkyZMdRluU5vrnq+zPYWnlPa5evXpp7ty5F3y9vBW+ChodU69ePe3Zs0dHjhxR69atHfbnLRtdWnFxcZKKv9Q7AAAVialGAAAUkzGm0P379++XlDsV6fwlb/P6k2zdulWPPvqoEhISNH78eLtwpjDdunXTjTfeKEn6/fffbdu9vLwkSdnZ2Q7nNGzYUK1atdIff/yhvXv3Fus+Falfv35yd3fXmjVrbNOCimPy5Mny8PDQ/PnzC5xmJElXXHGFJNlN8Sqp8ngOPT098/16VaT+/fvL3d1dX3zxhbKysi74em3atJGHh4dDf6M8vXv3liR99NFHDvvOnj2rr776Kt/zCnt9n2v37t2SckfwAABQ2RC8AABQTI899pj++c9/5juCIjo6WrfddpskaeTIkbY3jOfK608ya9YsSfmHBUePHtWCBQuUmppqtz09PV3r1q2T9PdIDkmqX7++JBX4hvexxx6T1WrV1Vdfre3btzvsj42NdWj6W1Hq16+vm266Senp6YqMjFRsbKzd/tOnT2vLli0O59WrV08jR47Ur7/+qg0bNuiSSy5Rly5dHI7r0qWL+vfvr9OnT2vKlClKSUmx23/48GHt3LmzyDrL+jmsX7++Tp065dR+JA0aNNDNN9+sw4cPa9y4cTp16pTDMfv379cnn3xSrOv5+/urY8eOOnHihKKjox32T5o0Sd7e3lq8eLGt6bEkZWVladq0aQ5fmzxFvb7zbN26VZLUt2/fYtULAEBFYqoRAADFlJycrDlz5ujFF19UixYt1Lp1a/n4+OjYsWPasmWLsrKy1KxZM82ePTvf8/P6k0RFRTmswJPn7NmzmjRpku6880517txZDRs2VEpKijZt2qQzZ86oc+fOuuqqq2zHjxgxQk8++aQeeOABff3117aRNs8//7xq1qypG264Qbt27dIzzzyjSy+9VB06dFBERISMMTpw4IB+++03BQQE5BsCVYQ5c+Zoz549WrduncLDw9WnTx8FBQXpyJEj+uWXX3T77bera9euDudNnTpVy5Ytk2S/KtT5Fi1apMsvv1xLlizRmjVr1KtXL3l7e+vAgQPavn27XnrpJbVr167QGsv6ORw5cqTmzp2rTp06qUePHvLx8VHLli31z3/+s1jnl5U5c+bo8OHD+uSTT7R69Wp16NBBjRs3VkpKiv744w/t379fo0aN0tVXX12s6w0bNkw//fST1q9fr/Hjx9vta9q0qV566SXdddddGjRokPr06aOwsDBt3rxZcXFxGj9+vBYvXuxwzZEjR2rDhg26/PLL1b9/f/n7+6tWrVp67rnnbMckJyfr559/VqtWrWwrOAEAUKk4dzVrAACqjjNnzphFixaZCRMmmHbt2pmaNWsaDw8PExoaanr27Gn+85//mOTk5EKvMWHCBCPJvP766/nuT0xMNC+99JIZOnSoadKkifHx8TE1a9Y0nTt3NrNmzTIpKSkO5yxevNh06tTJ+Pr6GklGkjl06JDdMRs2bDDXXnutqV+/vvH09DQ1a9Y0l1xyibnrrrvMhg0b7I6NjIw0ksy6desc7rVu3TojyURGRuZbf3h4uCnprxcZGRlmzpw5pkuXLiYgIMD4+vqaiIgIM2nSJLNt27Z8z0lLSzOenp7G19fXxMXFFXr9xMRE8+STT5pLLrnE+Pr6moCAANOqVStz1113mX379hX7sZXkOSxMcnKyueuuu0yjRo2Mh4eHkWT69u1r29+3b998v4aSTHh4eL7XLO3XLDs72yxcuNAMGDDAhIaGGk9PT1O/fn3TvXt388QTT5g9e/YU+3EdPXrUuLu7m6FDhxZ4zKeffmq6du1qfH19TY0aNcyoUaPM7t27zYwZM4wk884779gdn5WVZR599FETERFhPD09830O3n33XSPJvPTSS8WuFQCAimQxpogJ6wAAoEykpqaqQYMGys7O1vHjxwtcehdFW7JkiW644QZFRkZWq6a3Vd2YMWP0xRdfKCoqSmFhYRVyz0GDBun777/X0aNHVbNmzQq5JwAAJUGPFwAAKsgrr7yi+Ph4RUZGErpcgKysLD3//POSpDvvvNPJ1eBcTz31lKxWq1588cUKud8vv/yir776Svfffz+hCwCg0mLECwAA5Sg2Nlb/+te/dOrUKa1atUp+fn7avXu3bTlfFN+KFSu0fPlybd26Vbt27dLo0aP16aefOrssnOfmm2/Whx9+qEOHDtmWmS4vo0eP1o8//qgDBw4oICCgXO8FAEBpEbwAAFCODh8+rKZNm8rLy0vt2rXTiy++qH79+jm7rCpp5syZeuKJJ1SjRg0NGTJEc+fOVWhoqLPLAgAAKBTBCwAAAAAAQDmhxwsAAAAAAEA5IXgBAAAAAAAoJwQvAAAAAAAA5YTgBQAAAAAAoJwQvAAAAAAAAJQTghcAAAAAAIByQvACAAAAAABQTgheAAAAAAAAygnBCwAAAAAAQDn5P1vnWggrLFEOAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from qiskit import pulse\n", - "\n", - "with pulse.build() as h_q0:\n", - " pulse.play(pulse.library.Gaussian(duration=256, amp=0.2, sigma=50, name='custom'),\n", - " pulse.DriveChannel(0))\n", - "\n", - "circ.add_calibration('h', [0], h_q0)\n", - "\n", - "schedule = build_schedule(circ, backend)\n", - "schedule.draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Notice that the initial pulse on `D0`, the Hadamard gate, is now implemented with our custom pulse.\n", - "\n", - "## Scheduler methods\n", - "\n", - "The scheduler can follow multiple routines. By default, it follows an _as late as possible_ (ALAP) rule. Another scheduling method is _as soon as possible_, (ASAP). For both methods, the output schedule is minimal: in the longest-duration operation path of the input circuit, the start time of each operation is the end time of the proceeding operation. The methods determine how to schedule operations outside that longest path.\n", - "\n", - "This is made clear through an example:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.773164Z", - "iopub.status.busy": "2023-08-25T18:25:57.772613Z", - "iopub.status.idle": "2023-08-25T18:25:57.965098Z", - "shell.execute_reply": "2023-08-25T18:25:57.964225Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "circ = QuantumCircuit(2, 2)\n", - "circ.x(0)\n", - "circ.x(0)\n", - "circ.x(1)\n", - "circ.measure([0, 1], [0, 1])\n", - "\n", - "circ.draw(\"mpl\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For our mocked backend, these $X$ gate operations are each the same duration (`duration = 160 dt`). For the schedule to be minimal, the two $X$ operations on qubit 0 will be scheduled back-to-back, and the measurement pulse will immediately follow the second of those pulses.\n", - "\n", - "ALAP will choose the latest valid time to schedule lesser-constrained operations, so the $X$ gate on qubit 1 will play in sync with the second $X$ gate on qubit 0." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:57.969921Z", - "iopub.status.busy": "2023-08-25T18:25:57.969366Z", - "iopub.status.idle": "2023-08-25T18:25:58.202660Z", - "shell.execute_reply": "2023-08-25T18:25:58.201977Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "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qFBBxYoV05dffilJiomJ0bhx49SyZUsFBgbK09NTwcHBeuihh7R379401ydJhw4dsntv27VrZ1suozlXdu7cqf79+6tixYry9PRUUFCQHnroIe3Zs8dh2euPyX///VePPvqoKlasKC8vL9WtW1dz5szJ4NXMeVu2bJEku323Kl26tOrXr68rV65oxYoVTq3Px8dHNWrUkOT8sXrw4EHt2rVLPj4+uvvuux3q77//fknS119/7dT6rJYvX66WLVuqePHiKlOmjHr16uVwPEj/9x5Zv0+vPyZuPB4BAABQdJFcyaKKFStq2LBhOnfunP773/861earr77Sc889p5MnT6p+/frq2bOnKlWqpGXLlqlVq1b64Ycf0m07cuRIPfvsswoJCdGdd96puLg4Pffccxo3bpx+/fVXtWnTRseOHVPHjh1VsWJFLV++XHfccYcuXrxot57U1FT1799f/fr10+bNm9WwYUN17dpVSUlJevXVV9W+fXuHNtdPipoZ69atU/369fXee+8pJSVF9957r1q1aqWEhASNGzdOBw4ccHpd48eP14MPPihPT0/dc889qlu3rqRrk1w+8MAD6tevn37++Wc1aNBAPXv2VHBwsFasWKH33nsvUzE7o2HDhrbbaCpUqKDIyEjbX2bmkchO1tcyMDDQqeV/+eUXxcbGqnz58rrrrrtyMjQHERERKlasmPbv368jR45kyzr37NmjZs2aadOmTWrfvr06duxoG1Xy0Ucf6bXXXlNSUpKaNWumbt26yc/PTwsWLFCzZs20fft223rCwsJsc9T4+vravbedO3e+aRw//fSTmjZtqk8++UQVK1ZUr169VL58eS1YsEBNmzbVunXr0mwXHx+vli1bavny5WrTpo1atWql3bt3a/Dgwfroo48clrcmI3M6mWedsDa9Wx/LlCkjSdq2bZtT60tNTbUlKZw9Vq3rrlu3bpojhRo3bixJdu/jzcyYMUPdu3fXxo0b1axZM3Xs2FFbtmxR8+bNtX//frtlAwMDFRkZKV9fX0myOyasiR0AAABABpkiybi5uRljjDlx4oQpXry48fX1NadOnbItM378eCPJjB071q7t9u3bzV9//eWwzu+//954enqa0NBQk5qaalcXEhJiJJlKlSqZmJgYW/muXbuMl5eXKV68uKlataqZPn26rS45Odl06NDBSDJz5syxW9/EiRONJNOuXTtz/PhxuzaDBw82ksxzzz1n1+bgwYNGksnM4RIXF2fKlStnJJn//ve/5urVq3b169evNydPnrT9f+zYsUaSmTt3rt1y4eHhRpLx9vY2a9ascdjO66+/biSZOnXqmAMHDtjVxcfH27WJjo42kkxkZGSaMUdGRhpJJjo62q7c+h5cz/qahIeHp/MKZM7ixYszjO1m7rzzTiPJPPHEE04tP3ToUCPJjBgxIkvbS8vcuXOd3oewsDAjyaxcudJWlt7rbyXJhISEpLlNSebxxx83V65ccWj322+/ORwbxhgzZ84cI8m0b9/eqW1dz3pcHjx40FZ2/vx5U6FCBSPJvP/++3bLv/3220aSCQ4ONhcvXrSVW49JSaZPnz7m0qVLtrply5YZSaZKlSoO27d+XrJ6vBjj3DHXr1+/NL8TrOrVq2ckmV69ejm1zYULFxpJply5cnb7mpGpU6caSaZnz55p1sfHxxtJpnTp0k6tLzY21nh7exsPDw/z/fff28ovX75s+vfvb3s/bvwuSut7AAAAALBi5IoLKlSooEcffVRJSUmaMGHCTZevV6+ebr31VofyTp06qXfv3tq/f7/++uuvNNu+9tprCg0Ntf2/Vq1a6tq1qy5cuKDg4GANGzbMVufp6aknn3xS0rXbVqyuXLmiiRMnytfXV59++qndlWNPT0+99957CgwM1MyZM5Wammqr8/DwUM2aNVWzZs2b7qPVRx99pNOnT6tz58565plnVKyY/aHWsmVLlS9f3un1DR48WOHh4XZlly9f1uTJkyVdm/DSequOlb+/v0ObwmjGjBlatWqVAgIC9Pzzz990+eTkZH322WeScveWoOuVLVtWknT27NlsWV+5cuU0YcIEubm5OdTddtttDseGJA0cOFCtWrXSmjVrlJCQ4HIMS5Ys0cmTJ9WyZUsNHz7crm7kyJFq0qSJjhw5os8//9yhrZ+fn95//315eXnZynr06KG6devqn3/+cbj9qGzZsqpZs2aOTwBtferP4sWLdfnyZbu633//XTt27JB07UlEN3P48GE99dRTkq59n12/rxk5f/68JNk9Bet61hElzsQgXfuuuHTpkvr27atOnTrZyj08PDR16tR0twMAAABkhOSKi5577jn5+vpq+vTpOnny5E2XT05O1ldffaWXXnpJQ4cOVVRUlKKiomw/Uvbt25dmu7Ru3ahevfpN644fP24r27p1q86cOaPbb7/dblJIKx8fHzVp0kRnz561iyMoKEi7d+/W7t27b7p/VqtWrZIkPfLII063yUi3bt0cyn7//XfFx8erQYMGatGiRbZsp6BZt26dnnzySVksFs2ZM0eVKlW6aZsVK1bo7NmzqlWrlpo2bZoLUToy/3+uoMzeapaeO++8M8MfxefPn9fixYv13HPP6eGHH7Z97o4fPy5jjMOtIFlhveWnf//+adYPGDDAbrnrNWnSxHaLzfWs85Nc/zmWrj1da/fu3Ro/frxLMd9M//79FRwcrH/++UfdunXTX3/9pXPnzumHH35Qr169bE/nuTF5eqOkpCTdd999OnPmjHr06GGXDM5t1te/T58+DnVlypTJ9dvkAAAAUDjw3EoXlStXTsOHD9fEiRP11ltvacqUKekuu2PHDnXr1i3NSTCt0rv6mtYjX0uUKHHTuuTkZFuZdbs//vjjTX/UnjlzJlMjVW50+PBhSbIbbeOKG5+QkxPbyE7PPPOMzpw5Y1fWunVrDRkyJNu28ddff6l79+66fPmy3n33XfXs2dOpdtanBOXVqBVJttemdOnS2bK+tI4Pq9WrV6tPnz46ffp0uss4O+ohI9YJWtOb5NRafvToUYe64ODgNNuULFlSkv3nODeVKFFC33zzje655x6tXLlSK1eutNWFhYVp1KhRmjBhQoaPo09JSVHv3r31+++/q3Xr1raJlDMTg6R0J9u2zgtjfa1uxvo+hYSEpFnPJLUAAADICpIr2eDZZ5/VtGnTNGPGDI0ePTrNZYwxioiIUGxsrIYNG6Zhw4apevXqKlGihCwWi1588UWNHz/edkX/RhldGb7ZVWMr660+YWFhatWqVYbLpnUVPS8589hYV11/K5Srli5dmubTmrIruXLw4EHdddddOnv2rMaNG6cnnnjCqXbx8fH69ttvZbFY0h1hkdMSExNtE/DWqVPHqTY3e2/SOz7Onz+viIgI/fvvv3rllVfUp08fhYSEyMfHRxaLRf369dPixYvT/dxlp4wSms5+hvNCgwYNtGfPHi1ZskRbt27V1atX1bhxY/Xp08c2ciat2x2la+9bZGSkvvvuOzVs2FBff/21fHx8MrV9a+IsvcmPreXpJUsAAACA3EByJRuULVtWTzzxhMaPH6/x48eneWuG9baapk2bavr0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W13dunW1ZcsWtW3bVsWLF9eLL74oSUpOTtbYsWMVFhYmLy8vVa5cWaNHj3bYXnJyskaOHKly5cqpZMmS6tatm44cOSKLxaJx48Zl5uUAUESVLlNGTz71lDZv3qzly7+ylb/55htyd3fXs6Ofs1t++VdfKTy8nby8vLK0vbs6ddLxY8e0edMml+IGACCn0UcCKKrcs9IoIiJC1apV0/jx47V161Z99NFHKl++vCZMmGBbZsiQIZo/f77uv/9+jRo1Shs3btT48eO1a9cuLVu2LN11v/TSS6pZs6Zmzpyp1157TdWqVVNoaKitPi4uTl26dFGfPn00YMAAVahQQampqerWrZt++eUXDR06VLVr19aOHTs0ZcoU7d27V19++aVdXAsXLlS/fv10++23a/Xq1br77ruz8jIAKMLuu6+Xvl7+taa8/bbatg3Xht9+06+//qrnnnteFSpUsC139MgR7d27VwMefNCu/Rtv/EefLVmS4TZWfPudgoKCVKdOHfn7+yt6TbSat2iRI/sDAEB2oY8EUBRlKbnSqFEjzZ492/b/uLg4zZ4925Zc2bZtm+bPn68hQ4Zo1qxZkqTHHntM5cuX16RJkxQdHa327dunue6OHTvq6NGjmjlzprp06aKmTZva1Z84cUIzZszQI488YitbuHChVq1apbVr16p169a28rp162rYsGFav369br/9dm3btk0LFy7UY489pg8++ECSNHz4cPXv31/bt2/PyksBoIiyWCwa8/LL6vNAhN544z/6Y+tW1bn1Vj3Qp4/dcn9u2yZJql27tl35nXfeqV07d+rEiRMaOfJpW/n06dPk6emlh4c+rKCgIFt5rdq1te3PP3NuhwAAyCb0kQCKoizNuTJs2DC7/7dp00ZxcXFKTEyUJH377beSpKefftpuuVGjRkmSVqxYkZXNSrp2b+XAgQPtyj777DPVrl1btWrV0pkzZ2x/HTp0kCRFR0fbxTVixAi79k899VSW4wFQdIWFhemhyEj9+MMPOnv2rF5++WWHyfhiDx6UJAUFBduVt2hxm1JSrqhmrVq6+557bH8JCQlq1KiRunTpard8cFCwDhw4kLM7BABANqGPBFDUZGnkSpUqVez+X6pUKUnS2bNn5efnp0OHDqlYsWIKCwuzWy4wMFABAQE6dOhQFsOVgoKC5OnpaVe2b98+7dq1S+XKlUuz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xmnWW8u3b9+eazEBOWH79u2aPHmyBg4cqDZt2mQ4MeeePXuUnJyscuXKKTg42KGezwUKkurVqys0NFR79uzRJ598on79+tnqdu3apYULF6pUqVLq2bNnHkYJ5B+pqak6dOiQJCkwMDCPowGyX0pKin755Re5u7urefPm2r59uz777DOdOnVKQUFB6t69u+3HIlCQHThwQJLk4eGh0qVLS8qZ83zrdrLSZzBypRD5559/JCnNA+v6cutJBlAQpaamasiQIQoICNDEiRNvuvzNPhe+vr4KCAjQ2bNnde7cuWyNFchubm5umj9/vgICAtS/f381adJEffr0UYcOHVS/fn0FBwfrp59+sp10AEXd4sWLderUKZUrV0633357XocDZLsDBw7o0qVLKlOmjKZMmaJGjRrpP//5j2bOnKmxY8eqUaNGGjlyZF6HCbhs6tSpkqTOnTvbRvJm93n+/v379c0330iSunXrlukYSa4UItZHUxUvXjzNel9fX0niByQKtPfee0+bN2/Wf//7X6dufbjZ50Lis4GCpVWrVlq7dq2qV6+urVu36n//+5+io6NVrFgxdezYUdWrV8/rEIF84fDhw3rqqackSa+99prTt9UBBcnZs2clSXFxcXrhhRc0bNgw7d+/X2fOnNHs2bPl4+Ojd955Rx988EEeRwpk3bfffqvZs2fLw8NDr7/+uq08O8/zr1y5oqioKCUnJ+uBBx5QkyZNMh0nyRUABcY///yjMWPGKDw8XFFRUXkdDpAnFi9erObNm6ty5crauHGjzp8/r7179yoqKkqTJ09Whw4dlJycnNdhAnkqKSlJ9913n86cOaMePXpo2LBheR0SkCNSU1MlXfth2KVLF33wwQeqXr26ypQpo0GDBum///2vJGn8+PF5GSaQZbt379aAAQNkjNF///vfHLvNbcSIEfrll19UvXp1TZs2LUvrILlSiJQoUUKSdOHChTTrk5KSJEklS5bMtZiA7DR8+HBdvnxZM2bMcLrNzT4XEp8NFBz79u1TZGSkypYtq2+++UbNmzeXr6+vbrnlFn344Ye65557tHXrVs2ZMyevQwXyTEpKinr37q3ff/9drVu31ieffJLXIQE5xnqeI0kDBw50qLdejDp69KhiYmJyKywgWxw9elSdO3fW2bNn9fTTT+vJJ5+0q8+u8/w33nhD06dPV4UKFbRy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888/rwMHDmQ51lOnTmnbtm265ZZbVLduXYd6i8WiVq1a6erVq7a5Ta531113OZRZb7lo2rSpSpUqZVfn7++v0qVL6/jx407Ft2bNGl29elWdO3dWSEjITZcPCQlR586dtXPnTq1fv95WvmPHDm3cuFFNmzZVo0aNbOWrVq2SJPXt21clS5Z0KqYbufoaZjcPD480b7uxvi8ZvWfXvy9r1qxRSkqKOnfuLB8fH4c2gYGBuuWWW7Rjxw5dvHjxpnGdOnVKkhyOCat169ZJkvr06eNQV6ZMmTTjzozSpUtL+r95kQAAyG0kVwAAyCRPT0/17NlT06dP15YtW3T69GlNnz5dpUqV0qlTp/T444/bLd+jRw8FBgZq8eLFOn/+vCRp69at2rp1q1q2bKlbb73Vbvn58+erXLlymjBhgkJDQ1W1alVFRkbqu+++y1Sc1glR9+3bZzch6PV/1glyz5w549A+rUcBW+dySe8xwSVKlNDly5ediu/w4cOSpNDQUKeWl9JOVFn//fDDD7u8/hu5+hpmt8DAQLm5uTmUZ/S+WOuSk5NtZdb9mjVrVrr79ffff8sYo3///femcVknOU4viXXs2DFJSjeJduMktZnl5+cnSelOHA0AQE5jQlsAAFwUEBCgYcOGqVKlSurevbuio6N14cIFFS9eXNK10QaDBg3Sm2++qU8//VRDhgzRRx99JMkxISBJHTp0UExMjL755ht9//33WrNmjT7++GN9/PHH6tWrl5YuXepUXKmpqZKu/SDv1KlThsum9aM3o4lMnZ3kNLt17dpVlStX1pIlSzR16lR5enpq4cKFKlGiRIZPqckqV1/D7Haz193Z98W6Xw0bNlSDBg0yXNbLy+um6/P395cknTt3zqntZzdrcicgICBPtg8AAMkVAACySYcOHSRJV69eVXx8vC25Il279eett97SrFmz1K9fP33yyScOT7a5np+fn/r162d79PCGDRvUu3dvff755/r222/VtWvXm8YTHBws6drTVebNm+fi3mU/69OX9u/f73QbNzc3Pfzww3rllVe0aNEi+fn56ezZsxoyZIjDqImsrP9G+f01zCrrfrVu3Vrvvfeey+uzPjkrvVEuFStW1J49e3To0CHVqVPHod76yOWsOnv2rCTnH5MOAEB247YgAACcZIzJsD4mJkbStduGbnxcrHW+kE2bNmnMmDFKSEhQ//797RIwGbntttv04IMPSpL++usvW7mnp6ck6cqVKw5tgoODVatWLe3cuVN79+51aju5qV27dnJzc9PKlSttt/A4Y8iQIXJ3d9esWbPSvSVIku68805JsrsdK7Ny4jX08PBI8/3KTe3bt5ebm5u++eYbpaSkuLy+W2+9Ve7u7g7zDVm1adNGkrRkyRKHun///Vc//PBDmu0yOr6vt2vXLknXRuIAAJAXSK4AAOCkl19+Wc8++2yaIyGOHj2qRx55RJLUrVs324/C61nnC5kyZYqktBMC//zzj+bNm6cLFy7YlV+6dEnR0dGS/m9EhiRVqlRJktL9Ufvyyy8rNTVVvXr10p9//ulQHxcX5zDRbm6pVKmSHnroIV26dEmRkZGKi4uzqz916pQ2btzo0K5ixYrq1q2b/vjjD61du1b169dX8+bNHZZr3ry52rdvr1OnTmno0KFKSkqyq4+NjdWOHTtuGmd2v4aVKlXSyZMn83R+kKCgIA0aNEixsbHq27evTp486bBMTEyMPv/8c6fW5+vrq0aNGun48eM6evSoQ/3AgQPl5eWlRYsW2SYalqSUlBSNHDnS4b2xutnxbbVp0yZJUnh4uFPxAgCQ3bgtCAAAJ50/f15Tp07VpEmTVKNGDdWpU0fe3t46cuSINm7cqJSUFIWFhemdd95Js711vpDDhw87PNnG6t9//9XAgQM1fPhwNW3aVMHBwUpKStL69et1+vRpNW3aVPfdd59t+XvvvVevvfaannnmGf3444+2ETMTJkxQmTJl1K9fP/39999688031aRJEzVs2FChoaEyxmj//v3avn27SpQokWaiJzdMnTpVe/bsUXR0tEJCQtS2bVv5+fnp0KFD2rp1qx599FG1aNHCod2wYcP0xRdfSLJ/2tKNFixYoDvuuEOLFy/WypUr1bp1a3l5eWn//v36888/NXnyZNWrVy/DGLP7NezWrZvee+89NW7cWLfffru8vb1Vs2ZNPfvss061zy5Tp05VbGysPv/8c33//fdq2LChqlSpoqSkJO3cuVMxMTHq3r27evXq5dT67r77bm3evFlr1qxR//797eqqVaumyZMn6/HHH1enTp3Utm1bBQYGasOGDTp79qz69++vRYsWOayzW7duWrt2re644w61b99evr6+Klu2rN566y3bMufPn9fvv/+uWrVq2Z6MBABArsvbJ0EDAFBwnD592ixYsMAMGDDA1KtXz5QpU8a4u7ub0qVLm1atWpmJEyea8+fPZ7iOAQMGGEnmww8/TLM+MTHRTJ482XTt2tVUrVrVeHt7mzJlypimTZuaKVOmmKSkJIc2ixYtMo0bNzY+Pj5GkpFkDh48aLfM2rVrTe/evU2lSpWMh4eHKVOmjKlfv755/PHHzdq1a+2WjYyMNJJMdHS0w7aio6ONJBMZGZlm/CEhISazpxfJyclm6tSppnnz5qZEiRLGx8fHhIaGmoEDB5otW7ak2ebixYvGw8PD+Pj4mLNnz2a4/sTERPPaa6+Z+vXrGx8fH1OiRAlTq1Yt8/jjj5t9+/Y5vW+ZeQ0zcv78efP444+bypUrG3d3dyPJhIeH2+rDw8PTfA8lmZCQkDTXmdX37MqVK2b+/PmmQ4cOpnTp0sbDw8NUqlTJtGzZ0rz66qtmz549Tu/XP//8Y9zc3EzXrl3TXWbZsmWmRYsWxsfHx5QqVcp0797d7Nq1y4wdO9ZIMnPnzrVbPiUlxYwZM8aEhoYaDw+PNF+Djz/+2EgykydPdjpWAACym8WYm9xADgAAssWFCxcUFBSkK1eu6NixY+k+thY3t3jxYvXr10+RkZGFaqLZgq5nz5765ptvdPjwYQUGBubKNjt16qRffvlF//zzj8qUKZMr2wQA4EbMuQIAQC754IMPFB8fr8jISBIrLkhJSdGECRMkScOHD8/jaHC9119/XampqZo0aVKubG/r1q364YcfNGrUKBIrAIA8xcgVAAByUFxcnJ577jmdPHlS3377rYoXL65du3bZHoUL5y1fvlxffvmlNm3apL///ls9evTQsmXL8jos3GDQoEH63//+p4MHD9oe0ZxTevTood9++0379+9XiRIlcnRbAABkhOQKAAA5KDY2VtWqVZOnp6fq1aunSZMmqV27dnkdVoE0btw4vfrqqypVqpS6dOmi9957T6VLl87rsAAAAEiuAAAAAAAAuII5VwAAAAAAAFxAcgUAAAAAAMAFJFcAAAAAAABcQHIFAAAAAADABSRXAAAAAAAAXEByBQAAAAAAwAUkVwAAAAAAAFxAcgUAAAAAAMAFJFcAAAAAAABc8P8Aycdmu63/SBMAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "schedule = build_schedule(circ, backend, method=\"as_late_as_possible\")\n", - "schedule.filter(channels=[pulse.DriveChannel(0), pulse.DriveChannel(1)]).draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "On the other hand, as the name suggests, ASAP will schedule operations as soon as its resources are free. Thus, the $X$ gate on qubit 1 will be scheduled at `time=0`, in sync with the first $X$ gate on qubit 0." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:58.206881Z", - "iopub.status.busy": "2023-08-25T18:25:58.206314Z", - "iopub.status.idle": "2023-08-25T18:25:58.444404Z", - "shell.execute_reply": "2023-08-25T18:25:58.443666Z" - } - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABFcAAAFdCAYAAADG/YI8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABhu0lEQVR4nO3dd3gU1f7H8c+SnkASeiCBAIk06VWkBFCkqBSRSNOEIhdFUUSxoSBeRbggolIEadK8iKIoKraACFIEBZQaIEgvkSQQIARyfn/w270sm4Qkm57363nyPHDOnJnv7M7umf3OmTMWY4wRAAAAAAAAsqRYXgcAAAAAAABQkJFcAQAAAAAAcALJFQAAAAAAACeQXAEAAAAAAHACyRUAAAAAAAAnkFwBAAAAAABwAskVAAAAAAAAJ5BcAQAAAAAAcALJFQAAAAAAACeQXMkki8Uii8Uif39/xcXFpbrMW2+9JYvForFjx+ZqbAXZ2LFjZbFYNH/+/LwOxUGVKlVksViydZ3Xrl3TsmXL9Oyzz6pNmzby8fGRxWJRZGRkmm3i4uK0ZMkS9enTR1WrVpW7u7tKlCih5s2ba+rUqUpOTnZoExMTYztm0/sbOHCg0/s0f/58h/X6+PioYsWKatu2rZ5//nn99ddfTm8nP2jbtq0sFotiYmLyOpQMy8oxZ7Vx40Z169ZNZcqUkaenp6pXr66XX35ZiYmJDsumpKRo3bp1GjVqlBo3bqwSJUrIw8NDISEhGjp0qA4dOpTl+KdMmaK6devKy8tLZcuWVXh4uHbv3p2l9aUlP38XAQAAIP9yzesACqr4+Hi9/fbbGjduXF6HgjwQExOjqlWrKiwsTGvWrMl0+/Pnz+uhhx7KVJtJkybpjTfekMViUYMGDdS8eXOdOXNG69ev1+bNm7V8+XKtXr1a3t7etjbFixdXREREmuv873//q8uXL6t169aZ3oe0hISEqFWrVpKkK1eu6OzZs/r999+1du1aTZw4Uf369dP06dPl6+ubbdvMbhaLRcHBwQUqeXIrWTnmJGnx4sWKiIjQtWvX1KhRIwUHB2vr1q1688039dVXX2ndunV27+XBgwfVpk0bSVJAQIDat28vFxcXbd68WR988IGWLFmir7/+2naMZERKSop69eqlFStWyN/fX/fee6/Onj2r5cuXa9WqVYqKilKzZs0yvW+ZUaVKFR0+fFjGmBzdDgAAAAomkitZYLFY5OHhoalTp2rEiBEqWbJkXodU4D3xxBPq3bu3KlSokNehOPjxxx9THRXiDDc3Nz388MNq0qSJmjZtqr1792rAgAHptvHx8dGoUaM0bNgwVa5c2Va+f/9+3X333frll1/073//W2+++aatrkyZMmlegd+9e7cWLFggLy8v9ezZM1v2S5JatWrlsE1jjFatWqUnn3xSixcv1tGjR/X999/Lzc0t27abmz766CNdvHhRgYGBeR1KhmXlmDt69KgGDx6sa9euac6cObYRTleuXFFkZKSWLl2q5557Th988IGtjcViUYcOHfTCCy+oXbt2tlFfSUlJGjp0qObPn69+/fopOjo6w+//3LlztWLFCt12221at26dypcvL0n69NNP9eCDD6pfv37avXu3XF3p0gAAAJA3uC0oC4oVK6YhQ4YoISFBkyZNyutwCoUyZcqoZs2a8vPzy+tQHISEhKhmzZrZuk4fHx999NFHGj58uFq0aCFPT89btnnxxRc1YcIEu8SKJN1222166623JElLly7NcAyLFi2SJHXr1i3HR5FYLBbdd9992rRpkypWrKi1a9dqxowZObrNnFS5cmXVrFmzQCWHsnLMzZ8/X5cvX1aHDh3sbh1zd3fX+++/rxIlSmju3LmKjY211YWEhOi7775T+/bt7W6n8/Dw0PTp0+Xn56e///5bGzZsyHDsb7/9tiRp4sSJtsSKJPXs2VNdu3ZVdHS0vvjiiwyvDwAAAMhuJFey6IUXXpCXl5fee+89ux8W6Tlx4oQmTpyosLAwBQYGyt3dXQEBAXrggQe0ZcuWVNvcON/HtGnTVKdOHXl5ealq1aqaOHGibYj6tm3bdP/996tUqVIqXry4unXrpsOHD6e6TmOMli5dqvbt26tkyZLy9PRUrVq1NHbsWF28eDELr0bajhw5ouHDh6t69ery8vJSqVKl1KRJE7322mtKSEiwLZfWPAc3zm2xZMkS3XHHHSpRooT8/f0d9qdDhw4qXbq0PD09VaVKFYWHh+vHH3+0LbdmzZp055iIjIyUxWJxuM3n5jlXxo4dq6pVq0qS1q5dazfHSEbmr8gJ9evXlyQdP348Q8sbY7RkyRJJ0sMPP5xjcd2sXLlytlvp3n33Xbu6tF5/K4vFoipVqtiVWed5GTt2rPbt26fevXurfPny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P6Q7J47V5s2bVbNmTd11110Odc8884wkadSoUXbfG5999plWrlyp0NBQdevWLUP7M2DAAHl4eGjx4sW2ibel63PDjBgxwjaHy82sI5P27t2boe0AAACgaOG2oGw0cuRIvffee6n+SC9XrpwGDRqk2bNnq379+mrfvr28vLy0bt06Xbt2TZGRkTl+q44kvfDCC9qzZ48WLlyoWrVqqWHDhqpataquXLmivXv3ateuXapXr57dyIbk5ORM/6AoVaqUPvnkE3Xt2lUjRozQu+++q6ZNm+rSpUvavXu3oqOj9fvvv2f5arbVSy+9pN9//12ff/65qlevrtatW6tcuXI6cuSItm3bpg4dOtg9jnnMmDEaNGiQevbsqTZt2qh48eLavHmzfH191a1bN7ukS3rc3d3VqVMnffnll6pfv74aNWokd3d3tWzZUgMGDMjQOh5//HHbyAlrMmHVqlW64447bMts3LjR9u+XX35Zv/76q1xcXOTq6qpBgwalut60jqPFixfLGKP7779ffn5+6cZmnePE+jSYzPjll19st49cuXJFsbGxtidVSddHzUybNs1h3e3atVNYWJjWrl2r2rVrq2XLljp79qw2bdqk4cOHa9KkSZmOpUmTJurQoYO+//57Va9e3Xb7ypo1a1SmTJk03/OuXbvqvffeU6NGjXTnnXfK09NTNWrU0HPPPZfmtnx8fLR48WLdf//9+te//qVZs2apevXq2rNnj37//XcVL15cS5cuzZb5g86ePau9e/c6PEXoVjJ7zEnS008/rV27dql+/foqW7asjhw5ol9//VUWi0UffPCB2rVrZ7f8H3/8oZEjR0q6ntB84403Uo1l8ODBatWqle3/Fy9e1N69e3X58mWHZQcOHKivv/5aK1assCVgzp49q7Vr18rLy0uLFi3K8LFatWpVTZ48WU888YQ6duyoNm3aKCAgQBs3btS5c+fUr18/LV682KFd165dtXbtWt11111q166dfHx8VKZMGb311lsZ2i4AAAAKOYNMkWRcXFzSrH/11VeNJCPJjBkzxq7u6tWrZvLkyaZ27drG09PTlC9f3vTr18/ExMSYMWPGGElm3rx5dm2Cg4NNWm9TWm2MMebQoUNGkgkLC0u17RdffGHuvfdeU65cOePm5mbKlStnGjdubEaNGmW2bt2a6rqycrgcPHjQDB061FSpUsW4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saNOHD+uLZs3OxU3AAAAgJzhmpVG4eHhqlq1qsaPH69t27bpww8/VLly5TRhwgTbMoMHD9aCBQv04IMPauTIkdq0aZPGjx+v3bt3a8WKFWmu++WXX1aNGjU0a9YsjRs3TlWrVlVISIitPjY2Vp07d1bv3r3Vv39/lS9fXikpKeratat++eUXDRkyRLVq1dLOnTs1ZcoU7du3T59//rldXIsWLVLfvn1155136qefftK9996blZcBQBH2wAM99eXKLzXl7bfVpk2YNv76q9avX6/nn39B5cuXty137OhR7du3T/0fftiu/Rtv/FufLFuW7jZWff2NAgMDVbt2bfn5+SlqTZSaNW+eI/sDAAAAIOuylFxp2LCh5syZY/t/bGys5syZY0uubN++XQsWLNDgwYM1e/ZsSdLjjz+ucuXKadKkSYqKilK7du1SXXeHDh107NgxzZo1S507d1aTJk3s6k+ePKmZM2fqX//6l61s0aJF+uGHH7R27Vq1atXKVl6nTh0NHTpUGzZs0J133qnt27dr0aJFevzxxzVt2jRJ0rBhw9SvXz/t2LEjKy8FgCLKYrFo9CuvqPdD4XrjjX/r923bVPv22/VQ7952y/2xfbskqVatWnbld999t3bv2qWTJ09qxIhnbOUzZkyXu7uHHh3yqAIDA23lNWvV0vY//si5HQIAAACQZVmac2Xo0KF2/2/durViY2OVkJAgSfr6668lSc8884zdciNHjpQkrVq1KiublXR9/oEBAwbYlX3yySeqVauWatasqbNnz9r+2rdvL0mKioqyi2v48OF27Z9++uksxwOg6AoNDdUjERH6/rvvdO7cOb3yyisOE9bGHDokSQoMDLIrb978DiUnX1WNmjV173332f7i4+PVsGFDde7cxW75oMAgHTx4MGd3CAAAAECWZGnkSuXKle3+X7JkSUnSuXPn5Ovrq8OHD6tYsWIKDQ21Wy4gIED+/v46fPhwFsOVAgMD5e7uble2f/9+7d69W2XLlk21jXWCXGtcN95mJEk1atTIcjwAiraS/te//8qWLavQ0Nsc6uPi4+Tq6ipvb2+78mvXrikm5pDuaHGHrezkyZM6f/68w3enJPn6+ury5cu6dOmSvLy8snkvAAAAADgjS8kVFxeXVMtvfpKFxWLJyurTldqPipSUFNWtW1dvv/12qm0qVaqU7XEAwMmT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N2v06NGKj49Xv3797BIw6bnjjjv08MMPS5L+/PNPW7m7u7sk6erVqw5tgoKCVLNmTe3atUv79u3L0HZyU9u2beXi4qLVq1fbbuHJiMGDB8vV1VWzZ89O85YgSbr77rslye52rMzKidfQzc0t1fcrN7Vr104uLi766quvlJyc7PT6br/9drm6ujrMN2TVunVrSdKyZcsc6v755x999913qbZL7/i+0e7duyVdH4kDAEBeILkCAEAGvfLKK3ruuedSHQlx7Ngx/etf/5Ikde3a1faj8EbW+UKmTJkiKfWEwN9//6358+fr4sWLduWXL19WVFSUpP+NyJCkihUrSlKaP2pfeeUVpaSkqGfPnvrjjz8c6mNjYx0m2s0tFStW1COPPKLLly8rIiJCsbGxdvWnT5/Wpk2bHNpVqFBBXbt21e+//661a9eqXr16atasmcNyzZo1U7t27XT69GkNGTJEiYmJdvUxMTHauXPnLePM7tewYsWKOnXqVJ7ODxIYGKiBAwcqJiZGffr00alTpxyWiY6O1qeffpqh9fn4+Khhw4Y6ceKEjh075lA/YMAAeXh4aPHixbaJhiUpOTlZI0aMcHhvrG51fFtt3rxZkhQWFpaheAEAyG7cFgQAQAZduHBBU6dO1aRJk1S9enXVrl1bnp6eOnr0qDZt2qTk5GSFhobqnXfeSbW9db6QI0eOODzZxuqff/7RgAEDNGzYMDVp0kRBQUFKTEzUhg0bdObMGTVp0kQPPPCAbfn7779f48aN07PPPqvvv//eNmJmwoQJKl26tPr27au//vpLb775pho3bqwGDRooJCRExhgdOHBAO3bsUPHixVNN9OSGqVOnau/evYqKilJwcLDatGkjX19fHT58WNu2bdNjjz2m5s2bO7QbOnSoPvvsM0n2T1u62cKFC3XXXXdp6dKlWr16tVq1aiUPDw8dOHBAf/zxhyZPnqy6deumG2N2v4Zdu3bVe++9p0aNGunOO++Up6enatSooeeeey5D7bPL1KlTFRMTo08//VTffvutGjRooMqVKysxMVG7du1SdHS0unXrpp49e2Zofffee6+2bNmiNWvWqF+/fnZ1VatW1eTJk/XEE0+oY8eOatOmjQICArRx40adO3dO/fr10+LFix3W2bVrV61du1Z33XWX2rVrJx8fH5UpU0ZvvfWWbZkLFy7ot99+U82aNW1PRgIAINfl7ZOgAQAoOM6cOWMWLlxo+vfvb+rWrWtKly5tXF1dTalSpUzLli3NxIkTzYULF9JdR//+/Y0k88EHH6Ran5CQYCZPnmy6dOliqlSpYjw9PU3p0qVNkyZNzJQpU0xiYqJDm8WLF5tGjRoZLy8vI8lIMocOHbJbZu3ataZXr16mYsWKxs3NzZQuXdrUq1fPPPHEE2bt2rV2y0ZERBhJJioqymFbUVFRRpKJiIhINf7g4GCT2dOLpKQkM3XqVNOsWTNTvHhx4+XlZUJCQsyAAQPM1q1bU21z6dIl4+bmZry8vMy5c+fSXX9CQoIZN26cqVevnvHy8jLFixc3NWvWNE888YTZv39/hvctM69hei5cuGCeeOIJU6lSJePq6mokmbCwMFt9WFhYqu+hJBMcHJzqOrP6nl29etUsWLDAtG/f3pQqVcq4ubmZihUrmhYtWpjXXnvN7N27N8P79ffffxsXFxfTpUuXNJdZsWKFad68ufHy8jIlS5Y03bp1M7t37zZjxowxksy8efPslk9OTjajR482ISEhxs3NLdXX4KOPPjKSzOTJkzMcKwAA2c1izC1uIAcAANni4sWLCgwM1NWrV3X8+PE0H1uLW1u6dKn69u2riIiIQjXRbEHXo0cPffXVVzpy5IgCAgJyZZsdO3bUL7/8or///lulS5fOlW0CAHAz5lwBACCXTJs2TXFxcYqIiCCx4oTk5GRNmDBBkjRs2LA8jgY3ev3115WSkqJJkyblyva2bdum7777TiNHjiSxAgDIU4xcAQAgB8XGxur555/XqVOn9PXXX8vb21u7d++2PQoXGbdy5Up9/vnn2rx5s/766y91795dK1asyOuwcJOBAwfqv//9rw4dOmR7RHNO6d69u3799VcdOHBAxYsXz9FtAQCQHpIrAADkoJiYGFWtWlXu7u6qW7euJk2apLZt2+Z1WAXS2LFj9dprr6lkyZLq3Lmz3nvvPZUqVSqvwwIAACC5AgAAAAAA4AzmXAEAAAAAAHACyRUAAAAAAAAnkFwBAAAAAABwAskVAAAAAAAAJ5BcAQAAAAAAcALJFQAAAAAAACeQXAEAAAAAAHACyRUAAAAAAAAnkFwBAAAAAABwwv8BstNmu4iRQaYAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "schedule = build_schedule(circ, backend, method=\"as_soon_as_possible\")\n", - "schedule.filter(channels=[pulse.DriveChannel(0), pulse.DriveChannel(1)]).draw()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "ALAP is the default because it allows qubits to remain idle as long as possible. In this case, the difference between ALAP and ASAP may be negligible, but in ALAP, qubit 0 has _no_ time to decay from the excited state before measurement." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:25:58.448706Z", - "iopub.status.busy": "2023-08-25T18:25:58.448222Z", - "iopub.status.idle": "2023-08-25T18:25:58.548097Z", - "shell.execute_reply": "2023-08-25T18:25:58.547431Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:25:58 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "1c267ace46b44e488cfa77b105eb246c": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "79bcf42e20984e78a4a4d9b04cc8afca": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_1c267ace46b44e488cfa77b105eb246c", - "placeholder": "​", - "style": "IPY_MODEL_c2829c01ec9943729b7d01247c3651ca", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "c2829c01ec9943729b7d01247c3651ca": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/tutorials/circuits_advanced/08_gathering_system_information.ipynb b/docs/tutorials/circuits_advanced/08_gathering_system_information.ipynb deleted file mode 100644 index f52d09462181..000000000000 --- a/docs/tutorials/circuits_advanced/08_gathering_system_information.ipynb +++ /dev/null @@ -1,911 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Obtaining information about your `backend`\n", - "\n", - "#### _Note: All the attributes of the backend are described in detail in the [Qiskit Backend Specifications](https://arxiv.org/pdf/1809.03452.pdf). This page reviews a subset of the spec._\n", - "\n", - "Programming a quantum computer at the microwave pulse level requires more information about the device than is required at the circuit level. A quantum circuit is built for an abstract quantum computer -- it will yield the same quantum state on any quantum computer (except for varying performance levels). A pulse schedule, on the other hand, is so specific to the device, that running one program on two different backends is not expected to have the same result, even on perfectly noiseless systems.\n", - "\n", - "As a basic example, imagine a drive pulse `q0_X180` calibrated on qubit 0 to enact an $X180$ pulse, which flips the state of qubit 0. If we use the samples from that pulse on qubit 1 on the same device, or qubit 0 on another device, we do not know what the resulting state will be -- but we can be pretty sure it won't be an $X180$ operation. The qubits are each unique, with various drive coupling strengths. If we have specified a frequency for the drive pulse, it's very probable that pulse would have little effect on another qubit, which has its own resonant frequency.\n", - "\n", - "With that, we have motivated why information from the backend may be very useful at times for building Pulse schedules. The information included in a `backend` is broken into three main parts:\n", - "\n", - " - [**Configuration**](#Configuration): static backend features\n", - " - [**Properties**](#Properties): measured and reported backend characteristics\n", - " - [**Defaults**](#Defaults): default settings for the OpenPulse-enabled backend\n", - " \n", - "which are each covered in the following sections. While all three of these contain interesting data for Pulse users, the defaults are _only_ provided for backends enabled with OpenPulse.\n", - "\n", - "The first thing you'll need to do is grab a backend to inspect. Here we use a mocked backend that contains a snapshot of data from the real OpenPulse-enabled backend." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.182928Z", - "iopub.status.busy": "2023-08-25T18:26:00.180031Z", - "iopub.status.idle": "2023-08-25T18:26:00.800954Z", - "shell.execute_reply": "2023-08-25T18:26:00.800060Z" - } - }, - "outputs": [], - "source": [ - "from qiskit.providers.fake_provider import FakeHanoi\n", - "\n", - "backend = FakeHanoi()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration\n", - "\n", - "The configuration is where you'll find data about the static setup of the device, such as its name, version, the number of qubits, and the types of features it supports.\n", - "\n", - "Let's build a description of our backend using information from the `backend`'s config." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.805066Z", - "iopub.status.busy": "2023-08-25T18:26:00.804526Z", - "iopub.status.idle": "2023-08-25T18:26:00.812013Z", - "shell.execute_reply": "2023-08-25T18:26:00.811250Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "This backend is called fake_hanoi, and is on version 1.0.18. It has 27 qubits. It supports OpenPulse programs. The basis gates supported on this device are ['id', 'rz', 'sx', 'x', 'cx', 'reset'].\n" - ] - } - ], - "source": [ - "config = backend.configuration()\n", - "\n", - "# Basic Features\n", - "print(\"This backend is called {0}, and is on version {1}. It has {2} qubit{3}. It \"\n", - " \"{4} OpenPulse programs. The basis gates supported on this device are {5}.\"\n", - " \"\".format(config.backend_name,\n", - " config.backend_version,\n", - " config.n_qubits,\n", - " '' if config.n_qubits == 1 else 's',\n", - " 'supports' if config.open_pulse else 'does not support',\n", - " config.basis_gates))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Neat! All of the above configuration is available for any backend, whether enabled with OpenPulse or not, although it is not an exhaustive list. There are additional attributes available on Pulse backends. Let's go into a bit more detail with those.\n", - "\n", - "The **timescale**, `dt`, is backend dependent. Think of this as the inverse sampling rate of the control rack's arbitrary waveform generators. Each sample point and duration in a Pulse `Schedule` is given in units of this timescale." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.815230Z", - "iopub.status.busy": "2023-08-25T18:26:00.814771Z", - "iopub.status.idle": "2023-08-25T18:26:00.824414Z", - "shell.execute_reply": "2023-08-25T18:26:00.823660Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "2.2222222222222221e-10" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "config.dt # units of seconds" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The configuration also provides information that is useful for building measurements. Pulse supports three measurement levels: `0: RAW`, `1: KERNELED`, and `2: DISCRIMINATED`. The `meas_levels` attribute tells us which of those are supported by this backend. To learn how to execute programs with these different levels, see this page -- COMING SOON." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.827893Z", - "iopub.status.busy": "2023-08-25T18:26:00.827440Z", - "iopub.status.idle": "2023-08-25T18:26:00.834149Z", - "shell.execute_reply": "2023-08-25T18:26:00.833444Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[1, 2]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "config.meas_levels" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For backends which support measurement level 0, the sampling rate of the control rack's analog-to-digital converters (ADCs) also becomes relevant. The configuration also has this info, where `dtm` is the time per sample returned:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.837568Z", - "iopub.status.busy": "2023-08-25T18:26:00.837118Z", - "iopub.status.idle": "2023-08-25T18:26:00.843699Z", - "shell.execute_reply": "2023-08-25T18:26:00.842924Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "2.2222222222222221e-10" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "config.dtm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The measurement map, explained in detail on [this page COMING SOON], is also found here." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.847113Z", - "iopub.status.busy": "2023-08-25T18:26:00.846674Z", - "iopub.status.idle": "2023-08-25T18:26:00.854021Z", - "shell.execute_reply": "2023-08-25T18:26:00.853295Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[[0,\n", - " 1,\n", - " 2,\n", - " 3,\n", - " 4,\n", - " 5,\n", - " 6,\n", - " 7,\n", - " 8,\n", - " 9,\n", - " 10,\n", - " 11,\n", - " 12,\n", - " 13,\n", - " 14,\n", - " 15,\n", - " 16,\n", - " 17,\n", - " 18,\n", - " 19,\n", - " 20,\n", - " 21,\n", - " 22,\n", - " 23,\n", - " 24,\n", - " 25,\n", - " 26]]" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "config.meas_map" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The configuration also supplies convenient methods for getting channels for your schedule programs. For instance:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.857432Z", - "iopub.status.busy": "2023-08-25T18:26:00.856980Z", - "iopub.status.idle": "2023-08-25T18:26:00.863645Z", - "shell.execute_reply": "2023-08-25T18:26:00.862911Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "DriveChannel(0)" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "config.drive(0)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.866911Z", - "iopub.status.busy": "2023-08-25T18:26:00.866449Z", - "iopub.status.idle": "2023-08-25T18:26:00.873733Z", - "shell.execute_reply": "2023-08-25T18:26:00.872896Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "MeasureChannel(0)" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "config.measure(0)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.877227Z", - "iopub.status.busy": "2023-08-25T18:26:00.876773Z", - "iopub.status.idle": "2023-08-25T18:26:00.882389Z", - "shell.execute_reply": "2023-08-25T18:26:00.881841Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "AcquireChannel(0)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "config.acquire(0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is a matter of style and personal preference whether you use `config.drive(0)` or `DriveChannel(0)`.\n", - "\n", - "## Properties\n", - "\n", - "The `backend` properties contain data that was measured and optionally reported by the provider. Let's see what kind of information is reported for qubit 0." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.885836Z", - "iopub.status.busy": "2023-08-25T18:26:00.885297Z", - "iopub.status.idle": "2023-08-25T18:26:00.964666Z", - "shell.execute_reply": "2023-08-25T18:26:00.963753Z" - } - }, - "outputs": [], - "source": [ - "props = backend.properties()" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.968875Z", - "iopub.status.busy": "2023-08-25T18:26:00.968367Z", - "iopub.status.idle": "2023-08-25T18:26:00.976375Z", - "shell.execute_reply": "2023-08-25T18:26:00.975620Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Qubit 0 has a \n", - " - T1 time of 162.29562357444243 microseconds\n", - " - T2 time of 171.74648699183206 microseconds\n", - " - U2 gate error of 0.00013790682762652163\n", - " - U2 gate duration of 21.333333333333332 nanoseconds\n", - " - resonant frequency of 5.035257503599211 GHz\n" - ] - } - ], - "source": [ - "def describe_qubit(qubit, properties):\n", - " \"\"\"Print a string describing some of reported properties of the given qubit.\"\"\"\n", - "\n", - " # Conversion factors from standard SI units\n", - " us = 1e6\n", - " ns = 1e9\n", - " GHz = 1e-9\n", - "\n", - " print(\"Qubit {0} has a \\n\"\n", - " \" - T1 time of {1} microseconds\\n\"\n", - " \" - T2 time of {2} microseconds\\n\"\n", - " \" - U2 gate error of {3}\\n\"\n", - " \" - U2 gate duration of {4} nanoseconds\\n\"\n", - " \" - resonant frequency of {5} GHz\".format(\n", - " qubit,\n", - " properties.t1(qubit) * us,\n", - " properties.t2(qubit) * us,\n", - " properties.gate_error('sx', qubit),\n", - " properties.gate_length('sx', qubit) * ns,\n", - " properties.frequency(qubit) * GHz))\n", - "\n", - "describe_qubit(0, props)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Properties are not guaranteed to be reported, but backends without Pulse access typically also provide this data.\n", - "\n", - "## Defaults\n", - "\n", - "Unlike the other two sections, `PulseDefaults` are only available for Pulse-enabled backends. It contains the default program settings run on the device." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:00.980008Z", - "iopub.status.busy": "2023-08-25T18:26:00.979601Z", - "iopub.status.idle": "2023-08-25T18:26:01.018075Z", - "shell.execute_reply": "2023-08-25T18:26:01.017180Z" - } - }, - "outputs": [], - "source": [ - "defaults = backend.defaults()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Drive frequencies\n", - "\n", - "Defaults contains the default frequency settings for the drive and measurement signal channels:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.022748Z", - "iopub.status.busy": "2023-08-25T18:26:01.022143Z", - "iopub.status.idle": "2023-08-25T18:26:01.029240Z", - "shell.execute_reply": "2023-08-25T18:26:01.028455Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DriveChannel(0) defaults to a modulation frequency of 5.035257503599211 GHz.\n", - "MeasureChannel(0) defaults to a modulation frequency of 7.1653715820000015 GHz.\n" - ] - } - ], - "source": [ - "q0_freq = defaults.qubit_freq_est[0] # Hz\n", - "q0_meas_freq = defaults.meas_freq_est[0] # Hz\n", - "\n", - "GHz = 1e-9\n", - "print(\"DriveChannel(0) defaults to a modulation frequency of {} GHz.\".format(q0_freq * GHz))\n", - "print(\"MeasureChannel(0) defaults to a modulation frequency of {} GHz.\".format(q0_meas_freq * GHz))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Pulse Schedule definitions for QuantumCircuit instructions\n", - "\n", - "Finally, one of the most important aspects of the `backend` for `Schedule` building is the `InstructionScheduleMap`. This is a basic mapping from a circuit operation's name and qubit to the default pulse-level implementation of that instruction. " - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.033179Z", - "iopub.status.busy": "2023-08-25T18:26:01.032721Z", - "iopub.status.idle": "2023-08-25T18:26:01.038754Z", - "shell.execute_reply": "2023-08-25T18:26:01.038005Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "calibrations = defaults.instruction_schedule_map\n", - "print(calibrations)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Rather than build a measurement schedule from scratch, let's see what was calibrated by the backend to measure the qubits on this device:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:01.042291Z", - "iopub.status.busy": "2023-08-25T18:26:01.041906Z", - "iopub.status.idle": "2023-08-25T18:26:03.194422Z", - "shell.execute_reply": "2023-08-25T18:26:03.193769Z" - }, - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABGMAAA97CAYAAAAmLz0IAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3xV9f3H8ffNTkgChBEgTNkqQ6YgIKJsEBmyEUXraBERKFCtVq39uVoqImpddTAdIChLsJWKgrhAtOBiQ0L2nvee7+8Pmishe92T8Xo+HnkoZ37uzc09977PdziMMUYAAAAAAADwCC+7CwAAAAAAAKhNCGMAAAAAAAA8iDAGAAAAAADAgwhjAAAAAAAAPIgwBgAAAAAAwIMIYwAAAAAAADyIMAYAAAAAAMCDCGMAAAAAAAA8iDAGAAAAAADAgwhjKpnD4ZDD4VC9evWUmJhY4DaPP/64HA6HHnroIY/WBpRG69at3a9nh8MhHx8fhYWFqVOnTpo+fbpef/11ZWZm2l1muX388cdyOBy6+eab7S6lxI4fP57nd1PYz5w5c/Ls99prrxW5/dSpU/Od6+abby7RuU6ePFmi2r/99lvNnTtXV155pZo1ayZ/f3/VrVtX/fr104oVK5STk1Mhz5GdCnueg4KCdOmll2rx4sWKj4+3pTaHw6HWrVvbcu6yyH2tDx48uMKOuXPnTg0YMEAhISHu301ZDR48WA6HQ8ePH6+w+sqiMp4nO1XH92UAAIrjY3cBtUVSUpKWLVumRx55xO5SgHKZOHGigoODZYxRcnKyjh07prfeektr167VkiVL9M9//lMjR460u8xCPfTQQ3r44Yf1z3/+s8Z8sA8ODtbs2bMLXb9+/XplZmZq4MCBBa7v1q2bunfvnm9537598y0bMGBAoef54YcftG/fPrVq1UotWrQovnBJ//nPf7Ry5Uq1atVKl156qRo1aqSYmBh9+umn2rdvn9599119+OGH8vPzK9HxqrK2bdu6nz9jjCIjI7Vv3z499dRTeuutt7Rv3z41adLE5iprl5MnT2r8+PHKzs7Wddddp8aNG9tdEgAAqCUIYzzA4XDI399fy5cv17333qv69evbXRJQZn/961/z3UmPiorSo48+qpUrV2rMmDHasmWLRowYYU+B5dSnTx8dPnxYdevWtbuUEmvYsKFee+21AtcdPnxYr7/+ugIDAzVx4sQCt7nhhhtK3DLvtttu02233VbguilTpmjfvn2aOXNmiVsXjBo1SqNGjdIll1ySZ/m5c+d03XXXaffu3XrxxRc1d+7cEh2vKhswYEC+31NMTIwGDx6s//73v/q///s/PfPMM/YUV0vt2rVLaWlpeuCBB7hZUoVVx/dlAACKQzclD/Dy8tLtt9+u5ORk/fWvf7W7HKDCNWnSRM8++6z+/Oc/y7Is3XLLLcrKyrK7rDIJCgpSp06d1LRpU7tLqRCrVq2SJI0bN06hoaGVdp7k5GS9//77kqRZs2aVeL9LLrkkXxAjSeHh4VqyZIkk6V//+lfFFFkFNWrUSAsXLpR0vpUQPOv06dOSVOBrEFVHTXtfBgBAIozxmKVLlyowMFArVqxQXFxcifaJjIzUk08+qauvvloRERHy8/NTkyZNNGHCBH3xxRcF7pM7rockrVy5UpdffrkCAwPVpk0bPfnkkzLGSJK+/vprjR07VmFhYQoODta4ceN04sSJAo9pjNHatWs1ZMgQ1a9fXwEBAercubMeeughpaenl+HZyO/C/u1paWlasGCBWrRoocDAQPXo0cP9JU+S3n77bfXt21d16tRReHi45s2bp4yMjAKPm56erscee0xXXHGFgoODFRwcrCuvvFKvv/56gdt/8sknmjt3rrp27ar69esrMDBQnTp10tKlSwsd8+ezzz7TDTfcoFatWsnf319NmjRRnz59tHTpUqWmprq3e+ihh+RwOAptwXDh7y7Xhf3ko6KidNttt6l58+by8fHR008/7d7u1KlTmjt3rtq2bauAgACFhYVpzJgx+uyzzwo8V2X5wx/+oFatWikqKkpvv/22e3lx4xcU9tzkPifGGK1YsULdunVTUFCQu0tN7mtz6tSp6tChg+rUqaOQkBD16dNHzz33nCzLyne8hx9+WJJ0yy235Bm/4+OPP5ZU9NgETqdTK1asUM+ePd2vpz59+uj555+Xy+XKt/2F40e89957uvLKK1WnTh2FhYVp2rRp7i+ClcUYozVr1kgqXUBSFu+++64yMjLUu3dvdezYsUKO6evrK0ml6qJUluc8Oztbzz33nHr37q0GDRooKChIrVu31pgxY7Ru3boKeSxFye0a43Q68ywv7ev7Qtu3b9f111+v8PBw+fv7q0WLFhozZozefffdEtX03XffKSIiQr6+vlq9erV7udPp1PPPP69+/fopNDRUgYGB6t69u55++ul89Ut539defvllde3aVYGBgWrSpInuuOOOQt9XT506pVmzZqlRo0YKCgpSz5493cFiRcj9O//Tn/4kKe/7QW4rscTERK1YsULDhw93v783aNBAI0aM0M6dO0t1vqSkJA0aNEgOh0N33323+1osnf9djR49Wo0aNZK/v78uueQSLViwoMSfFYqTkZGhpUuXuh9Du3bt9MQTT+SpIVdpr4EXvl/Gx8frrrvuUtOmTeXv76/LL79cr776aqF17d27V+PGjXM/7tatW+u3v/2tzp49W+R5AACoMQwqlSTj7e1tjDHm3nvvNZLMkiVL8mzz2GOPGUnmT3/6U57lzz//vJFkOnbsaEaMGGEmT55srrjiCiPJ+Pr6mh07duQ7X6tWrYwkM3/+fBMYGGhGjRplxowZY0JCQowk8+CDD5o9e/aYoKAg06NHDzN58mTTrl07I8m0bdvWpKen5zmey+Uy06ZNM5JMcHCwGTx4sBk/frxp0aKFkWT69OmTb59jx44ZSaY0L6/cffr162f69u1rGjdubCZNmmQGDx5svLy8jLe3t9m5c6dZtmyZ8fHxMddee60ZP368adCggZFkpk+fnu+Y586dM127djWSTJMmTcyoUaPMyJEjTd26dY0kM3fu3Hz79O3b1wQEBJg+ffqYiRMnmtGjR5umTZsaSeayyy4zKSkpebbfvHmz8fLyMg6Hw/Tt29dMnTrVjBgxwrRt29ZIMseOHXNv+6c//clIMv/85z8LfA5yf3cX+ve//20kmVGjRpnmzZubJk2amEmTJpkxY8aYf/zjH8YYYz777DNTv35992tlwoQJZuDAgcbHx8d4e3ubdevWlfj3UJTc+i58TAVZtGiRkWTmzJnjXpb7+7366qsL3Kew5yb3nLfffrvx9fU11113nZkyZYoZP368McaYjIwMI8k0aNDADBw40EyZMsVcd911JigoyEgys2fPznO8hQsXmm7duhlJ5qqrrjKzZ892/xw+fNgY8+tzfvG+TqfTjBo1ykgyoaGh5oYbbjDjxo1z/22NHz/euFyuPPtcffXVRpL5/e9/b7y9vc3gwYPNpEmT3H8/7du3z/f3Y4xx//0U91wX5z//+Y+RZBo3bmxycnLyrf/nP/9pJJkxY8aYRYsWmdtvv908+OCD5uOP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JG9vHwXWDVVg3boKCq0r31J0WTZpWbLOJcu7ad3KKhcAAFQxtS6MmTRpkubOnavVq1fnCWOaNm2qa6+9VjExMQXul5qaKkmFtpypU+d88+KUlJRKqNqzTJazRDMo5WRmKjM1RelJycpITpYzu2K6NAEAUN24XE6lxscr9X8zK/r6+ysw9HyrmcDQ0GLHnHEei5dXw2A5fMvf9QkAAFR9tS6MqVevnkaPHq1NmzYpKipKp06d0g8//KB7771X3hXQ9/tChw4dcoc01YYx8j2RLK/EvDMoWS6XcjIzlJORqZz0dGWnp8mVw5gvAACUhLevn/zqBMk3MEh+gYHyCQjIN+aMK+aUnK1CbaoQAIDaKS0tzZbz1rowRjrfHWnDhg1at26djh075l5WlNzxZdLTC56CMvcXGBIS4l42csTwiijXo0IdAeoW0FRekryNkff//ltr5kAHAMBDLEkuh0Ou//3XkvR1xhmlKbuYPQEAQHVXK8OYUaNGqV69enrjjTd09uxZde7cudCBeXO1bNlSknT69OkC1+cub9WqlXvZtu07ql/LGAAAAAAAaom0tDRbGlLUyjDG399fN954o1566SVJ0rx584rdp1u3bpKkr7/+usD1ucu7du3qXtalSxeFhtLcGAAAAACAqig5OdmW89ba3iezZs1SgwYN1LBhQ82YMaPY7UeMGCEvLy998sknio6OzrMuKytL77//vry9vTVq1KjKKhkAAAAAANQAtTaMGThwoGJjYxUTE5Ona1FhmjZtqmnTpik7O1u//e1v5XQ63esWL16smJgYzZw5U40bN67MsgEAAAAAQDVXK7spldXTTz+tffv26d1331WnTp3Uq1cvff/99/ruu+/Uvn17LVu2zO4SAQAAAABAFVdrW8aURcOGDbV//37dfffdys7O1saNG5WUlKR58+Zp//79CgsLs7tEAAAAAABQxTmMMcbuImqa5ORk1a1bV6fPnGUAXwAAAAAAqqjk5GQ1j2impKQkj35/p2UMAAAAAACABxHGAAAAAAAAeBBhDAAAAAAAgAcRxgAAAAAAAHgQYQwAAAAAAIAHEcYAAAAAAAB4EGEMAAAAAACABxHGAAAAAAAAeBBhDAAAAAAAgAcRxgAAAACoFb744gt179ZVX3zxhd2lAKjlShXG3HzzzXI4HIX+nDlzpsj9f/jhB917773q37+/AgIC5HA4dPz48Xzbffzxx0We5y9/+UuJ6o2OjtbSpUvVpUsXBQcHKyAgQO3atdMtt9yiPXv25Nn2tddek8Ph0JdfflngsQYPHqzLL7+8ROcFgKJs2rRJ3bt1VfduXfXN11/nW2+M0fBhQ9W9W1fdPXeuR2vLzs7W03//u4Zed6369umtmTOma+/evR6tAQCAC6+V3bt1VZ/evTT0umt11513as3q1UpLS7O7RAAoF5/SbHzHHXfouuuuy7PMGKM777xTrVu3VkRERJH77927V88884wuvfRSde7cWQcOHChwu86dO+vNN9/Mt/zNN9/Uhx9+qGHDhhVb6/79+zV69GilpKRo6tSpuvPOO+Xv769jx47pvffe02uvvabdu3dr0KBBxR4LACqDv7+/tm7bqit69Miz/Msvv9S5c+fk5+fn8ZoefOCP2rVrl6bPmKGWLVtp8+ZNunvu7/TSSy/nqxMAgMr229/+ThEREcpxOhUXF6svv/hSTz31pN5c9aaWL39GHTp0sLtEACiTUoUx/fr1U79+/fIs27Nnj9LT0zVjxoxi97/++uuVmJiokJAQ/fWvfy00jAkPD9fMmTPzLX/44YfVvn179e7du8jzJCQk6IYbbpCPj48OHDigTp065Vn/6KOPat26dQoMDCy2ZgCoLAMGDNCunTu1ZMlS+fj8+na8bdtWXXrppUpITPRoPYcOHdL27dt174IFmj37ZknS2LFjNWniBP396b/rjTfyh+QAAFSmqwYM0GWXXeb+96233qb9n3+uefPu1j33zNPGje8pICDAxgoBoGzKPWbMmjVr5HA4NH369GK3DQsLU0hISJnOs3//fv38888lCn1eeOEFRUZG6umnn84XxEiSw+HQtGnTig11ilJUl63HHnuszMcFUHuMGDlSiYmJ2rfv125AOTk52rVzp0aOHFXq49166xxNnDBeP/74o26dc4uu7NtHY8eM1s6dH0o63+Jm5ozp6tunt8ZdP1b79u3Ls/+uXTvl7e2tiRMnuZf5+/vrhvHj9e3Bg4qKiirjIwUAoOL06dtXv7n9dkWePastWz5wLz927JgWLVygQQMHqE/vXpo+bao+/vjfxR7v66+/0qJFCzVi+DD17tVTw4cN1VNPPanMzEz3Nu+99566d+uqI4cP59v/5ZdfUo8ruuvcuXMV8wAB1ArlCmNycnL01ltv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X37RiBEj1LhxY23durXIVi7e3t55Ao5du3ZJKnpcF+n8gF779u3Txo0by9yFqiQ++eQTLVq0SPPnzy/XuDkAarch116rRx/9s7799ls98eRTdpcDAECV98EH52dR6t+vv5pHNJck+fj46MorryzVcX766SedOHFCf370UY0d++v3mtyBgS82Zuz1euONN7R79259umeP6tevr/79+5fxUQCozco0ZkxMTIx27dql8ePHu2c4utjJkyfz9LGUzs+cNGzYMHl5eWnHjh1q1KhRqc75xBNPqGvXrsWGMXfddZfCw8N177336scff8y3viLuBEdGRmry5MkaMGCAnnqKL08Ayi4oKEj33X+/7rzrLl199dV2lwMAQJW2//PP9dKLLyoiIkKjRo9WWIMG6tWrt9595x3FxMTk2z4+Pr7QY3l7n/86dOH3A2OM1qxeXeD2HTp0UIcOHbRxwwZ99NEujRgxssBW/gBQnDK9c6xfv15Op7PI1iA33XSTdu/eneeNbcSIETp69KgWL16sPXv2aM+ePe514eHhecZxufrqq9WvXz+1a9dOUVFRevHFF5WamqoPPvig0Gb4ucLCwrRx40aNHTtW3bp109SpU9W7d2/5+vrq1KlTevvttyVJLQtprlgS8+bNU0xMjBYvXqx169blWXfJJZeU+bgAaqfrr68aMxqtW7tWKSkpio6JliT9Z/fHiv7foIdTp01zjzUDAIAnfLpnj44fOyany6X4uDjt379f+/btVdOmTbV8+TPy9/eXJP3hvvt0y82zdeOkiZowYaIimkcoPi5eB789qOhz5/TW2+8UePzWrduoRYsW+vuyZYqOjlZwnWDt2rVLySmFj6k2ZsxYLVv2N0nSqNGjK/5BA6gVyhTGrF69Wo0bNy71qOEHDx6UJD355JP51l199dV5wpiePXvq7bff1pkzZxQaGqqhQ4fqz3/+c4mDjn79+um7777TsmXLtGXLFq1fv16WZSkiIkIDBgzQiy++qIEDB5aq/gvFxMTI5XJpwYIF+dYtXbq0zMcFADu9/sbrivzf+FyS9NFHH+mjjz6SdP4DJ2EMAMCTnntupSTJ19dXdevWVbt27fX73y/WuBtuyDNobtu2bbVm7Tq98MLz2rx5kxITExUWFqZOnTrp9jvuKPT4vr6+Wv7MCj3xxON69ZVX5O/vr2uGDNHUqdM0+cZJ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JZfo1ltv1caNG0u1/fTTT2UYhj788MMK7z0kJKRG7w8A5Vm/fr169uiunj2669/795c5b5qmRgwfpp49uuuhBx+0oMJz3nzzDfXs0V2/mDjBshoAAN7pwmdlzx7d1bdPbw27cajunz1bK+LjlZOTY3WJAFAnPjVpPGvWLN14442ljpmmqdmzZys2NlYxMTGVXn/fffepTZs2mj59utq3b6+DBw/qz3/+szZt2qT9+/crMDBQkrRr1y49/vjjGj16tJ544gn5+Pjob3/7m6ZMmaJvv/1WTz75ZJW1/vjjjxoxYoROnDihCRMm6I477lBISIhOnTqlTZs2aezYsXr33Xd1++231+Q/AgBwG39/f23avElX9+pV6vi+ffuUmJgoPz8/iyqTEhPP6u233ir5uwwAgBUeeOB/FBMTo6LiYqWmpmjf3n1auvRZvbf8PS1b9qK6dOlidYkAUCs1CmMGDBigAQMGlDq2c+dO5ebmatq0aVVe/+GHH2rIkCGljl1zzTW68847FR8fr3vuuUeSdOWVV+qHH37QJZdcUtLugQce0I033qhnnnlGCxcuVHBwcIXvU1xcrAkTJigxMVE7duzQtddeW+r8b37zG3388cdyOp1V1gwA9WXgwIHavm2bFi1aLB+fn/8cb968SVdccYXSLxo16EnP/+l5de/eXU6XSxnp6ZbVAQDwbtcOHKgrr7yy5N93332P9nz5pebMeUhz587R2rXrFBAQYGGFAFA7dV4zZsWKFTIMQ7fddluVbS8OYiRpwoRzw9+/++67kmMdOnQoFcRIkmEYuvnmm1VQUKCjR49W+j4ffPCBvvnmG/3qV78qE8ScN3z4cI0aNarKmivy29/+tsLpWvfff3+t+wXgPUaOGqWMjAzt3r2r5FhRUZG2b9umUaNG17i/u+++S7+YOEHff/+97r5rpvr366txY8do27aPJZ0bcTN92m3q17ePbho/Trt37y63n6++2qft27fp0YULa3djAADUo779+une++5Twpkz2rjx7yXHjx07pgWPzNfgQQPVt09v3TZ1ij799J9V9rd//1dasOARjRwxXH16X6MRw4dp6dJnlZ+fX9Jm3bp16tmjuw5d8J3lvLfeelO9ru6pxMRE99wgAK9QpzCmqKhI77//vuLi4hQbG1urPs6ePStJatGihdvafvTRR5Kk6dOn17ierKwspaSklHkVFBSUajdx4kS99957pV7z5s2r9r0AQJs2Mereo4e2bN5ccmznzp3Kzs7WiJEja9Wnw5GlOQ89qKu6ddO8eQ/L19dPixct0tYtW7R40UINHDRIc+bOVV5enhYseKTMnHun06lnnn5aEyZM1KWXMvQbANAwjR07VtK55Q2kc0sU3H77dB09ekwz77pL8x9ZoMDAQD08b57+8cknlfa17eNtys/P1y2TJ2vR4l8qLi5Oq1au1BNPPF7SZtiwYQoICNCmTRvLXL9p4yb17t1brVq1cuMdAmjqajRN6WJbt25VampqtaYoVeSZZ56R3W7XpEmTKm2Xlpamt956S4MGDVJ0dHSlbQ8dOqRmzZqVWcMmJydHeXl5Jf/28/NTWFhYqTZ33XVXhf1eODWqe/fu6t69e8m/U1JS9Pjjj6tbt25avHixXnzxxUprBABJGjVqtF56cZny8/NLPuRdc801atmyZa36S05O0h+ffrpkZE3/Af1180036Ze/XKy//vVddfvv360OHTrqgftna/v27brppptKrv/ggw90JiFBr73+Rt1vDgCAetKqVWuFhIbqp1OnJElLn31G0a1bK37FypI112699VbNmHGnXnjhBd0wdGiFfc2dN6/UVKdJkyapXbv2eumlF5WQkKDo6GgFBwdryPXXa/PmLZr38HzZbOd+0z703Xc6evSI7pwxo/5uFkCTVKeRMStWrJCvr68mT55c6+vffvttPfLII7r00ksrbOdyuTRt2jRlZGTopZdeqrJfh8NR7s5Hjz/+uKKiokpe5U2t+vWvf61t27aVeQ0fPrzC93M6nZo6daqysrK0du3aStezAYALDR8+XAUFBfrXv/6lnJwcffavf2nU6JpPUTovKChII0f+PAUzNraDQkND1aFDh5IgRpK6desmSTr9008lxzIyMvTqKy/rvnvvU2RkZK1rAADAE4ICA5WTm6vMzEzt2bNHw4ePUE5OjtLT05Wenq6MjAzFDYjTyZMnKp1CdGEQk5ebq/T0dPXo2UOmaerQoUMl58aNG6fk5CTt3bun5NimTRsVEBBQZpMTAKhKrUfGZGdna/369RoxYoSaN29e4+s/++wz3X333RoxYoR+//vfV9r2oYce0pYtW/Tuu++qR48eVfYdGhqq1NTUMscfeOCBkiGNFU1h6tatW7l/TJcvX17h+z3xxBP6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+GP5+JwrMywsTH/4wx80d+5cde3atcJr09PTdfPNN8vHx0cHDhwo03bJkiVatWqVAgMDq6wZAOrLwIEDtX3bNi1atLjk75wkbd68SVdccYXSMzI8Wk+PHj3Uo0ePUsf+vX+/8vPzNXrMaI/WAgCAJF07cKCuvPLKkn/fffc92vPll5oz5yHNnTtHa9euU0BAgIUVAkDt1HnNmBUrVsgwDN12221Vto2MjFRoaGiV7b799lt9++23uu+++0p9QXnggQdkmqY+/PDDSq9/7bXXlJCQoBdeeKHc0MYwDE2dOlV9+vSpspaKVDZl649//GOt+wXgPUaOGqWMjAzt3r2r5FhRUZG2b9umUaNqHn7cffdd+sXECfr+++91910z1b9fX40bO0bbtn0s6dyIm+nTblO/vn100/hx2r17d5V9btq8SYZh1KoeAADqQ99+/XTvffcp4cwZbdz495Ljx44d04JH5mvwoIHq26e3bps6RZ9++s8q+9u//ystWPCIRo4Yrj69r9GI4cO0dOmzys/PL2mzbt069ezRXYe++67M9W+99aZ6Xd2T6bwAaqROYUxRUZHef/99xcXFKTY21k0lSf/+978lSb179y51vE2bNmrbtm3J+Yp89NFHCgwM1MSJE2v83pmZmUpJSSnzKioqKtVu1qxZeu+990q9zo8OatGiRY3fF4D3adMmRt179NCWzZtLju3cuVPZ2dkaMXJkrfp0OLI056EHdVW3bpo372H5+vpp8aJF2rplixYvWqiBgwZpzty5ysvL04IFj1Q6576oqEjbPv5YPXr0rHIaKgAAnjR27FhJ55ZBkKQff/xRt98+XUePHtPMu+7S/EcWKDAwUA/Pm6d/fPJJpX1t+3ib8vPzdcvkyVq0+JeKi4vTqpUr9cQTj5e0GTZsmAICArRp08Yy12/auEm9e/dmOi+AGqnRNKWLbd26VampqdWaolQTCQkJkqTo6Ogy56Kjo3XmzJlKrz906JAuu+wy+fr6ljqelZWlgoKCkn8HBgYqODi4VJuL18S50IVDJC+esvXjjz/qwQcf1LBhw3TXXXdpwYIFldYIAJI0atRovfTiMuXn55d8yLvmmmvUsmXLWvWXnJykPz79dMlIlv4D+uvmm27SL3+5WH/967vq1r27JKlDh4564P7Z2r59u2666aZy+9r1xRfKyMhgihIAoMFp1aq1QkJD9dOpU5Kkpc8+o+jWrRW/YmXJmmu33nqrZsy4Uy+88IJuGDq0wr7mzptXaqrTpEmT1K5de7300otKSEhQdHS0/j97dx4eVX2wffyemYQsJCQkBISAgAsCylIKCAhUVEBWARFRcMMF9a0oStW6tbZ2UVsrrq3bY5VF1LJVQARbUSwKSFFc2GQnezLJZGayzHLePyiRmD2ZzMnMfD/XlevSc87M3NPHJzO5z29p3bq1Lhw1SuvWva+75t8tq/XEPe3d332nAwe+13XXX998bxZAWGrSyJglS5YoOjpaM2bMCFQeSVJJSYmkE4tb/lhsbGzF+Zo4HA4lJCRUOX7NNdcoLS2t4ue+++6rcs3zzz+vDRs2VPnp+78/YKrjcrk0depUtW3bVkuXLpXNZqvrLQKAJGnMmDEqKyvTxx9/LJfLpU8+/ljjxje+/IiPj9ell46r+Pdu3borMTFR3bt3ryhiJKlPnz6SpOPHjtX4XGvXrVVUVJTGjBnb6DwAADSX+Lg4udxuFRUVaevWrRozZqxcLpfsdrvsdrsKCws1bOgwHTlyuNYpRKcWMSVut+x2u/r17yfDMLR79+6Kc5MmTVJubo62bdtacWzt2jWKjY2t9YYuAFSn0SNjnE6nVq1apbFjxyo1NTWQmSoW1j11FMtJpaWldS68m5iYKKfTWeX4b37zG/38f9vEjh49utrHDh48uMr0KElq27at8vLyqn3MzTffrO+//17/+c9/lJqaKofDUWs+ADgpJSVF559/vtatXavS0hL5fD5dckn1v5/qo0OHDrJYLJWOJSQm6rTTTqt07OT6XTX9vnK73fro3//WsGHDlJyc3Og8AAA0F3dJiVJSUnTkyBEZhqHnn39Ozz//XLXX2gsKapxGlJmZqRdeeF6bPvqoyuei01lc8c9DhgxVWlqa1q5Zq/PPHyK/3691697XhRdeWGW0PQDUpdFlzMqVK+u9i1JDnZyelJmZqS5dulQ6l5mZqcGDB9f6+J49e+rLL7+Ux+OpNFWpttEtjbVw4UItXbpUixYtUv/+/QP+/ADC37hx4/Wb3zyq/Pw8XTB8uNq0adPo5zo5bLrq8epH7BmGUe3xf//rXyd2URo/odFZAABoLtnZWXIWF6vL6afLMPySpGuvu07Dhl1Q7fVdTj+92uM+n0+3zr1FDodDN9wwR926d1dcXJxycrL1yMMPy+//4XPSZrNp3LhxWr58uR548EHt3Plf5ebmaPyEiYF/gwDCXqOnKS1evFgJCQmaPHlyIPNIUkWpsX379krHMzIydOzYsTpLj4kTJ6qkpEQrVqwIeLZTffLJJ1qwYIHuuuuuZimlAESGiy6+WFarVV999VWL2bVo7do1io+P188uvNDsKAAAVPHeeyd2URo2dJg6p3eWJEVFRWnIkCHV/tQ0cmXfvn06fPiw7r7nHt0wZ45GjRqlIUOGKC2t+rXbJk6aLKfTqU2bNmntmrVq27athg0b1jxvEkBYa1QZk5ubq40bN2rq1KmKj4+v9pojR45UmmPZEOeee6569uypl156ST6fr+L4iy++KIvFounTp9f6+Ntuu00dOnTQ/PnztXfv3irna7oT3BCZmZmaMWOGhg8frieffLLJzwcgcsXHx+uBBx/Urbfdpp/97Gdmx1FBQYE+//xzXXTRRXVOCwUAINi2fv65Xn7pJaWnp2v8hAlKSU3VwIGD9I9331Vubm6V6wsKCmp8LpvtxJ9Dp/59YBiGlixeXO31PXr0UI8ePbRi+XJ9+OFGXXrpOEVFNWlPFAARqlG/OZYtWyav11vraJBrr71WmzZtqvSLraioSM8++6wk6dNPP5UkPffcc0pOTlZycnLFei6S9OSTT2ry5MkaM2aMZs6cqa+//lrPPfecbrrpJvXq1avWfCkpKVqxYoUmTZqkfv36aebMmRo0aJCio6N19OhRvfPOO5Kk02sYrlgf8+bNU25uru6991699dZblc6dccYZjX5eAJFp8uTqdzQywwfr18vr9TJFCQBguk83b9ahgwfl9flUkJ+vrVu36rPPtqhjx45auPCZig0/fvnAA7rh+ut0xfTLNW3a5UrvnK6C/AJ9+dWXysnO1tvvvFvt83fr1l1dunTRX556Sjk5OUponaCNGzfKUVzzGpATJ07SU0/9WZI0fgKflQAap1FlzOLFi9W+ffsGrxput9v18MMPVzr25z+f+EXWtWvXSmXMxIkTtXz5cj366KO64447lJaWpgceeECPPPJIvV5r6NCh+vrrr/XUU09pzZo1WrZsmfx+v9LT0zV8+HC99NJLGjFiRIPynyo3N1c+n0933313lXP3339/o58XAMy2du2aEwsLDxli4sbcAwABAABJREFUdhQAQIR74YXnJUnR0dFKSkrSWWed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7/drw4YN+tnPflZx7uOPP9aYMWNUXl6u/fv3q127dkpKStKx4xlq06ZNsN5msyopKNSxrf9la04AAJrIYrWqy9CBik1KNDsKAAARz+FwqHN6JxUVFQX17/eIGhkza9Ys5eXlaf369VXOLVu2TB6PR7Nnz672sZ9//rnKysp00UUXVSpiJGnkyJEaO3asDMPQ9u3bmyW7mXwejzK//IYiBgCAADD8fmV9+Q2L4QMAEMEiqoy5+uqrZbFYqp2OtGjRIiUkJOiyyy6r9rExMTH1eo3U1NQmZWxpDMNQ9q7d8paUmh0FAICwUe50KefbvWbHAAAAJomoMqZLly4aOXKkVq9eLafTWXH8wIED2rJli6ZOnar4+PhqHzt48GAlJyfrX//6lzZt2lTp3Mcff6z169fr7LPP1ogRI5r1PQSb/eARObNyzI4BAEDYcRzLVOGR42bHAAAAJoioMkaSZs+eLbfbreXLl1ccO7mob01TlCQpKSlJr776qqxWq0aNGqXhw4dr5syZGj58uC688EINGjRI69evV6tWrZr9PQSLMydP+Xu+NzsGAABhK/fbvXLn282OAQAAgiziypjp06crJiam0q5KixcvVseOHXXxxRfX+thp06Zp3bp1Sk1N1aeffqply5bp008/VWJiosaMGaP09PTmjh80pYUOZe38hu03AQBoRobfr8wdu1TmKDY7CgAACKKoui8JL8nJyZowYYJWrVqlrKwsHT16VHv27NH8+fNls9lqfeyf//xn3XvvvZoyZYp+/etf64wzztCBAwf0yCOP6JFHHtHnn3+u9957r+L6Xbt2qXXr1s39lgLO43LL/vUeGV6f2VEAAIgIBw4dVNtzeygqPs7sKAAARBSXy2XK60bU1tbe/+1asGLFCk2bNk1/+ctfdPDgQT3zzDP64osvNGDAAGVlZaljx45Vtrb+6KOPNGrUKA0YMEDbtm2T1frDoCKfz6eBAwdq586dWrt2rS644AIlJSUF+20GRLQs6qxWsslidhQAACKKT4aOqEzcCgEAIPiCvbV1xI2MkaTx48crOTlZb7zxhjIyMtSrVy8NGDCg1se8+eabkqSpU6dWKmIkyWazadq0adq5c6c+/vhjXXDBBZKkde+vD7mRMRa3R632FUphX9EBANDCWKTynikyYmofqQsAAALH5XJp3KVjg/66EVnGxMTE6Ior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jggAjdKgMmbo0KEaOnRopWObN2+W2+3WrFmz6nz8u+++qwsvvLDSsZ/+9Ke67rrrtHjxYt10002SpHPPPVf79u1T165dK667/fbbdckll+jxxx/Xvffeq9atW9f4Ol6vV1OnTlV2drY2bdqkCy64oNL5X/3qV/rggw/k8/nqzAwATTV8+HBt3LBB9913v6Kifvi1u27dWvXu3Vv2H40ObG5paWna+OG/1K5dO33zzTeadfVVNV47Y8aVuuGGOYqNjdUffv97yhgAQFBdMHy4zj333Ip/v/HGm7T18881b94duvPOeVqxYqViY2NNTAgAjdPkNWOWLFkii8Wiq6++us5rf1zESNLUqVMlSd99913Fse7du1cqYiTJYrFoypQpKisr04EDB2p9nXfeeUdff/21Hn744SpFzEljxozRuHHj6sxck1//+tc1Tte67bbbGv28AMLPpePGqbCwUJ99tqXimMfj0cYNGzRu3PgGP9+NN87R5dOmau/evbpxzg0acv5gTZo4QRs2fCDpxIib2bOu1vmDB+myyZP02WefVXp8q1at1K5du3q9VmpqKl9yAQAtyuDzz9fNt9yizIwMrVnzXsXxgwcPasE9d2vkiOEaPGigrr5qpj766N91Pt+OHV9owYJ7dOnYMRo08KcaO2a0nnzyCZWWllZcs3LlSvXv11e7T/mb5aRXXnlZA37SX9nZ2YF5gwAiQpPKGI/Ho7ffflvDhg1Tt27dGvUcWVlZklSvPwzqe+0///lPSdLs2bMbnKe4uFh5eXlVfsrKyipdN23aNL355puVfu666656vxcAkaNTp3T17ddP769bV3Fs8+bNcjqdGnvppY16ToejWPPu+LnO69NHd901X9HRrXT/ffdp/fvv6/777tXwESM07847VVJSogUL7mFuPQAgrEycOFHSieUNpBNLFFxzzWwdOHBQN8yZo7vvWaC4uDjNv+su/evDD2t9rg0fbFBpaamumDFD993/Sw0bNkxvLV2qhx56sOKa0aNHKzY2VmvXrqny+LVr1mrgwIHq0KFDAN8hgHDXoGlKP7Z+/Xrl5+fXa4pSTR5//HH9f/buOzyqOm//+D0z6b0QICQgiCKgFJEuKIqEjoIIKAhYVtFViriKgu7adhWfdUWFtaBrIQiKtF2agj9QXKSIYAMV6RDSk0nPZGZ+f2SJhBRSJnMyyft1XXNd5JzvnPOZfXzmnLnPt1gsFo0dO7bSdmlpaVq8eLH69++v6OjoStsePHhQYWFhZeawycnJUV5eXsnfPj4+CgkJKdXmzjvvrPC45w6N6ty5szp3/n0iy5SUFM2dO1edOnXSnDlz9Morr1RaI4DGZejQYXr1lQXKz88vuZm76qqr1LRp0xodLzk5SX97/vmSnjW9+/TWTTfeqMcem6P33ntfnf73/dSmzcW6/75p2rx5s2688UaXfR4AAIzUrFlzBQUH6+SJE5KkF+e/oOjmzRW/9MOSudjGjx+vqVOn6OWXX9b1AwdWeKwZM2eW6gU6duxYtWzZSq+++ooSEhIUHR2twMBADbjuOm3YsFEzZz0ks7n4mfbBAwd0+PBvmjJ1at19WAANUq16xixdulTe3t4aN25cjd//9ttva/bs2br00ksrbOdwODRx4kRlZGTo1VdfveBxrVZruSsfzZ07V1FRUSWv8oZWPfnkk/rss8/KvOLi4io8n91u16233qqsrCytWrWq0vlsADROcXFxKigo0BdffKGcnBx9+cUXGjqs+kOUzgoICNCQIb8PtWzduo2Cg4PVpk2bkiBGkjp16iRJOnXyZM2LBwCgHgrw91dObq4yMzO1a9cuxcUNVk5OjtLT05Wenq6MjAz17dNXx48fq3QI0blBTF5urtLT09Wlaxc5nU4dPHiwZN/IkSOVnJyk3bt3lWxbv36d/Pz8yixyAgAXUuOeMdnZ2VqzZo0GDx6syMjIar//yy+/1F133aXBgwfrueeeq7Ttgw8+qI0bN+r9999Xly5dLnjs4OBgpaamltl+//33l3RprGgIU6dOncr9Ml2yZEmF55s3b54+//xzrVu3Tm3btpXVar1gjQAal4iICPXq1Usb1q9Xfn6e7Ha7brhhUI2P16xZM5lMplLbgoKD1bx581LbgoODJYnvJQBAg5Obl6eIiAgdP35cTqdTCxe+poULXyu3bXpaWoXDiBISErRo0UJt27q1zPUyOzur5N+9e/dRVFSU1q9br169esvhcGjDho0aMGAAD2MBVFuNw5jVq1dXeRWl8+3fv1+jRo3SFVdcoRUrVpRaXeR8Tz31lBYtWqTnn3++ystQt2/fXvv27dOpU6dKDVVq165dyfJ3rpqQcvXq1XrhhRf0zDPPaEgN534A0DgMHTpMTz/9lFJTU3R1v35lhklWx9nu0WW3W8rd7nQ6a3wuAADqm8TEM8rOylLLVq3kdDokSZOnTFHfvuUv3tGyVatyt9vtdk279x5ZrVbdccedat2mjfz9/ZWUlKgnn3hCDsfv10+LxaKhQ4dq5cqVenzuXO3b962Sk5M0bPgI139AAA1ejYcpxcfHKygoSKNGjarW+3777TcNGTJETZs21fr168sdTnTWwoUL9Ze//EUzZ87Uo48+WuVznO39Eh8fX63aquuXX37RlClTdNNNN+nxxx+v03MB8HzXDxwos9ms7777rkarKAEAgGL/+U/xKkp9+/RVbEysJMnLy0u9e/cu91VRz5Vff/1Vx44d00OzZ+uOO+/Uddddp969eysqqvw53UaMHKXs7Gxt27ZN69etV3h4uPr27Vs3HxJAg1ajMCY5OVmbN2/W6NGjFRAQUG6b48ePlxpjKRWvhhQXFyez2axNmzYpKiqqwnMsX75c06dP18SJE/XSSy9Vq75x48apY8eOeuaZZ8os6XpWbZ8SZ2dna/To0YqJidF7771XZrgAAJwvICBAj8+dq2n33adrr73W6HIAAPBIu3bu1FtvvqmYmBgNGz5cEZGR6t69hz5ZsULJycll2qelpVV4LIul+OfQub8NnE6nllbwUPdsT/tVK1dqy5bNGjJkaKW9/AGgIjX65li+fLmKiooqHaI0efJkbdu2rdQX25AhQ3T48GE98sgj2r59u7Zv316yr1mzZho0qHj+hF27dmny5MmKjIzUwIEDy/Rw6du3ry6++OIKz+3t7a1Vq1Zp8ODB6tevn8aMGaP+/fsrMDBQp06d0tq1a3X8+HENHz68Jh9fUvHwqZ9++knz5s3TmjVrSu07f84GADhr1Kj6saLRsg8/VFZWlpKSkyRJX2zbqqT/TW444dZbS+aaOX36tNb97+njTz/9KEl66803JUnR0dEaMXKku0sHADQiX23frqNHjqjIbldaaqp27dqlr7/eoejoaC1Y8Ip8fX0lSY89/rjumDpFt4y9WWPG3KyY2BilpaZp/3f7lZSYqI8+XlHu8Vu3bqOWLVvqHy+9pKSkJAUFBmnz5s2yZlU819qIESP10kt/lyQNq8XvCQCNW43CmPj4eDVt2rTas4bv379fkjR//vwy+6699tqSMOann35SYWGhkpOTy11q+l//+lelYYxUnFrv27dPr7zyilatWqUNGzao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oMsuu0yzZ89WRkZGqXapqal68cUXdc011ygqKkphYWHq3bu3li9fXq0PZ7Va9dxzz6l79+4KDQ2Vr6+vLrroIo0fP17r1q0r1Xbr1q0ymUxasWJFhZ89KIggwtyCZTiBqvrP15+r14Nj1OvBMdr324Ey+51Op0Y+8Qf1enCMHnr9ObfVlVuQpzfXLdOMRU9r0KOT1evBMfrP15+X23b1V59p2oJ5GvL4Heo3a5xu+vM0Pb3kVZ1OTXJbvUCDYTIx5BeoojVr1qhrl84lr549umvQDQN137RpWhofr5ycHKNLBIBa8apO43vvvVc33HBDqW1Op1PTpk1T69atFRMTU+n777nnHrVo0UKTJk1Sq1at9P333+u1117T+vXrtXfvXvn7+0uSduzYoblz52rYsGGaN2+evLy89Mknn2jChAn66aef9NRTT12w1kOHDmnw4ME6duyYRo8ercmTJysoKEgnTpzQ+vXrNWLECL3//vu6/fbbq/M/QaNnDvCRKdBXzpwCo0sBPIavt48+3fOFurbtUGr73kM/KikjVT5e3m6tJyM7S29v/EjNw5vokpjW2vvrDxW2/eXkYbWIaKb+V/RQcECQTqcmas1/N+urH7/RkjkvKSo0wo2VA57NFOwrk797//8d8HT33/9HxcTEyFZUpNTUFO3ZvUcvvjhfHyz5QAsWvKJ27doZXSIA1Ei1wpg+ffqoT58+pbZt375dubm5mjhx4gXfv2LFCg0YMKDUtquuukpTpkxRfHy87r77bknS5Zdfrl9//VUXXXRRSbv7779fN9xwg1544QU98sgjCgyseLx1UVGRRo8ercTERG3btk1XX311qf1//vOf9emnn8put1+wZpRlaRGiol+TjS4D8Bh9OnbTln079NDYu+Vl+X3epU17vlT7lm2VmWN1az1NQsK1/rm3FRkSrgPHD2nqi49U2PaR8feW2XZt516a+uKftH7nVk2JG1OXpQINioXepUC1Xd2vny6//PKSv++6627t2rlT06c/qBkzpmvVqtXy82PFTwCep9ZzxixdulQmk0m33XbbBdueH8RI0ujRoyVJBw783oW/TZs2pYIYSTKZTLrppptUUFCgw4cPV3qejz/+WD/88IOeeOKJMkHMWXFxcRo6dOgFa67IX/7ylwqHa9133301Pq4nMEcGSmamGwKqKu6qfsrMydKug/tL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7/Xaa69p/fr12rt3r/z9/UvaTpw4UWvXrtUf/vAHdevWTadPn9bChQvVp08fff/997rooosqPdeuXbs0fPhwZWVlacKECZo2bZp8fX115MgRrV69Wu+++662bduma665pjr/EzR6PoEB8gsNUX6m1ehSAI/i7eWt7fv2qn3ri0tt/+nIb0rNzJC3V7W+jmvNmpujFZ9/qiZh4Wod3UI/Hj5Upfdt/O+XSslIr+PqgIbJPyJM3v5+RpcBeJT77/+jYmJiZCsqUmpqivbs3qMXX5yvD5Z8oAULXlG7du2MLhEAaqRad/99+vRRnz59Sm3bvn27cnNzNXHixAu+f8WKFRowYECpbVdddZWmTJmi+Ph43X333ZKkU6dOaeXKlXr44Yf14osvlrTt37+/rr/+eq1cuVKzZs2q8Dzp6em66aab5OXlpX379ql9+/al9j/77LNatmxZqfAHVRd6Uazyv/vJ6DIAj3LlZR2044d9umPkaFkslpLt2/d9o4tjYpXl5id84cEhevPxpxQWHKLfTh7XYwv/ccH3ZGZnacXnm3TjNQP10eYNbqgSaFiYuBeovqv79dPll19e8vddd92tXTt3avr0BzVjxnStWrVafn6EnAA8T63njFm6dKlMJpNuu+22C7Y9P4iRpNGjR0uSDhw4ULItKytLktSsWbNSbaOjoyXpgiHK66+/roSEBL388stlghhJMplMuvXWW9WjR48L1lyRyoZs/e1vf6vxcT1BUNNImc/5MQngwvp1uVLZubn67tAvJduKior09Q/fqV+Xq6p9vL+8+Zpmv/yCjiWc1p/ffE2TnnxED774nL7+fp8k6afDh/T4wn9o4hOPaMbf/6rvDv1c6v3eXl4KCw6p1jnjN/5HLZo01TVXVr9eoLGz+PgokIl7AZfo2auX/nDPPUo4fVrr1v2nZPuRI0f08OyHdE3/furZo7tuu3WCtm698JDavXu/0cMPz9aQwXHq0f0qDY4bpBdfnK/8/PySNqtXr1bXLp118JzfLGctXvyWul3ZVYmJia75gAAahVqFMTabTR999JH69u2r1q1b1+gYZ86ckSQ1adKkZFvbtm0VGxurv//97/r3v/+tkydPateuXZo2bZratGmjCRMmVHrMf//73/L399eYMWOqXU9mZqZSUlLKvGw2W6l29957rz744INSr7O9g879LA2RxcdH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VXfc8SvdeecdWrbsbcXFxVlYIQA0T4vnjFm4cKFsNpumTp16wrYZGRlKTk5u1nW2bdum7777rlGhz4svvqjc3Fw988wzdYIYSbLZbLruuutOGOocz/GGbP3+979v9nkBRKZLxo9XSUmJPvxwS802n8+nde++q/HjJzT5fDffPFM/vWKKvvnmG9088yYNPe9cTZ40Ue+++46k6h4306dN1XnnDtFll07Whx9+WOv4mJgYdejQoVHXWvDG6zrzzDN10ejRMgxD5V5vk+sFACBYzj3vPP38lluUe/iwVq78Z832PXv26J6779KokSN07pDBmnrdtdqw4f+d8HyffPKx7rnnbl0ybqyGDD5H48aO0VNPPamKioqaNm+//bYGDRygnV9/Xef4V155WWefNUh5eXnBeYMA2oUWhTE+n09LlizR8OHD1bNnzyCVVL+cnBxJalQY849//EPx8fG64oormnyd0tJSFRYW1nn5fL5a7W699Va98cYbtV5Ha2vsDxwA7UfXrtkaMHCg1qxeXbNt8+bNcrvdGnfJJc06p8tVpjt+dbvO7N9fs2fPUXR0jO6/7z6tXbNG9993r0aMHKk77rxT5eXluueeu5s1vt7tduuLL77QGWecqfnz52nE+cM1bNhQTZwwXmvXrm1W3QAAtNSkSZMkVU+DIEnfffedZsyYrt279+immTN11933KD4+XnNmz9Z769cf91zvvvOuKioqdNXVV+u++3+t4cOH681Fi/Tgg3Nr2owZM0ZxcXFatWplneNXrVylwYMHq1OnTkF8hwAiXZOGKf3Y2rVrVVRU1KiApCUCgYAWL16sc889V6eeeuoJ2+/cuVOnn366oqOja20vKytTZWVlzd/x8fFKTEys1ebHc+Ic69gukj8esvXdd9/p9ttv15gxYzRz5kzdc889J6wTQPsyfvwEPTt/nioqKmq+0J1zzjnq2LFjs85XUJCv3//hDzU9a4YOG6rLL7tMv/71/XrttdfVf8AASVKvXifrF7fN0rp163TZZZc16RoHDxyQaZpau3aNHA6HZs+Zo6SkJC3MydH9992rpKREnX/+iGbVDwBAc3Xq1FlJyck6eOCAJOmpJ59Ql86dlbNwUc08bNdcc41uvPEGPfPMM8cdZnvn7Nm1hjpdeeWV6t79JD377Hzl5uaqS5cuSkxM1AUXXqjVq9do9py7ZLdX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ulRvvvmm9uzZU7MtFI4OUWqoV8z4S8aFpI5gSlWMBsRkyiab1aUAANCuGKapT30F8oj5YwAAiHRhH8ZMmDBBaWlpev3113X48GH17dtXZ599dqtft6SkRKtWrZLNZtO0adPqbbN6zdqw6xkDAAAAAEB74fF4LOlIEfZhTGxsrK666iq9/PLLkqQ77rgjJNddsmSJKisrNWrUKPXo0aPeNv3791dKSkpI6gEAAAAAAE3jcrksuW5YT+B71IwZM5SZmakOHTo02Esl2E40RAkAAAAAAKA+Yd8zRpJGjhypwsLQTTi7b98+bd68WXFxcbrqqqtCdl0AAAAAABD+IiKMaY5XXnlFr7zyiiTJ5/NJknJzczV06NCaNi+88EK988/k5OTINE1NnjxZqampoSkYAAAAAABEhHYbxhw8eFBbt26tta2qqqrWtobGjuXk5EgK3apNAAAAAAAgcthM0zStLiLSuFwupaam6uChw0zgCwAAAABAG+VyudQtu6tKS0tD+vs9IibwBQAAAAAACBeEMQAAAAAAACFEGAMAAAAAABBChDEAAAAAAAAhRBgDAAAAAAAQQoQxAAAAAAAAIUQYAwAAAAAAEEKEMQAAAAAAACFEGAMAAACgXdi+fbsGDRyg7du3W10KgHauSWHMjTfeKJvN1uDr0KFDxz1+2bJlGjdunLp27arY2Fh169ZNV155pb744os6bXv27FnvNWbNmtXoevPz83X//ferf//+SkpKUlxcnE499VTddNNN2rx5c622f/vb32Sz2fTRRx/Ve64LLrhAZ555ZqOvDQCStHz5cg0aOECDBg7Qp598Ume/aZoaN3aMBg0coF/dfnvI6vJ6vXrhhef1i9tmadTIERo0cICWL19eb9uj9df3uvXWW0JWMwCg/Tj2/jlo4ACdO2Swxlw8WrfNmqWFOTnyeDxWlwgALRLVlMa33nqrLr744lrbTNPUrFmz1LNnT2VnZx/3+M8//1zp6em688471aFDBx05ckR//etfde6552rLli0aOHBgrfaDBg3S3XffXWtb7969G1Xrtm3bNHHiRJWVlenaa6/VrFmzFBsbqz179ujtt9/W3/72N23cuFGjRo1q1PkAoCViY2O1avUqnXX22bW2f/TRR8rLy1NMTExI63E6nXrpL39Rly5d1Lv36froo4afEP7ud/9dZ9uXX32phTk5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PAffd999uuGGG4psM01T999/v9q3b6/Y2Niznr9v3z41btxYU6dO1Xnnnafk5GQtXLhQvXr10vbt29W9e/cSz3vkkUcUEhKi/Pz8MmfdsWOHhg4dKqfTqXHjxun+++9XWFiYEhIStHr1ar3//vvaunWrrrrqqjJfEwAqKiwsTOs3rNfvLrusyPZdu3YpJSVF9erV82ue9/+xUHv27NHAgYPUqXMnZaRnaNmypRo37lZ99NEiXdCpU+GxKSnJmnz3JEVFRenhh6coJzdHH37wgQ4eOqjFi5coNDTUr9kBAHXHgw/+QbGxsfJ4vcrISNeunbv08ssv6aNFH2nu3Hnq3Lmz1REBoELKVcb07dtXffv2LbJt27ZtysnJ0fjx4895/jPPPFNs2z333KPWrVvr73//u95+++1i+zdu3KiNGzdq5syZev7558uU026366abblJISIj27Nmjiy66qMj+559/XsuWLVN4eHiZrgcAldW/f39t3rRJjz8+SyEhv/7o3bBhvbp06SJ7VpZf80yYeIdeeHFOkSJlUFycbhlzsxYuXKi/vvBC4fZ3331Xubm5WrJ0mVq2bClJuvTSrrr/vnu1Zs0ajRkzxq/ZAQB1x5X9++uSSy4p/H7y5Hu047vvNGXKw5o6dYpWrVqt+vXrW5gQACqm0mvGLFmyRDabTbfffnuFzm/WrJkiIiKUVcIfIh6PR1OnTtXUqVPVsWPHMl/z7bffVlJSkt54441iRYwk2Ww23XbbbbriiisqlFk6+5StF874IwYAJGnwkCHKysrSt99uL9zm8Xi0edMmDRlyY7mvN3ny3bp59Cj9/PPPmnz3JPXp3UvDhw3Vpk1fSDo14mbC+NvVu9cVGjliuL799tsi5/fo0aPYiJZ27dqpY8eOSkiIL7L9y82bNeCqqwqLGEnq06eP2rVrp01fbCx3dgAAKqNX7976/b33KikxUevW/bNwe0JCgmZMf1RXDeivXlf01O23jdOWLf/vnNfbvfs/mjFjugbHDdIVPS9X3KCBevnll5SXl1d4zOrVq9Wjezcd+PHHYue/++4CXfa7HkpJSamaNwigTqhUGePxePTxxx+rX79+at++fZnPy8rKUlpamvbt26d77rlHDodD119/fbHj3njjDdntdj311FPlyvXZZ58pPDxco0ePLtd5kpSdna309PRiXx6Pp8hx9913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unfvXuqxN9xwgz777DMlJycXToH67rtvdfToUU2YMNFfkQEAkCTt+O47LXjnHcXGxurGoUMVFhamnj2v0KcrVui2225X06ZFF53PzMxUTExMidcKDj41yvTMvw9M09SSxYtLPL5z587q3LmzVq1cqX37vtfw4SMUElKpZ6IAqKMq9JNj+fLl8nq9Zx0Ncscdd2jr1q1FfrB17dpV119/vXr06KHGjRvr4MGDeu+99+TxePTiiy8WHjdo0KBi1zs9Eubqq68ucU2XM8XExGjVqlUaPny4unfvrnHjxumKK65QaGiojh8/rk8++USSzjrV6VymTJmitLQ0zZw5U8uWLSuy7/zzz6/wdQHUfiNGlP5EI3959dVXtGXLFl199dVyZGdr3S93GE8bOuzXIdeT7/m9Nm3apN/fM1m33z5eObk5+uD999WpUyeNvOkmPycHANQl32zbpiMJCfL6fMrMyNCOHTv07bfb1bJlS82dO09hYacWk3/iySc16a47dcuYmzV69M2KbR2rzIxM7f1+r1JTUvTxJytKvH779h3Upk0bvf7aa0pNTVVUZJQ2b94sh7P0NSCHDRuu1157VZJ049ChVf+mAdQJFSpjFi9erGbNmpV71fAHHnhA69at0+effy6n06lmzZpp0KBBevLJJwuf9lFV+vbtq/379+u1117TunXrtHz5chmGodjYWPXv31/vvPOOBgwYUOHrp6Wlyefz6dFHHy22b9asWZWJDgDV7qeffpIkbd26VVu3bi22/8wypkWLFnr3vYV69ZVXNHfuGwoNDdWAAVdp+owZrBcDAKhWf/vbfElSaGioGjZsqAsu6KTHHpupkTfdVGTR3I4dO2rJ0mV6++2/a+3aNcrKylJMTIwuuugi3XvffaVePzQ0VHPnvak5c17UwvfeU1hYmK697jqNG3ebxt4ypsRzbhw6VHPnvqHWrVtX+d8wAOoOm1kVc3ZQhMPhUMOGDXXiZGKlFucEAAAAULPY7XYNvOF63XvvfWctegAEBofDodaxrZSdne3Xv9/L/9xoAAAAAKij1q5dI5/PV2QUKQCUF6tNAUANkp2dLY/HU+r+oKCgUhchBAAA1WfHd98pPj5e7y5YoGuvvU6xsbFWRwIQwChjAKAGefTRR/SfXbtK3d+yVStt2PC5HxMBAABJ+r93/k979+xRjx6/0+OsEQmgkihj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unWMfAEAAAAAAOUS8CNjJGnAgAFKT08v8/E5OTkaOnSo9u3bp7Fjx2rBggWy2WznPG/atGlatGiRLrroIn3xxRdq1KhRJVIDAAAAAIC6qFaUMeU1e/Zsbd++XcHBwQoJCdHkyZNLPO79998v/PeaNWs0b948SVKbNm302GOPlXjOrFmzyvQkJgAAAAAAUDfVyTLGbrdLknw+n5YsWVLqcWeWMafPkaRNmzaVes5dd91FGQMAAAAAAEplM02T5ydWMYfDoYYNG+rEyUQW8AUAAAAAoIZyOBxqHdtK2dnZfv37vVYs4AsAAAAAABAoKGMAAAAAAAD8iDIGAAAAAADAjyhjAAAAAAAA/IgyBgAAAAAAwI8oYwAAAAAAAPyIMgYAAAAAAMCPKGMAAAAAAAD8iDIGAAAAAADAjyhjAAAAANQJO3fuVI/u3bRz506rowCo48pVxtx1112y2Wylfp08efKs569cuVK33nqrzj//fEVEROjCCy/U9OnTlZWVVezYRx55RJdddpliYmIUERGhiy++WM8995xcLleZ8zocDv3lL39Rz5491bBhQ4WFhaldu3a69dZbtW7duiLHbtmyRTabTStWrCj1vUdFRZX5tQFgzZo16tG9m3p076b/7t5dbL9pmoobNFA9unfTww895Ldc+/fv1wt//atGjxqlPr17aXDcID322AwdPXKkyHGGYWjNmjWaOuVhxQ0aqD69e+nm0aO04J13lJ+f77e8AIC658zP0B7du6nXFT018Ibr9cD992vJ4sVyu91WRwSASgkpz8H33XefbrjhhiLbTNPU/fffr/bt2ys2Nvas5997771q1aqVJkyYoLZt22rfvn166623tH79eu3evVvh4eGFx+7cuVMDBgzQpEmTVL9+ff33v//Viy++qM2bN+urr75SUNDZe6RDhw4pLi5OR48e1ahRo3THHXcoKipKx48f1/r16zVs2DB9+OGHmjhxYnn+EwBAuYWFhWn9hvX63WWXFdm+a9cupaSkqF69en7N8/4/FmrPnj0aOHCQOnXupIz0DC1btlTjxt2qjz5apAs6dZIk5eXl6dlnnla3bt005pZbFBMTo+/3fq+///1v+u6777Tg3Xdls9n8mh0AULc8+OAfFBsbK4/Xq4yMdO3auUsvv/ySPlr0kebOnafOnTtbHREAKqRcZUzfvn3Vt2/fItu2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gQMHKikpSVOnTq1wESNJ5513niQpLS2tcNuGzzcG3MgYAAAAAADqCrfbrSGD/b/+a8CXMZI0ceJErVy5UjabTePHjy/TOXa7XXFxcTp8+LAmTZqk119/vVIZtm7dKknq2LFj4bauXbv6tVkDAAAAAABl53A4LHndWlHGDBgwQOnpZV9wNicnR0OHDtW+ffs0duxYLViwQDab7azn/Pjjj9q3b59uuukm1atXr3C7aZpavny5XnrpJdlsNt15550Vfh8AAAAAAKD2qxVlTHnNnj1b27dvV3BwsEJCQjR58uQSj3v//fcL/52SkqJbb71VDRs21OWXX64WLVooKytLP/zwg44cOaKgoCC99tpruuKKKyxr1gAAAAAAQM1XJ8sYu90uSfL5fFqyZEmpx51ZxlxyySX605/+pC1btujnn3/WN998o6CgILVu3Vp33323/vCHP+iyyy6r7ugAAAAAACDABdzTlAKBw+FQw4YNdeJkImvGAAAAAABQQzkcDrWObeX3pykF+e2VAAAAAAAAQBkDAAAAAADgT5QxAAAAAAAAfkQZAwAAAAAA4EeUMQAAAAAAAH5EGQMAAAAAAOBHlDEAAAAAAAB+RBkDAAAAAADgR5QxAAAAAAAAfkQZAwAAAKBO2Llzp3p076adO3daHQVAHVeuMuauu+6SzWYr9evkyZNnPX/lypW69dZbdf755ysiIkIXXnihpk+frqysrGLH5uXl6YUXXlCXLl0UERGh2NhY3XLLLfrf//5X5rypqamaNWuWunbtqqioKNWvX18XXHCBJk2apG3bthU59v3335fNZtOuXbtKvNY111yjSy+9tMyvDQBr1qxRj+7d1KN7N/139+5i+03TVNyggerRvZsefughv+Xav3+/XvjrXzV61Cj16d1Lg+MG6bHHZujokSPFjv300xWafPckXXftNbqi5+W6cchgPfP00+f8eQ8AQGWc+Rnao3s39bqipwbecL0euP9+LVm8WG632+qIAFApIeU5+L777tMNN9xQZJtpmrr//vvVvn17xcbGnvX8e++9V61atdKECRPUtm1b7du3T2+99ZbWr1+v3bt3Kzw8vPDY8ePHa+3atfr973+vyy67TImJiZo/f7769u2rffv2qV27dmd9rR07dmjo0KFyOp0aN26c7r//foWFhSkhIUGrV6/W+++/r61bt+qqq64q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fyvRmqLeHbopyL/0D28eRbROK2q5YRRclpaZrmn/nitbRrremfavYneZ8q3lo+CAQFnTCw94APyFVjFA+YuNjVGazaZmzZvLMFySpNtuv119+vQtdPtmzZsXutzpdOqeqVNktVo1efKdatmqlfz8/BQXF6t/zpkjl+uvm6enp6eGDRum1atX64knn9T+/T8rPj5O1w0fUf4XCKDaK3UYEx4ersDAQI0aNapE+x07dkzXXnutGjRooI0bNxbanehc69evl81mK1HoM2LECO3evVtr1qwpdReq4tixY4dmzpypadOmlWncnKrKo16g9Gd84d/wABRqYJdeenHlIh068Yf+NXmGqbVk23M0450XdCouSm898LQuatys2PumZ2UqJd2mkKCCg/0COIenB2EMUAG+/PLsLEp9evdR07CmkiQvLy9dccUVJTrOn3/+qZMnT+q5uXM1cuRf32tyBwb+uxEjR+njjz/W9u3b9d3OnapTp4769Cl9K1cANVep2szGx8dr69atGjNmjPz9/Qvd5tSpU/n6WEpnZ04aMmSIPDw8tGXLFtWvX/+C51q+fLn8/f3zpsAujnvvvVcNGzbU9OnT9ccffxRYb5RDeBAdHa3x48erX79+mj9/fpmPVxVZvDzkEVr4//8ACufv46fHbpqqu4fdpH6XFuwS6S5Ol1NPfviKDh4/oufvnKlOrS4udLtse06hszB9sPlTGYah3h0uq+hSgSrNo15Age6EAMpmzw8/aPG77yosLEzXDR+u0Lp11b17D33+2WeKj48vsH1SUlKRx/L8XxfCc78fGIah5f+byfXv2rVrp3bt2mnN6tX6+uutuvbaYedt5Q8ARSnVJ8eqVavkcDjO2xrktttu0/bt2/N9sF177bWKiIjQrFmztHPnTu3cuTNvXcOGDQuM45KUlKRNmzbphhtuuGALmnOFhoZqzZo1GjlypLp06aIJEyaoR48e8vb2VmRkpD799FNJUvMimisWx0MPPaT4+HjNmjVLK1euzLfuoosuKvVxqxqPeoFyJdJVCSiJ4b0GmV2CFqxZoh0Hf1T/S7vLmpGmTT9uz7d+WI8BkqREa4pumzdDQy7vpxYNz7553P37z/r+t33q3eEyXdmpp9trB6oSj7q0igHK4rudO3Xi+HE5nE4lJSZqz5492r17lxo3bqwFC96Qj4+PJOnxJ57Q5Dtu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vrzI7f4exkjSM888oy5duuj111/X7t275XA4dPHFF+v222/XAw88UOIptQEAAAAAQM1iMQzDMLuI6sZqtSo4OFinz0QxfgwAAAAAAJWU1WpV07AmSk1Ndev392oxZgwAAAAAAEBVQRgDAAAAAADgRoQxAAAAAAAAbkQYAwAAAAAA4EaEMQAAAAAAAG5EGAMAAAAAAOBGhDEAAAAAAABuRBgDAAAAAADgRoQxAAAAAGqEH3/8UV27dNaPP/5odikAargShTF33HGHLBZLkT9nzpw57/5HjhzR9OnT1adPH/n6+spisejEiRNFbm+z2TRr1iy1atVKPj4+CgsL07hx45SRkVGseuPi4jR79mx16tRJgYGB8vX1VZs2bTR58mTt3Lkz37ZLliyRxWLR3r17Cz3WwIEDdemllxbrvACQa926derapbO6dumsn/ftK7DeMAwNHTJYXbt01oMPPOC2ug4dOqQXnn9eY8eM0RW9euraoUP06KMzdbKIz2SXy6VPPlml8eNvVK+ePTTgyv66+x936ciRI26rGQBQc5x7/+zapbN69uiuwddcrXvvuUfLw8OVnp5udokAUCZeJdl46tSpuuaaa/ItMwxD99xzj1q2bKmwsLDz7r9r1y698cYb6tixozp06KD9+/cXuW1qaqoGDBig06dPa8qUKWrTpo3i4+O1Y8cOZWdny9/f/7zn2rNnj4YPHy6bzaYJEybonnvukY+Pj44fP661a9dqyZIl2r59u6688spiXz8AlJaPj482btqoy7p1y7d87969io2NVa1atdxaz5IPP9D+/fs1ePAQtW3XVokJiVq5coUmTLhJS5cuU5u2bfNt/9RT/9SmjRs1YsRITZgwQZmZmTp8+LCSkpLcWjcAoGa57777FRYWJrvDocTEBO39ca/mz39JS5ct1YIFb6hdu3ZmlwgApVKiMKZ3797q3bt3vmU7d+5URkaGbrnllgvuP2rUKKWkpCgoKEgvv/zyecOYxx9/XCdPntS+ffvUqlWrvOWPPfbYBc+TnJys66+/Xl5eXtq/f7/at2+fb/3cuXO1cuVK+fn5XfBYAFAe+vXrp61ffaXHHpstL6+/Pno3bdqojh07Kjklxa31TLr1Nr3w4jx5e3vnLRsydKhuHHeDPvjgAz3/wgt5y7ds2aIv1q/Xq6++pquuvtqtdQIAara+/frpkksu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mZO3zbFjx9S3b1/FxsYqKChIvXv3lp+fn3788UdFRUUpNDRU27dv16WXXuq+iwcKkft53bp1ax09erTI7ZYvX65bbrlFPXr00J49e4p17NOnT6tFixby8vJSTEwMg7ajUrjjjjv00UcfKSQkRH369FFmZqa+//57ZWdn6+6779a7776bt63FYlGtWrV02WWXqWXLlsrJyVFERIQOHDggSRo/frzCw8Pl5ZV/RIvIyEj17t1bZ86cUbt27dSpUyf9/vvv+u233+Tn56f169frmmuucet1A+eaMmWKFi9erEcffVQvvfRSkdstWbJEd999t7p3767mzZsrMzNT3333nZKSkjR06FCtXbtWvr6++fYZOHCgtm/frs6dO6tdu3ZyuVz67bffdPjwYUm64DkLVaLoBtVSRkaGMWfOHKN169ZGrVq1jEaNGhl33HGHERkZaXZpQLF9+OGHedPQFfXz4YcfGoZhGFar1XjssceMAQMGGGFhYYaPj4/h7+9vXHLJJcaMGTOM06dPF3qO1NRU45577jE6duxoBAUF5e3z1FNPGTabzY1XC+QXERFhPPHEE0bfvn2Nxo0bG97e3kZAQIBxySWXGA8++KDx559/Ftgnt+XA+X5uv/32AvvFxMQY06dPN9q3b2/4+voaPj4+Rps2bYz777+f+wYqjeK2jBk2bJghyViwYEGxjz1v3jxDkjFmzJiylgmUG5fLZbz77rvG5Zdfbvj7+xsBAQFG7969jSVLlhTY9s033zTGjh1rXHTRRUZAQIBRq1YtIywszBg9erSxZs2a854nLi7OuP/++43mzZsb3t7eRoMGDYwJEyYYBw8erKArA4onKyvLqFOnjiHJOHDgwHm3/eWXX4wJEyYYLVu2NHx9fY3g4GCjX79+xvvvv5+v1de5li1bZowbN85o06aNUbt2bcPb29to0qSJMXbsWOOrr74qVc20jAEAAAAAAHAjBvAFAAAAAABwI8IYAAAAAAAANyKMAQAAAAAAcCPCGAAAAAAAADcijAEAAAAAAHAjwhgAAAAAAAA3IowBAAAAAABwI8IYAAAAAAAANyKMAQAAAAAAcCPCGAAA4BYWi6VEPy1btpQkDRw4UBaLRSdOnDC1/tK68847FRAQoLi4OLedMzo6Wn5+frrvvvvcdk4AAFB8XmYXAAAAaobbb7+9wLKdO3fq2LFj6tKli7p27ZpvXb169dxUWcU5ePCgPvroI82YMUMNGjRw23kbN26sKVOm6O2339a0adPUrl07t50bAABcmMUwDMPsIgAAQM10xx136KOPPtJTTz2lp59+utBtTp06pYyMDLVu3Vre3t7uLbCMRo8erU2bNun06dNuDWMk6cyZM2revLnGjRunVatWufXcAADg/GgZAwAAKrXmzZubXUKpREZG6ssvv9R1113n9iBGksLCwjRo0CCtWbNGsbGxatiwodtrAAAAhWPMGAAAUKkVNWZM7rgyDodDzz33nNq0aSM/Pz916NBBH374Yd52//3vfzVo0CDVrl1bderU0W233abExMRCz+VwOPTvf/9bvXv3Vu3ateXn56euXbvq9ddfl8PhKFHdH3zwgVwul26++eYC606cOCGLxaKBAwcqMzNTs2fPVosWLeTj46M2bdpo3rx5Kqzx8smTJ3XvvfeqXbt28vf3V2hoqC655BJNnTpVR44cKbD9xIkTZbfbtWTJkhLVDgAAKhYtYwAAQJU2fvz4vMCldevW2r59u+68805JUlBQkG6++WZdccUVGjp0qHbt2qWlS5fq+PHj+vbbb2WxWPKOk5mZqeHDh+ubb75RaGiorrjiCvn6+uqHH37Q9OnT9c0332jNmjXy8Cjeu6wvv/xS0tkwqSg5OTkaMmSIfvvtNw0cOFDp6enavn27Zs+eLZvNprlz5+ZtGxkZqW7duikpKUlt27bVddddJ6fTqZMnT2rx4sXq3bu3Lr744nzHzz33hg0b9NhjjxWrbgAAUPEIYwAAQJV18uRJBQUF6c8//1T9+vUlSd98842uuuoqPfnkk8rJydHatWs1fPhwSZLValWfPn20c+dObdu2TYMGDco71syZM/XNN9/opptu0jvvvKPg4GBJks1m04QJE7R+/Xq9++67uueeey5YV1pamn7++Wc1adJETZo0KXK7Xbt2acCAATp+/Lhq164tSdq7d6+uuOIKvfbaa5o9e7YCAwMlSe+9956SkpL0wAMP6M0338x3nFOnTslutxc4/kUXXaR69eppz549/9/evYREucZxHP86k9kQSQbFgKmgREREDkVBRCBEJBER3YggbBZt2kSraGUXKiqoNkVIRATCQEMEA7qKCVdT7qqxwFComJroCpUSOWcRzumcGXU6R6fj6fuBd/O8z+W//vFcGBoaYtasWRPWLkmSpp7HlCRJ0rR24cKFfBAD0NLSQiQSIZPJ0Nramg9iAKqrq9m/fz8Ad+/ezbdns1k6Ojqoq6vj2rVr+SAGvu+uuXr1KjNnzuTy5csl1ZROp/n27VvBTpW/CwQCXLlyJR/EAKxcuZLW1lY+f/5Mb29vvv3169cArF+/vmCe+vp6mpqaiq6xePFihoeH6evrK6l2SZI09QxjJEnStFVZWVn0GFBjYyMAGzZsGPNfJpPJtyWTSb5+/crGjRsJhUIFY8LhMIsWLeLBgwd8+fJlwrqy2SwANTU14/ZraGgoGtiMPkX9Y40rVqwA4MiRIyQSCYaGhiasA2DevHnAn2GOJEn69QxjJEnStBUOhwkGgwXto0d7amtrx/w3PDycbxu9HLijo4OKioqi36NHj8jlcrx9+3bCuj58+AB831UznoULFxZtHx33Y41tbW3s3LmTdDrN5s2bqampYd26dZw8eZKXL1+Oucborpv3799PWLckSSoP74yRJEnT1kSX6ZZ62e7IyAgAzc3NLF++fNy+VVVVE873430zk1EfQDAYJBaLcfjwYW7fvs2dO3dIpVL09PRw+vRpuru7WbNmTcG40WBo7ty5Ja8lSZKmlmGMJEn67Y3uUFm7dm3B5bj/xIIFCwBK2kXzsyKRCJFIhPb2dj5+/Eh7ezvnz5/n4MGD3Lt3r6D/u3fvAP5yr44kSfq1PKYkSZJ+ey0tLQSDQRKJRNFXiX7W0qVLmTFjBk+ePJmE6sZWXV3NqVOnqKio4OHDh0X7PH78mKqqKpYsWTKltUiSpNIZxkiSpN9ebW0t0WiUwcFBdu/ezatXrwr69Pf3E4/HS5pv9uzZ+RedXrx4MSk13rhxo2jg0tXVRS6Xo66uruDf06dPefPmDatWrfJZa0mS/kM8piRJkgRcvHiRwcFB4vE43d3dNDc3U19fz6dPn0in0/T397Nlyxa2bdtW0nybNm3i/v37JJNJ9uzZ86/ri8fj7N27l6amJpYtW0YoFGJgYIBUKkUgEODEiRMFY5LJZL4WSZL03+HOGEmSJCAUCtHV1cX169dZvXo1fX193Lx5k97eXubPn8/Ro0c5c+ZMyfNFo1GCwSCdnZ2TUt+hQ4c4cOAAc+bMoaenh1u3bpHNZtm1axepVIodO3YUjOns7KSyspK2trZJqUGSJE2Oilwul/vVRUiSJP0fbd26lUQiwbNnzwiHw2Vd+/nz5zQ0NLB9+3ZisVhZ15YkSeNzZ4wkSdIUOX78OCMjI5w7d67sa589e5ZAIMCxY8fKvrYkSRqfO2MkSZKmUDQaJRaLMTAwkH/yeqplMhkaGxvZt28fly5dKsuakiSpdIYxkiRJkiRJZeQxJUmSJEmSpDIyjJEkSZIkSSojwxhJkiRJkqQyMoyRJEmSJEkqI8MYSZIkSZKkMjKMkSRJkiRJKiPDGEmSJEmSpDIyjJEkSZIkSSojwxhJkiRJkqQy+gOWs9Kdk8hbqQAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "measure_schedule = calibrations.get('measure', range(config.n_qubits))\n", - "measure_schedule.draw(backend=backend)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This can easily be appended to your own Pulse `Schedule` (`sched += calibrations.get('measure', ) << sched.duration`)!\n", - "\n", - "Likewise, each qubit will have a `Schedule` defined for each basis gate, and they can be appended directly to any `Schedule` you build." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:03.200463Z", - "iopub.status.busy": "2023-08-25T18:26:03.199858Z", - "iopub.status.idle": "2023-08-25T18:26:03.204762Z", - "shell.execute_reply": "2023-08-25T18:26:03.204153Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# You can use `has` to see if an operation is defined. Ex: Does qubit 3 have an x gate defined?\n", - "calibrations.has('x', 3)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:03.208015Z", - "iopub.status.busy": "2023-08-25T18:26:03.207557Z", - "iopub.status.idle": "2023-08-25T18:26:03.213089Z", - "shell.execute_reply": "2023-08-25T18:26:03.212535Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Schedule((0, ShiftPhase(-3.1415, DriveChannel(0))), (0, ShiftPhase(-3.1415, ControlChannel(1))), name=\"u1\")" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Some circuit operations take parameters. U1 takes a rotation angle:\n", - "calibrations.get('u1', 0, P0=3.1415)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "While building your schedule, you can also use `calibrations.add(name, qubits, schedule)` to store useful `Schedule`s that you've made yourself.\n", - "\n", - "On this [page](07_pulse_scheduler.ipynb), we'll show how to schedule `QuantumCircuit`s into Pulse `Schedule`s." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": { - "execution": { - "iopub.execute_input": "2023-08-25T18:26:03.216531Z", - "iopub.status.busy": "2023-08-25T18:26:03.216149Z", - "iopub.status.idle": "2023-08-25T18:26:03.307875Z", - "shell.execute_reply": "2023-08-25T18:26:03.306848Z" - } - }, - "outputs": [ - { - "data": { - "text/html": [ - "

Version Information

SoftwareVersion
qiskitNone
qiskit-terra0.45.0
qiskit_aer0.12.2
System information
Python version3.8.17
Python compilerGCC 11.3.0
Python builddefault, Jun 7 2023 12:29:56
OSLinux
CPUs2
Memory (Gb)6.7694854736328125
Fri Aug 25 18:26:03 2023 UTC
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.

" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import qiskit.tools.jupyter\n", - "%qiskit_version_table\n", - "%qiskit_copyright" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "162b5a3bb512479f872f850c1993ab82": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "93319d06a08845b6a804b41843dda562": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_ba8945cef5a348699840ff369b8b7d0b", - "placeholder": "​", - "style": "IPY_MODEL_162b5a3bb512479f872f850c1993ab82", - "tabbable": null, - "tooltip": null, - "value": "

Circuit Properties

" - } - }, - "ba8945cef5a348699840ff369b8b7d0b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": "0px 0px 10px 0px", - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/tutorials/circuits_advanced/pulse_modulation.png b/docs/tutorials/circuits_advanced/pulse_modulation.png deleted file mode 100644 index 8e8ebd746858..000000000000 Binary files a/docs/tutorials/circuits_advanced/pulse_modulation.png and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index 25ff0a5dade7..154754c224b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,6 +2,134 @@ requires = ["setuptools", "wheel", "setuptools-rust"] build-backend = "setuptools.build_meta" +[project] +name = "qiskit" +description = "An open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives." +requires-python = ">=3.8" +license = { file = "LICENSE.txt" } +authors = [ + { name = "Qiskit Development Team", email = "qiskit@us.ibm.com" }, +] +keywords = [ + "qiskit", + "quantum circuit", + "quantum computing", + "quantum programming language", + "quantum", + "sdk", +] +classifiers = [ + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering", +] +# These are configured in the `tool.setuptools.dynamic` table. +dynamic = ["version", "readme", "dependencies"] + +# If modifying this table, be sure to sync with `requirements-optional.txt` and +# `qiskit.utils.optionals`. +[project.optional-dependencies] +qasm3-import = [ + "qiskit-qasm3-import >= 0.1.0", +] +visualization = [ + "matplotlib >= 3.3", + "ipywidgets >= 7.3.0", + "pydot", + "Pillow >= 4.2.1", + "pylatexenc >= 1.4", + "seaborn >= 0.9.0", +] +crosstalk-pass = [ + "z3-solver >= 4.7", +] +csp-layout-pass = [ + "python-constraint >= 1.4", +] +# This will make the resolution work for installers from PyPI, but `pip install .[all]` will be +# unreliable because `qiskit` will resolve to the PyPI version, so local changes in the +# optionals won't be reflected. +all = ["qiskit[qasm3-import,visualization,crosstalk-pass,csp-layout-pass]"] + +[project.urls] +Homepage = "https://qiskit.org" +Documentation = "https://qiskit.org/documentation" +Repository = "https://github.com/Qiskit/qiskit" +Issues = "https://github.com/Qiskit/qiskit/issues" +Changelog = "https://qiskit.org/documentation/release_notes.html" + +[project.entry-points."qiskit.unitary_synthesis"] +default = "qiskit.transpiler.passes.synthesis.unitary_synthesis:DefaultUnitarySynthesis" +aqc = "qiskit.transpiler.synthesis.aqc.aqc_plugin:AQCSynthesisPlugin" +sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevSynthesis" + +[project.entry-points."qiskit.synthesis"] +"clifford.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford" +"clifford.ag" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford" +"clifford.bm" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford" +"clifford.greedy" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford" +"clifford.layers" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford" +"clifford.lnn" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford" +"linear_function.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction" +"linear_function.kms" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction" +"linear_function.pmh" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction" +"permutation.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation" +"permutation.kms" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation" +"permutation.basic" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation" +"permutation.acg" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation" + +[project.entry-points."qiskit.transpiler.init"] +default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager" + +[project.entry-points."qiskit.transpiler.translation"] +synthesis = "qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager" +translator = "qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager" +unroller = "qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager" + +[project.entry-points."qiskit.transpiler.routing"] +basic = "qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager" +lookahead = "qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager" +none = "qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager" +sabre = "qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager" +stochastic = "qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager" + +[project.entry-points."qiskit.transpiler.optimization"] +default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager" + +[project.entry-points."qiskit.transpiler.layout"] +default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager" +dense = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager" +noise_adaptive = "qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager" +sabre = "qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager" +trivial = "qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager" + +[project.entry-points."qiskit.transpiler.scheduling"] +alap = "qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager" +asap = "qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager" +default = "qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.dynamic] +version = { file = "qiskit/VERSION.txt" } +readme = { file = "README.md", content-type = "text/markdown" } +dependencies = {file = "requirements.txt" } + +[tool.setuptools.packages.find] +include = ["qiskit", "qiskit.*"] + [tool.black] line-length = 100 target-version = ['py38', 'py39', 'py310', 'py311'] @@ -10,7 +138,7 @@ target-version = ['py38', 'py39', 'py310', 'py311'] manylinux-x86_64-image = "manylinux2014" manylinux-i686-image = "manylinux2014" skip = "pp* cp36-* cp37-* *musllinux*" -test-skip = "cp310-win32 cp310-manylinux_i686 cp311-win32 cp311-manylinux_i686" +test-skip = "*win32 *linux_i686" test-command = "python {project}/examples/python/stochastic_swap.py" # We need to use pre-built versions of Numpy and Scipy in the tests; they have a # tendency to crash if they're installed from source by `pip install`, and since @@ -25,6 +153,7 @@ environment = 'PATH="$PATH:$HOME/.cargo/bin" CARGO_NET_GIT_FETCH_WITH_CLI="true" repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel} && pipx run abi3audit --strict --report {wheel}" [tool.cibuildwheel.macos] +environment = "MACOSX_DEPLOYMENT_TARGET=10.12" repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} && pipx run abi3audit --strict --report {wheel}" [tool.cibuildwheel.windows] diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 20f2e3c80851..664cd8c57c5c 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -14,7 +14,6 @@ """Main Qiskit public functionality.""" -import pkgutil import sys import warnings @@ -26,6 +25,7 @@ # We manually define them on import so people can directly import qiskit._accelerate.* submodules # and not have to rely on attribute access. No action needed for top-level extension packages. sys.modules["qiskit._accelerate.nlayout"] = qiskit._accelerate.nlayout +sys.modules["qiskit._accelerate.quantum_circuit"] = qiskit._accelerate.quantum_circuit sys.modules["qiskit._accelerate.stochastic_swap"] = qiskit._accelerate.stochastic_swap sys.modules["qiskit._accelerate.sabre_swap"] = qiskit._accelerate.sabre_swap sys.modules["qiskit._accelerate.sabre_layout"] = qiskit._accelerate.sabre_layout @@ -69,14 +69,6 @@ import qiskit.circuit.measure import qiskit.circuit.reset -# Allow extending this namespace. Please note that currently this line needs -# to be placed *before* the wrapper imports or any non-import code AND *before* -# importing the package you want to allow extensions for (in this case `backends`). - -# Support for the deprecated extending this namespace. -# Remove this after 0.46.0 release -__path__ = pkgutil.extend_path(__path__, __name__) - # Please note these are global instances, not modules. from qiskit.providers.basicaer import BasicAer @@ -88,10 +80,6 @@ from qiskit.compiler import transpile, assemble, schedule, sequence from .version import __version__ -from .version import QiskitVersion - - -__qiskit_version__ = QiskitVersion() class AerWrapper: diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py deleted file mode 100644 index 18767f1fdd01..000000000000 --- a/qiskit/algorithms/__init__.py +++ /dev/null @@ -1,430 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -===================================== -Algorithms (:mod:`qiskit.algorithms`) -===================================== - -.. deprecated:: 0.25.0 - - The :mod:`qiskit.algorithms` module has been migrated to an independent package: - https://github.com/qiskit-community/qiskit-algorithms. - The current import path is deprecated and will be removed no earlier - than 3 months after the release date. If your code uses primitives, you can run - ``pip install qiskit_algorithms`` and import ``from qiskit_algorithms`` instead. - If you use opflow/quantum instance-based algorithms, please update your code to - use primitives following: https://qisk.it/algo_migration before migrating to - the new package. - -It contains a collection of quantum algorithms, for use with quantum computers, to -carry out research and investigate how to solve problems in different domains on -near-term quantum devices with short depth circuits. - -Algorithms configuration includes the use of :mod:`~qiskit.algorithms.optimizers` which -were designed to be swappable sub-parts of an algorithm. Any component and may be exchanged for -a different implementation of the same component type in order to potentially alter the behavior -and outcome of the algorithm. - -Quantum algorithms are run via a :class:`~qiskit.algorithms.QuantumInstance` -which must be set with the -desired backend where the algorithm's circuits will be executed and be configured with a number of -compile and runtime parameters controlling circuit compilation and execution. It ultimately uses -`Terra `__ for the actual compilation and execution of the quantum -circuits created by the algorithm and its components. - -.. currentmodule:: qiskit.algorithms - -Algorithms -========== - -It contains a variety of quantum algorithms and these have been grouped by logical function such -as minimum eigensolvers and amplitude amplifiers. - - -Amplitude Amplifiers --------------------- - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - AmplificationProblem - AmplitudeAmplifier - Grover - GroverResult - - -Amplitude Estimators --------------------- - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - AmplitudeEstimator - AmplitudeEstimatorResult - AmplitudeEstimation - AmplitudeEstimationResult - EstimationProblem - FasterAmplitudeEstimation - FasterAmplitudeEstimationResult - IterativeAmplitudeEstimation - IterativeAmplitudeEstimationResult - MaximumLikelihoodAmplitudeEstimation - MaximumLikelihoodAmplitudeEstimationResult - - -Eigensolvers ------------- - -Algorithms to find eigenvalues of an operator. For chemistry these can be used to find excited -states of a molecule, and ``qiskit-nature`` has some algorithms that leverage chemistry specific -knowledge to do this in that application domain. - -Primitive-based Eigensolvers -++++++++++++++++++++++++++++ - -These algorithms are based on the Qiskit Primitives, a new execution paradigm that replaces the use -of :class:`.QuantumInstance` in algorithms. To ensure continued support and development, we recommend -using the primitive-based Eigensolvers in place of the legacy :class:`.QuantumInstance`-based ones. - -.. autosummary:: - :toctree: ../stubs/ - - eigensolvers - - -Legacy Eigensolvers -+++++++++++++++++++ - -These algorithms, still based on the :class:`.QuantumInstance`, are superseded -by the primitive-based versions in the section above but are still supported for now. - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - Eigensolver - EigensolverResult - NumPyEigensolver - VQD - VQDResult - - -Time Evolvers -------------- - -Algorithms to evolve quantum states in time. Both real and imaginary time evolution is possible -with algorithms that support them. For machine learning, Quantum Imaginary Time Evolution might be -used to train Quantum Boltzmann Machine Neural Networks for example. - -Primitive-based Time Evolvers -+++++++++++++++++++++++++++++ - -These algorithms are based on the Qiskit Primitives, a new execution paradigm that replaces the use -of :class:`.QuantumInstance` in algorithms. To ensure continued support and development, we recommend -using the primitive-based Time Evolvers in place of the legacy :class:`.QuantumInstance`-based ones. - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - RealTimeEvolver - ImaginaryTimeEvolver - TimeEvolutionResult - TimeEvolutionProblem - PVQD - PVQDResult - SciPyImaginaryEvolver - SciPyRealEvolver - VarQITE - VarQRTE - -Legacy Time Evolvers -++++++++++++++++++++ - -These algorithms, still based on the :class:`.QuantumInstance`, are superseded -by the primitive-based versions in the section above but are still supported for now. - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - RealEvolver - ImaginaryEvolver - TrotterQRTE - EvolutionResult - EvolutionProblem - - -Variational Quantum Time Evolution -++++++++++++++++++++++++++++++++++ - -Classes used by variational quantum time evolution algorithms - :class:`.VarQITE` and -:class:`.VarQRTE`. - -.. autosummary:: - :toctree: ../stubs/ - - time_evolvers.variational - - -Trotterization-based Quantum Real Time Evolution -++++++++++++++++++++++++++++++++++++++++++++++++ - -Package for primitives-enabled Trotterization-based quantum time evolution -algorithm - :class:`~.time_evolvers.TrotterQRTE`. - -.. autosummary:: - :toctree: ../stubs/ - - time_evolvers.trotterization - - -Gradients ----------- - -Algorithms to calculate the gradient of a quantum circuit. - -.. autosummary:: - :toctree: ../stubs/ - - gradients - - -Minimum Eigensolvers ---------------------- - -Algorithms that can find the minimum eigenvalue of an operator. - -Primitive-based Minimum Eigensolvers -++++++++++++++++++++++++++++++++++++ - -These algorithms are based on the Qiskit Primitives, a new execution paradigm that replaces the use -of :class:`.QuantumInstance` in algorithms. To ensure continued support and development, we recommend -using the primitive-based Minimum Eigensolvers in place of the legacy :class:`.QuantumInstance`-based -ones. - -.. autosummary:: - :toctree: ../stubs/ - - minimum_eigensolvers - - -Legacy Minimum Eigensolvers -+++++++++++++++++++++++++++ - -These algorithms, still based on the :class:`.QuantumInstance`, are superseded -by the primitive-based versions in the section above but are still supported for now. - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - MinimumEigensolver - MinimumEigensolverResult - NumPyMinimumEigensolver - QAOA - VQE - - -Optimizers ----------- - -Classical optimizers for use by quantum variational algorithms. - -.. autosummary:: - :toctree: ../stubs/ - - optimizers - - -Phase Estimators ----------------- - -Algorithms that estimate the phases of eigenstates of a unitary. - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - HamiltonianPhaseEstimation - HamiltonianPhaseEstimationResult - PhaseEstimationScale - PhaseEstimation - PhaseEstimationResult - IterativePhaseEstimation - - -State Fidelities ----------------- - -Algorithms that compute the fidelity of pairs of quantum states. - -.. autosummary:: - :toctree: ../stubs/ - - state_fidelities - - -Exceptions ----------- - -.. autoexception:: AlgorithmError - -Utility classes ---------------- - -Utility classes used by algorithms (mainly for type-hinting purposes). - -.. autosummary:: - :toctree: ../stubs/ - - AlgorithmJob - -Utility functions ------------------ - -Utility functions used by algorithms. - -.. autofunction:: eval_observables -.. autofunction:: estimate_observables - -""" -import warnings - -from .algorithm_job import AlgorithmJob -from .algorithm_result import AlgorithmResult -from .evolvers import EvolutionResult, EvolutionProblem -from .evolvers.real_evolver import RealEvolver -from .evolvers.imaginary_evolver import ImaginaryEvolver -from .variational_algorithm import VariationalAlgorithm, VariationalResult -from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem, AmplitudeAmplifier -from .amplitude_estimators import ( - AmplitudeEstimator, - AmplitudeEstimatorResult, - AmplitudeEstimation, - AmplitudeEstimationResult, - FasterAmplitudeEstimation, - FasterAmplitudeEstimationResult, - IterativeAmplitudeEstimation, - IterativeAmplitudeEstimationResult, - MaximumLikelihoodAmplitudeEstimation, - MaximumLikelihoodAmplitudeEstimationResult, - EstimationProblem, -) -from .eigen_solvers import NumPyEigensolver, Eigensolver, EigensolverResult, VQD, VQDResult -from .minimum_eigen_solvers import ( - VQE, - VQEResult, - QAOA, - NumPyMinimumEigensolver, - MinimumEigensolver, - MinimumEigensolverResult, -) -from .phase_estimators import ( - HamiltonianPhaseEstimation, - HamiltonianPhaseEstimationResult, - PhaseEstimationScale, - PhaseEstimation, - PhaseEstimationResult, - IterativePhaseEstimation, -) -from .exceptions import AlgorithmError -from .aux_ops_evaluator import eval_observables -from .observables_evaluator import estimate_observables -from .evolvers.trotterization import TrotterQRTE - -from .time_evolvers import ( - ImaginaryTimeEvolver, - RealTimeEvolver, - TimeEvolutionProblem, - TimeEvolutionResult, - PVQD, - PVQDResult, - SciPyImaginaryEvolver, - SciPyRealEvolver, - VarQITE, - VarQRTE, - VarQTE, - VarQTEResult, -) - -__all__ = [ - "AlgorithmJob", - "AlgorithmResult", - "VariationalAlgorithm", - "VariationalResult", - "AmplitudeAmplifier", - "AmplificationProblem", - "Grover", - "GroverResult", - "AmplitudeEstimator", - "AmplitudeEstimatorResult", - "AmplitudeEstimation", - "AmplitudeEstimationResult", - "FasterAmplitudeEstimation", - "FasterAmplitudeEstimationResult", - "IterativeAmplitudeEstimation", - "IterativeAmplitudeEstimationResult", - "MaximumLikelihoodAmplitudeEstimation", - "MaximumLikelihoodAmplitudeEstimationResult", - "EstimationProblem", - "NumPyEigensolver", - "RealEvolver", - "ImaginaryEvolver", - "RealTimeEvolver", - "ImaginaryTimeEvolver", - "TrotterQRTE", - "EvolutionResult", - "EvolutionProblem", - "TimeEvolutionResult", - "TimeEvolutionProblem", - "Eigensolver", - "EigensolverResult", - "VQE", - "VQEResult", - "QAOA", - "NumPyMinimumEigensolver", - "MinimumEigensolver", - "MinimumEigensolverResult", - "HamiltonianPhaseEstimation", - "HamiltonianPhaseEstimationResult", - "VQD", - "VQDResult", - "PhaseEstimationScale", - "PhaseEstimation", - "PhaseEstimationResult", - "PVQD", - "PVQDResult", - "SciPyRealEvolver", - "SciPyImaginaryEvolver", - "IterativePhaseEstimation", - "AlgorithmError", - "eval_observables", - "estimate_observables", - "VarQITE", - "VarQRTE", - "VarQTE", - "VarQTEResult", -] - -warnings.warn( - "``qiskit.algorithms`` has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. " - "The ``qiskit.algorithms`` import path is deprecated as of qiskit-terra 0.25.0 and " - "will be removed no earlier than 3 months after the release date. " - "Please run ``pip install qiskit_algorithms`` and use ``import qiskit_algorithms`` instead.", - category=DeprecationWarning, - stacklevel=2, -) diff --git a/qiskit/algorithms/algorithm_job.py b/qiskit/algorithms/algorithm_job.py deleted file mode 100644 index 16db4df93dfc..000000000000 --- a/qiskit/algorithms/algorithm_job.py +++ /dev/null @@ -1,24 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -AlgorithmJob class -""" -from qiskit.primitives.primitive_job import PrimitiveJob - - -class AlgorithmJob(PrimitiveJob): - """ - This empty class is introduced for typing purposes. - """ - - pass diff --git a/qiskit/algorithms/algorithm_result.py b/qiskit/algorithms/algorithm_result.py deleted file mode 100644 index 0804303a4ef6..000000000000 --- a/qiskit/algorithms/algorithm_result.py +++ /dev/null @@ -1,65 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -This module implements the abstract base class for algorithm results. -""" - -from abc import ABC -import inspect -import pprint - - -class AlgorithmResult(ABC): - """Abstract Base Class for algorithm results.""" - - def __str__(self) -> str: - result = {} - for name, value in inspect.getmembers(self): - if ( - not name.startswith("_") - and not inspect.ismethod(value) - and not inspect.isfunction(value) - and hasattr(self, name) - ): - - result[name] = value - - return pprint.pformat(result, indent=4) - - def combine(self, result: "AlgorithmResult") -> None: - """ - Any property from the argument that exists in the receiver is - updated. - Args: - result: Argument result with properties to be set. - Raises: - TypeError: Argument is None - """ - if result is None: - raise TypeError("Argument result expected.") - if result == self: - return - - # find any result public property that exists in the receiver - for name, value in inspect.getmembers(result): - if ( - not name.startswith("_") - and not inspect.ismethod(value) - and not inspect.isfunction(value) - and hasattr(self, name) - ): - try: - setattr(self, name, value) - except AttributeError: - # some attributes may be read only - pass diff --git a/qiskit/algorithms/amplitude_amplifiers/__init__.py b/qiskit/algorithms/amplitude_amplifiers/__init__.py deleted file mode 100644 index bc45f18106bd..000000000000 --- a/qiskit/algorithms/amplitude_amplifiers/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Amplitude Amplifiers Package""" - -from .amplitude_amplifier import AmplitudeAmplifier, AmplitudeAmplifierResult -from .amplification_problem import AmplificationProblem -from .grover import Grover, GroverResult - -__all__ = [ - "AmplitudeAmplifier", - "AmplitudeAmplifierResult", - "AmplificationProblem", - "Grover", - "GroverResult", -] diff --git a/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py b/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py deleted file mode 100644 index 67b20751c417..000000000000 --- a/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py +++ /dev/null @@ -1,213 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Amplification problem class.""" -from __future__ import annotations - -from collections.abc import Callable -from typing import Any - -from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library import GroverOperator -from qiskit.quantum_info import Statevector - - -class AmplificationProblem: - """The amplification problem is the input to amplitude amplification algorithms, like Grover. - - This class contains all problem-specific information required to run an amplitude amplification - algorithm. It minimally contains the Grover operator. It can further hold some post processing - on the optimal bitstring. - """ - - def __init__( - self, - oracle: QuantumCircuit | Statevector, - state_preparation: QuantumCircuit | None = None, - grover_operator: QuantumCircuit | None = None, - post_processing: Callable[[str], Any] | None = None, - objective_qubits: int | list[int] | None = None, - is_good_state: Callable[[str], bool] | list[int] | list[str] | Statevector | None = None, - ) -> None: - r""" - Args: - oracle: The oracle reflecting about the bad states. - state_preparation: A circuit preparing the input state, referred to as - :math:`\mathcal{A}`. If None, a layer of Hadamard gates is used. - grover_operator: The Grover operator :math:`\mathcal{Q}` used as unitary in the - phase estimation circuit. If None, this operator is constructed from the ``oracle`` - and ``state_preparation``. - post_processing: A mapping applied to the most likely bitstring. - objective_qubits: If set, specifies the indices of the qubits that should be measured. - If None, all qubits will be measured. The ``is_good_state`` function will be - applied on the measurement outcome of these qubits. - is_good_state: A function to check whether a string represents a good state. By default - if the ``oracle`` argument has an ``evaluate_bitstring`` method (currently only - provided by the :class:`~qiskit.circuit.library.PhaseOracle` class) this will be - used, otherwise this kwarg is required and **must** be specified. - """ - self._oracle = oracle - self._state_preparation = state_preparation - self._grover_operator = grover_operator - self._post_processing = post_processing - self._objective_qubits = objective_qubits - if is_good_state is not None: - self._is_good_state = is_good_state - elif hasattr(oracle, "evaluate_bitstring"): - self._is_good_state = oracle.evaluate_bitstring - else: - self._is_good_state = None - - @property - def oracle(self) -> QuantumCircuit | Statevector: - """Return the oracle. - - Returns: - The oracle. - """ - return self._oracle - - @oracle.setter - def oracle(self, oracle: QuantumCircuit | Statevector) -> None: - """Set the oracle. - - Args: - oracle: The oracle. - """ - self._oracle = oracle - - @property - def state_preparation(self) -> QuantumCircuit: - r"""Get the state preparation operator :math:`\mathcal{A}`. - - Returns: - The :math:`\mathcal{A}` operator as `QuantumCircuit`. - """ - if self._state_preparation is None: - state_preparation = QuantumCircuit(self.oracle.num_qubits) - state_preparation.h(state_preparation.qubits) - return state_preparation - - return self._state_preparation - - @state_preparation.setter - def state_preparation(self, state_preparation: QuantumCircuit | None) -> None: - r"""Set the :math:`\mathcal{A}` operator. If None, a layer of Hadamard gates is used. - - Args: - state_preparation: The new :math:`\mathcal{A}` operator or None. - """ - self._state_preparation = state_preparation - - @property - def post_processing(self) -> Callable[[str], Any]: - """Apply post processing to the input value. - - Returns: - A handle to the post processing function. Acts as identity by default. - """ - if self._post_processing is None: - return lambda x: x - - return self._post_processing - - @post_processing.setter - def post_processing(self, post_processing: Callable[[str], Any]) -> None: - """Set the post processing function. - - Args: - post_processing: A handle to the post processing function. - """ - self._post_processing = post_processing - - @property - def objective_qubits(self) -> list[int]: - """The indices of the objective qubits. - - Returns: - The indices of the objective qubits as list of integers. - """ - if self._objective_qubits is None: - return list(range(self.oracle.num_qubits)) - - if isinstance(self._objective_qubits, int): - return [self._objective_qubits] - - return self._objective_qubits - - @objective_qubits.setter - def objective_qubits(self, objective_qubits: int | list[int] | None) -> None: - """Set the objective qubits. - - Args: - objective_qubits: The indices of the qubits that should be measured. - If None, all qubits will be measured. The ``is_good_state`` function will be - applied on the measurement outcome of these qubits. - """ - self._objective_qubits = objective_qubits - - @property - def is_good_state(self) -> Callable[[str], bool]: - """Check whether a provided bitstring is a good state or not. - - Returns: - A callable that takes in a bitstring and returns True if the measurement is a good - state, False otherwise. - """ - if (self._is_good_state is None) or callable(self._is_good_state): - return self._is_good_state # returns None if no is_good_state arg has been set - elif isinstance(self._is_good_state, list): - if all(isinstance(good_bitstr, str) for good_bitstr in self._is_good_state): - return lambda bitstr: bitstr in self._is_good_state - else: - return lambda bitstr: all( - bitstr[good_index] == "1" for good_index in self._is_good_state - ) - - return lambda bitstr: bitstr in self._is_good_state.probabilities_dict() - - @is_good_state.setter - def is_good_state( - self, is_good_state: Callable[[str], bool] | list[int] | list[str] | Statevector - ) -> None: - """Set the ``is_good_state`` function. - - Args: - is_good_state: A function to determine whether a bitstring represents a good state. - """ - self._is_good_state = is_good_state - - @property - def grover_operator(self) -> QuantumCircuit | None: - r"""Get the :math:`\mathcal{Q}` operator, or Grover operator. - - If the Grover operator is not set, we try to build it from the :math:`\mathcal{A}` operator - and `objective_qubits`. This only works if `objective_qubits` is a list of integers. - - Returns: - The Grover operator, or None if neither the Grover operator nor the - :math:`\mathcal{A}` operator is set. - """ - if self._grover_operator is None: - return GroverOperator(self.oracle, self.state_preparation) - return self._grover_operator - - @grover_operator.setter - def grover_operator(self, grover_operator: QuantumCircuit | None) -> None: - r"""Set the :math:`\mathcal{Q}` operator. - - If None, this operator is constructed from the ``oracle`` and ``state_preparation``. - - Args: - grover_operator: The new :math:`\mathcal{Q}` operator or None. - """ - self._grover_operator = grover_operator diff --git a/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py b/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py deleted file mode 100644 index 33ef90cb624e..000000000000 --- a/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py +++ /dev/null @@ -1,127 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The interface for amplification algorithms and results.""" -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Any - -import numpy as np - -from .amplification_problem import AmplificationProblem -from ..algorithm_result import AlgorithmResult - - -class AmplitudeAmplifier(ABC): - """The interface for amplification algorithms.""" - - @abstractmethod - def amplify(self, amplification_problem: AmplificationProblem) -> "AmplitudeAmplifierResult": - """Run the amplification algorithm. - - Args: - amplification_problem: The amplification problem. - - Returns: - The result as a ``AmplificationResult``, where e.g. the most likely state can be queried - as ``result.top_measurement``. - """ - raise NotImplementedError - - -class AmplitudeAmplifierResult(AlgorithmResult): - """The amplification result base class.""" - - def __init__(self) -> None: - super().__init__() - self._top_measurement: str | None = None - self._assignment = None - self._oracle_evaluation: bool | None = None - self._circuit_results: list[np.ndarray] | list[dict[str, int]] | None = None - self._max_probability: float | None = None - - @property - def top_measurement(self) -> str | None: - """The most frequently measured output as bitstring. - - Returns: - The most frequently measured output state. - """ - return self._top_measurement - - @top_measurement.setter - def top_measurement(self, value: str) -> None: - """Set the most frequently measured bitstring. - - Args: - value: A new value for the top measurement. - """ - self._top_measurement = value - - @property - def assignment(self) -> Any: - """The post-processed value of the most likely bitstring. - - Returns: - The output of the ``post_processing`` function of the respective - ``AmplificationProblem``, where the input is the ``top_measurement``. The type - is the same as the return type of the post-processing function. - """ - return self._assignment - - @assignment.setter - def assignment(self, value: Any) -> None: - """Set the value for the assignment. - - Args: - value: A new value for the assignment/solution. - """ - self._assignment = value - - @property - def oracle_evaluation(self) -> bool: - """Whether the classical oracle evaluation of the top measurement was True or False. - - Returns: - The classical oracle evaluation of the top measurement. - """ - return self._oracle_evaluation - - @oracle_evaluation.setter - def oracle_evaluation(self, value: bool) -> None: - """Set the classical oracle evaluation of the top measurement. - - Args: - value: A new value for the classical oracle evaluation. - """ - self._oracle_evaluation = value - - @property - def circuit_results(self) -> list[np.ndarray] | list[dict[str, int]] | None: - """Return the circuit results. Can be a statevector or counts dictionary.""" - return self._circuit_results - - @circuit_results.setter - def circuit_results(self, value: list[np.ndarray] | list[dict[str, int]]) -> None: - """Set the circuit results.""" - self._circuit_results = value - - @property - def max_probability(self) -> float: - """Return the maximum sampling probability.""" - return self._max_probability - - @max_probability.setter - def max_probability(self, value: float) -> None: - """Set the maximum sampling probability.""" - self._max_probability = value diff --git a/qiskit/algorithms/amplitude_amplifiers/grover.py b/qiskit/algorithms/amplitude_amplifiers/grover.py deleted file mode 100644 index be04929afa03..000000000000 --- a/qiskit/algorithms/amplitude_amplifiers/grover.py +++ /dev/null @@ -1,449 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Grover's search algorithm.""" -from __future__ import annotations - -import itertools -import operator -import warnings -from collections.abc import Iterator, Generator -from typing import Any - -import numpy as np - -from qiskit import ClassicalRegister, QuantumCircuit -from qiskit.algorithms.exceptions import AlgorithmError -from qiskit.primitives import BaseSampler -from qiskit.providers import Backend -from qiskit.quantum_info import partial_trace, Statevector -from qiskit.utils import QuantumInstance, algorithm_globals -from qiskit.utils.deprecation import deprecate_arg, deprecate_func - -from .amplification_problem import AmplificationProblem -from .amplitude_amplifier import AmplitudeAmplifier, AmplitudeAmplifierResult - - -class Grover(AmplitudeAmplifier): - r"""Grover's Search algorithm. - - .. note:: - - If you want to learn more about the theory behind Grover's Search algorithm, check - out the `Qiskit Textbook `_. - or the `Qiskit Tutorials - `_ - for more concrete how-to examples. - - Grover's Search [1, 2] is a well known quantum algorithm that can be used for - searching through unstructured collections of records for particular targets - with quadratic speedup compared to classical algorithms. - - Given a set :math:`X` of :math:`N` elements :math:`X=\{x_1,x_2,\ldots,x_N\}` - and a boolean function :math:`f : X \rightarrow \{0,1\}`, the goal of an - unstructured-search problem is to find an element :math:`x^* \in X` such - that :math:`f(x^*)=1`. - - The search is called *unstructured* because there are no guarantees as to how - the database is ordered. On a sorted database, for instance, one could perform - binary search to find an element in :math:`\mathbb{O}(\log N)` worst-case time. - Instead, in an unstructured-search problem, there is no prior knowledge about - the contents of the database. With classical circuits, there is no alternative - but to perform a linear number of queries to find the target element. - Conversely, Grover's Search algorithm allows to solve the unstructured-search - problem on a quantum computer in :math:`\mathcal{O}(\sqrt{N})` queries. - - To carry out this search a so-called oracle is required, that flags a good element/state. - The action of the oracle :math:`\mathcal{S}_f` is - - .. math:: - - \mathcal{S}_f |x\rangle = (-1)^{f(x)} |x\rangle, - - i.e. it flips the phase of the state :math:`|x\rangle` if :math:`x` is a hit. - The details of how :math:`S_f` works are unimportant to the algorithm; Grover's - search algorithm treats the oracle as a black box. - - This class supports oracles in form of a :class:`~qiskit.circuit.QuantumCircuit`. - - With the given oracle, Grover's Search constructs the Grover operator to amplify the - amplitudes of the good states: - - .. math:: - - \mathcal{Q} = H^{\otimes n} \mathcal{S}_0 H^{\otimes n} \mathcal{S}_f - = D \mathcal{S}_f, - - where :math:`\mathcal{S}_0` flips the phase of the all-zero state and acts as identity - on all other states. Sometimes the first three operands are summarized as diffusion operator, - which implements a reflection over the equal superposition state. - - If the number of solutions is known, we can calculate how often :math:`\mathcal{Q}` should be - applied to find a solution with very high probability, see the method - `optimal_num_iterations`. If the number of solutions is unknown, the algorithm tries different - powers of Grover's operator, see the `iterations` argument, and after each iteration checks - if a good state has been measured using `good_state`. - - The generalization of Grover's Search, Quantum Amplitude Amplification [3], uses a modified - version of :math:`\mathcal{Q}` where the diffusion operator does not reflect about the - equal superposition state, but another state specified via an operator :math:`\mathcal{A}`: - - .. math:: - - \mathcal{Q} = \mathcal{A} \mathcal{S}_0 \mathcal{A}^\dagger \mathcal{S}_f. - - For more information, see the :class:`~qiskit.circuit.library.GroverOperator` in the - circuit library. - - References: - [1]: L. K. Grover (1996), A fast quantum mechanical algorithm for database search, - `arXiv:quant-ph/9605043 `_. - [2]: I. Chuang & M. Nielsen, Quantum Computation and Quantum Information, - Cambridge: Cambridge University Press, 2000. Chapter 6.1.2. - [3]: Brassard, G., Hoyer, P., Mosca, M., & Tapp, A. (2000). - Quantum Amplitude Amplification and Estimation. - `arXiv:quant-ph/0005055 `_. - """ - - @deprecate_arg( - "quantum_instance", - additional_msg=( - "Instead, use the ``sampler`` argument. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - iterations: list[int] | Iterator[int] | int | None = None, - growth_rate: float | None = None, - sample_from_iterations: bool = False, - quantum_instance: QuantumInstance | Backend | None = None, - sampler: BaseSampler | None = None, - ) -> None: - r""" - Args: - iterations: Specify the number of iterations/power of Grover's operator to be checked. - * If an int, only one circuit is run with that power of the Grover operator. - If the number of solutions is known, this option should be used with the optimal - power. The optimal power can be computed with ``Grover.optimal_num_iterations``. - * If a list, all the powers in the list are run in the specified order. - * If an iterator, the powers yielded by the iterator are checked, until a maximum - number of iterations or maximum power is reached. - * If ``None``, the :obj:`AmplificationProblem` provided must have an ``is_good_state``, - and circuits are run until that good state is reached. - growth_rate: If specified, the iterator is set to increasing powers of ``growth_rate``, - i.e. to ``int(growth_rate ** 1), int(growth_rate ** 2), ...`` until a maximum - number of iterations is reached. - sample_from_iterations: If True, instead of taking the values in ``iterations`` as - powers of the Grover operator, a random integer sample between 0 and smaller value - than the iteration is used as a power, see [1], Section 4. - quantum_instance: Deprecated: A Quantum Instance or Backend to run the circuits. - sampler: A Sampler to use for sampling the results of the circuits. - - Raises: - ValueError: If ``growth_rate`` is a float but not larger than 1. - ValueError: If both ``iterations`` and ``growth_rate`` is set. - - References: - [1]: Boyer et al., Tight bounds on quantum searching - ``_ - """ - # set default value - if growth_rate is None and iterations is None: - growth_rate = 1.2 - - if growth_rate is not None and iterations is not None: - raise ValueError("Pass either a value for iterations or growth_rate, not both.") - - if growth_rate is not None: - # yield iterations ** 1, iterations ** 2, etc. and casts to int - self._iterations: Generator[int, None, None] | list[int] = ( - int(growth_rate**x) for x in itertools.count(1) - ) - elif isinstance(iterations, int): - self._iterations = [iterations] - else: - self._iterations = iterations - - if quantum_instance is not None and sampler is not None: - raise ValueError("Only one of quantum_instance or sampler can be passed, not both!") - - # check positionally passing the sampler in the place of quantum_instance - # which will be removed in future - if isinstance(quantum_instance, BaseSampler): - sampler = quantum_instance - quantum_instance = None - - self._quantum_instance: QuantumInstance | None = None - if quantum_instance is not None: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self.quantum_instance = quantum_instance - - self._sampler = sampler - - self._sample_from_iterations = sample_from_iterations - self._iterations_arg = iterations - - @property - @deprecate_func( - since="0.24.0", - is_property=True, - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def quantum_instance(self) -> QuantumInstance | None: - r"""Deprecated. Get the quantum instance. - - Returns: - The quantum instance used to run this algorithm. - """ - return self._quantum_instance - - @quantum_instance.setter - @deprecate_func( - since="0.24.0", - is_property=True, - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: - r"""Deprecated. Set quantum instance. - - Args: - quantum_instance: The quantum instance used to run this algorithm. - """ - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - self._quantum_instance = quantum_instance - - @property - def sampler(self) -> BaseSampler | None: - """Get the sampler. - - Returns: - The sampler used to run this algorithm. - """ - return self._sampler - - @sampler.setter - def sampler(self, sampler: BaseSampler) -> None: - """Set the sampler. - - Args: - sampler: The sampler used to run this algorithm. - """ - self._sampler = sampler - - def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult": - """Run the Grover algorithm. - - Args: - amplification_problem: The amplification problem. - - Returns: - The result as a ``GroverResult``, where e.g. the most likely state can be queried - as ``result.top_measurement``. - - Raises: - ValueError: If a quantum instance or sampler is not set. - AlgorithmError: If a sampler job fails. - TypeError: If ``is_good_state`` is not provided and is required (i.e. when iterations - is ``None`` or a ``list``) - """ - if self._sampler is None and self._quantum_instance is None: - raise ValueError("A quantum instance or sampler must be provided.") - - if self._quantum_instance is not None and self._sampler is not None: - raise ValueError("Only one of quantum_instance or sampler can be passed, not both!") - - if isinstance(self._iterations, list): - max_iterations = len(self._iterations) - max_power = np.inf # no cap on the power - iterator: Iterator[int] = iter(self._iterations) - else: - max_iterations = max(10, 2**amplification_problem.oracle.num_qubits) - max_power = np.ceil( - 2 ** (len(amplification_problem.grover_operator.reflection_qubits) / 2) - ) - iterator = self._iterations - - result = GroverResult() - - iterations = [] - top_measurement = "0" * len(amplification_problem.objective_qubits) - oracle_evaluation = False - all_circuit_results = [] - max_probability = 0 - shots = 0 - - for _ in range(max_iterations): # iterate at most to the max number of iterations - # get next power and check if allowed - power = next(iterator) - - if power > max_power: - break - - iterations.append(power) # store power - - # sample from [0, power) if specified - if self._sample_from_iterations: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - power = algorithm_globals.random.integers(power) - # Run a grover experiment for a given power of the Grover operator. - if self._sampler is not None: - qc = self.construct_circuit(amplification_problem, power, measurement=True) - job = self._sampler.run([qc]) - - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Sampler job failed.") from exc - - num_bits = len(amplification_problem.objective_qubits) - circuit_results: dict[str, Any] | Statevector | np.ndarray = { - np.binary_repr(k, num_bits): v for k, v in results.quasi_dists[0].items() - } - top_measurement, max_probability = max(circuit_results.items(), key=lambda x: x[1]) - - else: # use of else brach instead of elif as this seperates out the deprecated logic - if self._quantum_instance.is_statevector: - qc = self.construct_circuit(amplification_problem, power, measurement=False) - circuit_results = self._quantum_instance.execute(qc).get_statevector() - num_bits = len(amplification_problem.objective_qubits) - - # trace out work qubits - if qc.width() != num_bits: - indices = [ - i - for i in range(qc.num_qubits) - if i not in amplification_problem.objective_qubits - ] - rho = partial_trace(circuit_results, indices) - circuit_results = np.diag(rho.data) - - max_amplitude = max(circuit_results.max(), circuit_results.min(), key=abs) - max_amplitude_idx = np.where(circuit_results == max_amplitude)[0][0] - top_measurement = np.binary_repr(max_amplitude_idx, num_bits) - max_probability = np.abs(max_amplitude) ** 2 - shots = 1 - else: - qc = self.construct_circuit(amplification_problem, power, measurement=True) - circuit_results = self._quantum_instance.execute(qc).get_counts(qc) - top_measurement = max(circuit_results.items(), key=operator.itemgetter(1))[0] - shots = sum(circuit_results.values()) - max_probability = ( - max(circuit_results.items(), key=operator.itemgetter(1))[1] / shots - ) - - all_circuit_results.append(circuit_results) - - if (isinstance(self._iterations_arg, int)) and ( - amplification_problem.is_good_state is None - ): - oracle_evaluation = None # cannot check for good state without is_good_state arg - break - - # is_good_state arg must be provided if iterations arg is not an integer - if ( - self._iterations_arg is None or isinstance(self._iterations_arg, list) - ) and amplification_problem.is_good_state is None: - raise TypeError("An is_good_state function is required with the provided oracle") - - # only check if top measurement is a good state if an is_good_state arg is provided - oracle_evaluation = amplification_problem.is_good_state(top_measurement) - - if oracle_evaluation is True: - break # we found a solution - - result.iterations = iterations - result.top_measurement = top_measurement - result.assignment = amplification_problem.post_processing(top_measurement) - result.oracle_evaluation = oracle_evaluation - result.circuit_results = all_circuit_results - result.max_probability = max_probability - - return result - - @staticmethod - def optimal_num_iterations(num_solutions: int, num_qubits: int) -> int: - """Return the optimal number of iterations, if the number of solutions is known. - - Args: - num_solutions: The number of solutions. - num_qubits: The number of qubits used to encode the states. - - Returns: - The optimal number of iterations for Grover's algorithm to succeed. - """ - amplitude = np.sqrt(num_solutions / 2**num_qubits) - return round(np.arccos(amplitude) / (2 * np.arcsin(amplitude))) - - def construct_circuit( - self, problem: AmplificationProblem, power: int | None = None, measurement: bool = False - ) -> QuantumCircuit: - """Construct the circuit for Grover's algorithm with ``power`` Grover operators. - - Args: - problem: The amplification problem for the algorithm. - power: The number of times the Grover operator is repeated. If None, this argument - is set to the first item in ``iterations``. - measurement: Boolean flag to indicate if measurement should be included in the circuit. - - Returns: - QuantumCircuit: the QuantumCircuit object for the constructed circuit - - Raises: - ValueError: If no power is passed and the iterations are not an integer. - """ - if power is None: - if len(self._iterations) > 1: - raise ValueError("Please pass ``power`` if the iterations are not an integer.") - power = self._iterations[0] - - qc = QuantumCircuit(problem.oracle.num_qubits, name="Grover circuit") - qc.compose(problem.state_preparation, inplace=True) - if power > 0: - qc.compose(problem.grover_operator.power(power), inplace=True) - - if measurement: - measurement_cr = ClassicalRegister(len(problem.objective_qubits)) - qc.add_register(measurement_cr) - qc.measure(problem.objective_qubits, measurement_cr) - - return qc - - -class GroverResult(AmplitudeAmplifierResult): - """Grover Result.""" - - def __init__(self) -> None: - super().__init__() - self._iterations: list[int] | None = None - - @property - def iterations(self) -> list[int]: - """All the powers of the Grover operator that have been tried. - - Returns: - The powers of the Grover operator tested. - """ - return self._iterations - - @iterations.setter - def iterations(self, value: list[int]) -> None: - """Set the powers of the Grover operator that have been tried. - - Args: - value: A new value for the powers. - """ - self._iterations = value diff --git a/qiskit/algorithms/amplitude_estimators/__init__.py b/qiskit/algorithms/amplitude_estimators/__init__.py deleted file mode 100644 index 764f8863857d..000000000000 --- a/qiskit/algorithms/amplitude_estimators/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Amplitude Estimators package.""" - -from .amplitude_estimator import AmplitudeEstimator, AmplitudeEstimatorResult -from .ae import AmplitudeEstimation, AmplitudeEstimationResult -from .fae import FasterAmplitudeEstimation, FasterAmplitudeEstimationResult -from .iae import IterativeAmplitudeEstimation, IterativeAmplitudeEstimationResult -from .mlae import MaximumLikelihoodAmplitudeEstimation, MaximumLikelihoodAmplitudeEstimationResult -from .estimation_problem import EstimationProblem - -__all__ = [ - "AmplitudeEstimator", - "AmplitudeEstimatorResult", - "AmplitudeEstimation", - "AmplitudeEstimationResult", - "FasterAmplitudeEstimation", - "FasterAmplitudeEstimationResult", - "IterativeAmplitudeEstimation", - "IterativeAmplitudeEstimationResult", - "MaximumLikelihoodAmplitudeEstimation", - "MaximumLikelihoodAmplitudeEstimationResult", - "EstimationProblem", -] diff --git a/qiskit/algorithms/amplitude_estimators/ae.py b/qiskit/algorithms/amplitude_estimators/ae.py deleted file mode 100644 index c454cb18793b..000000000000 --- a/qiskit/algorithms/amplitude_estimators/ae.py +++ /dev/null @@ -1,688 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Quantum Phase Estimation-based Amplitude Estimation algorithm.""" - -from __future__ import annotations -from collections import OrderedDict -import warnings -import numpy as np -from scipy.stats import chi2, norm -from scipy.optimize import bisect - -from qiskit import QuantumCircuit, ClassicalRegister -from qiskit.providers import Backend -from qiskit.primitives import BaseSampler -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_arg, deprecate_func -from .amplitude_estimator import AmplitudeEstimator, AmplitudeEstimatorResult -from .ae_utils import pdf_a, derivative_log_pdf_a, bisect_max -from .estimation_problem import EstimationProblem -from ..exceptions import AlgorithmError - - -class AmplitudeEstimation(AmplitudeEstimator): - r"""The Quantum Phase Estimation-based Amplitude Estimation algorithm. - - This class implements the original Quantum Amplitude Estimation (QAE) algorithm, introduced by - [1]. This canonical version uses quantum phase estimation along with a set of :math:`m` - additional evaluation qubits to find an estimate :math:`\tilde{a}`, that is restricted to the - grid - - .. math:: - - \tilde{a} \in \{\sin^2(\pi y / 2^m) : y = 0, ..., 2^{m-1}\} - - More evaluation qubits produce a finer sampling grid, therefore the accuracy of the algorithm - increases with :math:`m`. - - Using a maximum likelihood post processing, this grid constraint can be circumvented. - This improved estimator is implemented as well, see [2] Appendix A for more detail. - - .. note:: - - This class does not support the :attr:`.EstimationProblem.is_good_state` property, - as for phase estimation-based QAE, the oracle that identifes the good states - must be encoded in the Grover operator. To set custom oracles, the - :attr:`.EstimationProblem.grover_operator` attribute can be set directly. - - References: - [1]: Brassard, G., Hoyer, P., Mosca, M., & Tapp, A. (2000). - Quantum Amplitude Amplification and Estimation. - `arXiv:quant-ph/0005055 `_. - [2]: Grinko, D., Gacon, J., Zoufal, C., & Woerner, S. (2019). - Iterative Quantum Amplitude Estimation. - `arXiv:1912.05559 `_. - """ - - @deprecate_arg( - "quantum_instance", - additional_msg=( - "Instead, use the ``sampler`` argument. See https://qisk.it/algo_migration for a " - "migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - num_eval_qubits: int, - phase_estimation_circuit: QuantumCircuit | None = None, - iqft: QuantumCircuit | None = None, - quantum_instance: QuantumInstance | Backend | None = None, - sampler: BaseSampler | None = None, - ) -> None: - r""" - Args: - num_eval_qubits: The number of evaluation qubits. - phase_estimation_circuit: The phase estimation circuit used to run the algorithm. - Defaults to the standard phase estimation circuit from the circuit library, - `qiskit.circuit.library.PhaseEstimation` when None. - iqft: The inverse quantum Fourier transform component, defaults to using a standard - implementation from `qiskit.circuit.library.QFT` when None. - quantum_instance: Deprecated: The backend (or `QuantumInstance`) to execute - the circuits on. - sampler: A sampler primitive to evaluate the circuits. - - Raises: - ValueError: If the number of evaluation qubits is smaller than 1. - """ - if num_eval_qubits < 1: - raise ValueError("The number of evaluation qubits must at least be 1.") - - super().__init__() - - # set quantum instance - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self.quantum_instance = quantum_instance - - # get parameters - self._m = num_eval_qubits - self._M = 2**num_eval_qubits # pylint: disable=invalid-name - - self._iqft = iqft - self._pec = phase_estimation_circuit - self._sampler = sampler - - @property - def sampler(self) -> BaseSampler | None: - """Get the sampler primitive. - - Returns: - The sampler primitive to evaluate the circuits. - """ - return self._sampler - - @sampler.setter - def sampler(self, sampler: BaseSampler) -> None: - """Set sampler primitive. - - Args: - sampler: A sampler primitive to evaluate the circuits. - """ - self._sampler = sampler - - @property - @deprecate_func( - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - since="0.24.0", - is_property=True, - ) - def quantum_instance(self) -> QuantumInstance | None: - """Deprecated: Get the quantum instance. - - Returns: - The quantum instance used to run this algorithm. - """ - return self._quantum_instance - - @quantum_instance.setter - @deprecate_func( - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - since="0.24.0", - is_property=True, - ) - def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: - """Deprecated: Set quantum instance. - - Args: - quantum_instance: The quantum instance used to run this algorithm. - """ - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - self._quantum_instance = quantum_instance - - def construct_circuit( - self, estimation_problem: EstimationProblem, measurement: bool = False - ) -> QuantumCircuit: - """Construct the Amplitude Estimation quantum circuit. - - Args: - estimation_problem: The estimation problem for which to construct the QAE circuit. - measurement: Boolean flag to indicate if measurements should be included in the circuit. - - Returns: - The QuantumCircuit object for the constructed circuit. - """ - # use custom Phase Estimation circuit if provided - if self._pec is not None: - pec = self._pec - - # otherwise use the circuit library -- note that this does not include the A operator - else: - from qiskit.circuit.library import PhaseEstimation - - pec = PhaseEstimation(self._m, estimation_problem.grover_operator, iqft=self._iqft) - - # combine the Phase Estimation circuit with the A operator - circuit = QuantumCircuit(*pec.qregs) - circuit.compose( - estimation_problem.state_preparation, - list(range(self._m, circuit.num_qubits)), - inplace=True, - ) - circuit.compose(pec, inplace=True) - - # add measurements if necessary - if measurement: - cr = ClassicalRegister(self._m) - circuit.add_register(cr) - circuit.measure(list(range(self._m)), list(range(self._m))) - - return circuit - - def evaluate_measurements( - self, - circuit_results: dict[str, int] | np.ndarray, - threshold: float = 1e-6, - ) -> tuple[dict[float, float], dict[int, float]]: - """Evaluate the results from the circuit simulation. - - Given the probabilities from statevector simulation of the QAE circuit, compute the - probabilities that the measurements y/gridpoints a are the best estimate. - - Args: - circuit_results: The circuit result from the QAE circuit. Can be either a counts dict - or a statevector or a quasi-probabilities dict. - threshold: Measurements with probabilities below the threshold are discarded. - - Returns: - Dictionaries containing the a gridpoints with respective probabilities and - y measurements with respective probabilities, in this order. - """ - # compute grid sample and measurement dicts - if isinstance(circuit_results, dict): - if set(map(type, circuit_results.values())) == {int}: - samples, measurements = self._evaluate_count_results(circuit_results) - else: - samples, measurements = self._evaluate_quasi_probabilities_results(circuit_results) - else: - samples, measurements = self._evaluate_statevector_results(circuit_results) - - # cutoff probabilities below the threshold - samples = {a: p for a, p in samples.items() if p > threshold} - measurements = {y: p for y, p in measurements.items() if p > threshold} - - return samples, measurements - - def _evaluate_statevector_results(self, statevector): - # map measured results to estimates - measurements = OrderedDict() # type: OrderedDict - num_qubits = int(np.log2(len(statevector))) - for i, amplitude in enumerate(statevector): - b = bin(i)[2:].zfill(num_qubits)[::-1] - y = int(b[: self._m], 2) # chop off all except the evaluation qubits - measurements[y] = measurements.get(y, 0) + np.abs(amplitude) ** 2 - - samples = OrderedDict() # type: OrderedDict - for y, probability in measurements.items(): - if y >= int(self._M / 2): - y = self._M - y - # due to the finite accuracy of the sine, we round the result to 7 decimals - a = np.round(np.power(np.sin(y * np.pi / 2**self._m), 2), decimals=7) - samples[a] = samples.get(a, 0) + probability - - return samples, measurements - - def _evaluate_quasi_probabilities_results(self, circuit_results): - # construct probabilities - measurements = OrderedDict() - samples = OrderedDict() - for state, probability in circuit_results.items(): - # reverts the last _m items - y = int(state[: -self._m - 1 : -1], 2) - measurements[y] = probability - a = np.round(np.power(np.sin(y * np.pi / 2**self._m), 2), decimals=7) - samples[a] = samples.get(a, 0.0) + probability - - return samples, measurements - - def _evaluate_count_results(self, counts) -> tuple[dict[float, float], dict[int, float]]: - # construct probabilities - measurements: dict[int, float] = OrderedDict() - samples: dict[float, float] = OrderedDict() - shots = sum(counts.values()) - for state, count in counts.items(): - y = int(state.replace(" ", "")[: self._m][::-1], 2) - probability = count / shots - measurements[y] = probability - a = np.round(np.power(np.sin(y * np.pi / 2**self._m), 2), decimals=7) - samples[a] = samples.get(a, 0.0) + probability - - return samples, measurements - - @staticmethod - def compute_mle( - result: "AmplitudeEstimationResult", apply_post_processing: bool = False - ) -> float: - """Compute the Maximum Likelihood Estimator (MLE). - - Args: - result: An amplitude estimation result object. - apply_post_processing: If True, apply the post processing to the MLE before returning - it. - - Returns: - The MLE for the provided result object. - """ - m = result.num_evaluation_qubits - M = 2**m # pylint: disable=invalid-name - qae = result.estimation - - # likelihood function - a_i = np.asarray(list(result.samples.keys())) - p_i = np.asarray(list(result.samples.values())) - - def loglikelihood(a): - return np.sum(result.shots * p_i * np.log(pdf_a(a_i, a, m))) - - # y is pretty much an integer, but to map 1.9999 to 2 we must first - # use round and then int conversion - y = int(np.round(M * np.arcsin(np.sqrt(qae)) / np.pi)) - - # Compute the two intervals in which are candidates for containing - # the maximum of the log-likelihood function: the two bubbles next to - # the QAE estimate - if y == 0: - right_of_qae = np.sin(np.pi * (y + 1) / M) ** 2 - bubbles = [qae, right_of_qae] - - elif y == int(M / 2): # remember, M = 2^m is a power of 2 - left_of_qae = np.sin(np.pi * (y - 1) / M) ** 2 - bubbles = [left_of_qae, qae] - - else: - left_of_qae = np.sin(np.pi * (y - 1) / M) ** 2 - right_of_qae = np.sin(np.pi * (y + 1) / M) ** 2 - bubbles = [left_of_qae, qae, right_of_qae] - - # Find global maximum amongst the two local maxima - a_opt = qae - loglik_opt = loglikelihood(a_opt) - for a, b in zip(bubbles[:-1], bubbles[1:]): - locmax, val = bisect_max(loglikelihood, a, b, retval=True) - if val > loglik_opt: - a_opt = locmax - loglik_opt = val - - if apply_post_processing: - return result.post_processing(a_opt) - - return a_opt - - def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimationResult": - """Run the amplitude estimation algorithm on provided estimation problem. - - Args: - estimation_problem: The estimation problem. - - Returns: - An amplitude estimation results object. - - Raises: - ValueError: If `state_preparation` or `objective_qubits` are not set in the - `estimation_problem`. - ValueError: A quantum instance or sampler must be provided. - AlgorithmError: Sampler job run error. - """ - # check if A factory or state_preparation has been set - if estimation_problem.state_preparation is None: - raise ValueError( - "The state_preparation property of the estimation problem must be set." - ) - if self._quantum_instance is None and self._sampler is None: - raise ValueError("A quantum instance or sampler must be provided.") - - if estimation_problem.objective_qubits is None: - raise ValueError("The objective_qubits property of the estimation problem must be set.") - - if estimation_problem.has_good_state: - warnings.warn( - "The AmplitudeEstimation class does not support an is_good_state function to " - "identify good states. For this algorithm, a custom oracle has to be encoded directly " - "in the grover_operator. If no custom oracle is set, this algorithm identifies good " - "states as those, where all objective qubits are in state 1." - ) - - result = AmplitudeEstimationResult() - result.num_evaluation_qubits = self._m - result.post_processing = estimation_problem.post_processing - - shots = 0 - if self._quantum_instance is not None and self._quantum_instance.is_statevector: - circuit = self.construct_circuit(estimation_problem, measurement=False) - # run circuit on statevector simulator - statevector = self._quantum_instance.execute(circuit).get_statevector() - result.circuit_results = statevector - # store number of shots: convention is 1 shot for statevector, - # needed so that MLE works! - shots = 1 - else: - circuit = self.construct_circuit(estimation_problem, measurement=True) - if self._quantum_instance is not None: - # run circuit on QASM simulator - result.circuit_results = self._quantum_instance.execute(circuit).get_counts() - shots = sum(result.circuit_results.values()) - else: - try: - job = self._sampler.run([circuit]) - ret = job.result() - except Exception as exc: - raise AlgorithmError("The job was not completed successfully. ") from exc - - shots = ret.metadata[0].get("shots") - if shots is None: - result.circuit_results = ret.quasi_dists[0].binary_probabilities() - shots = 1 - else: - result.circuit_results = { - k: round(v * shots) - for k, v in ret.quasi_dists[0].binary_probabilities().items() - } - - # store shots - result.shots = shots - samples, measurements = self.evaluate_measurements(result.circuit_results) - - result.samples = samples - result.samples_processed = { - estimation_problem.post_processing(a): p for a, p in samples.items() - } - result.measurements = measurements - - # determine the most likely estimate - result.max_probability = 0 - for amplitude, (mapped, prob) in zip(samples.keys(), result.samples_processed.items()): - if prob > result.max_probability: - result.max_probability = prob - result.estimation = amplitude - result.estimation_processed = mapped - - # store the number of oracle queries - result.num_oracle_queries = result.shots * (self._M - 1) - - # run the MLE post-processing - mle = self.compute_mle(result) - result.mle = mle - result.mle_processed = estimation_problem.post_processing(mle) - - result.confidence_interval = self.compute_confidence_interval(result) - result.confidence_interval_processed = tuple( - estimation_problem.post_processing(value) for value in result.confidence_interval - ) - - return result - - @staticmethod - def compute_confidence_interval( - result: "AmplitudeEstimationResult", alpha: float = 0.05, kind: str = "likelihood_ratio" - ) -> tuple[float, float]: - """Compute the (1 - alpha) confidence interval. - - Args: - result: An amplitude estimation result for which to compute the confidence interval. - alpha: Confidence level: compute the (1 - alpha) confidence interval. - kind: The method to compute the confidence interval, can be 'fisher', 'observed_fisher' - or 'likelihood_ratio' (default) - - Returns: - The (1 - alpha) confidence interval of the specified kind. - - Raises: - NotImplementedError: If the confidence interval method `kind` is not implemented. - """ - # if statevector simulator the estimate is exact - if isinstance(result.circuit_results, (list, np.ndarray)): - return (result.mle, result.mle) - - if kind in ["likelihood_ratio", "lr"]: - return _likelihood_ratio_confint(result, alpha) - - if kind in ["fisher", "fi"]: - return _fisher_confint(result, alpha, observed=False) - - if kind in ["observed_fisher", "observed_information", "oi"]: - return _fisher_confint(result, alpha, observed=True) - - raise NotImplementedError(f"CI `{kind}` is not implemented.") - - -class AmplitudeEstimationResult(AmplitudeEstimatorResult): - """The ``AmplitudeEstimation`` result object.""" - - def __init__(self) -> None: - super().__init__() - self._num_evaluation_qubits: int | None = None - self._mle: float | None = None - self._mle_processed: float | None = None - self._samples: dict[float, float] | None = None - self._samples_processed: dict[float, float] | None = None - self._y_measurements: dict[int, float] | None = None - self._max_probability: float | None = None - - @property - def num_evaluation_qubits(self) -> int: - """Returns the number of evaluation qubits.""" - return self._num_evaluation_qubits - - @num_evaluation_qubits.setter - def num_evaluation_qubits(self, num_evaluation_qubits: int) -> None: - """Set the number of evaluation qubits.""" - self._num_evaluation_qubits = num_evaluation_qubits - - @property - def mle_processed(self) -> float: - """Return the post-processed MLE for the amplitude.""" - return self._mle_processed - - @mle_processed.setter - def mle_processed(self, value: float) -> None: - """Set the post-processed MLE for the amplitude.""" - self._mle_processed = value - - @property - def samples_processed(self) -> dict[float, float]: - """Return the post-processed measurement samples with their measurement probability.""" - return self._samples_processed - - @samples_processed.setter - def samples_processed(self, value: dict[float, float]) -> None: - """Set the post-processed measurement samples.""" - self._samples_processed = value - - @property - def mle(self) -> float: - r"""Return the MLE for the amplitude, in $[0, 1]$.""" - return self._mle - - @mle.setter - def mle(self, value: float) -> None: - r"""Set the MLE for the amplitude, in $[0, 1]$.""" - self._mle = value - - @property - def samples(self) -> dict[float, float]: - """Return the measurement samples with their measurement probability.""" - return self._samples - - @samples.setter - def samples(self, value: dict[float, float]) -> None: - """Set the measurement samples with their measurement probability.""" - self._samples = value - - @property - def measurements(self) -> dict[int, float]: - """Return the measurements as integers with their measurement probability.""" - return self._y_measurements - - @measurements.setter - def measurements(self, value: dict[int, float]) -> None: - """Set the measurements as integers with their measurement probability.""" - self._y_measurements = value - - @property - def max_probability(self) -> float: - """Return the maximum sampling probability.""" - return self._max_probability - - @max_probability.setter - def max_probability(self, value: float) -> None: - """Set the maximum sampling probability.""" - self._max_probability = value - - -def _compute_fisher_information(result: AmplitudeEstimationResult, observed: bool = False) -> float: - """Computes the Fisher information for the output of the previous run. - - Args: - result: An amplitude estimation result for which to compute the confidence interval. - observed: If True, the observed Fisher information is returned, otherwise - the expected Fisher information. - - Returns: - The Fisher information. - """ - fisher_information = None - mlv = result.mle # MLE in [0,1] - m = result.num_evaluation_qubits - M = 2**m # pylint: disable=invalid-name - - if observed: - a_i = np.asarray(list(result.samples.keys())) - p_i = np.asarray(list(result.samples.values())) - - # Calculate the observed Fisher information - fisher_information = sum(p * derivative_log_pdf_a(a, mlv, m) ** 2 for p, a in zip(p_i, a_i)) - else: - - def integrand(x): - return (derivative_log_pdf_a(x, mlv, m)) ** 2 * pdf_a(x, mlv, m) - - grid = np.sin(np.pi * np.arange(M / 2 + 1) / M) ** 2 - fisher_information = sum(integrand(x) for x in grid) - - return fisher_information - - -def _fisher_confint( - result: AmplitudeEstimationResult, alpha: float, observed: bool = False -) -> tuple[float, float]: - """Compute the Fisher information confidence interval for the MLE of the previous run. - - Args: - result: An amplitude estimation result for which to compute the confidence interval. - alpha: Specifies the (1 - alpha) confidence level (0 < alpha < 1). - observed: If True, the observed Fisher information is used to construct the - confidence interval, otherwise the expected Fisher information. - - Returns: - The Fisher information confidence interval. - """ - # approximate the standard deviation of the MLE and construct the confidence interval - std = np.sqrt(result.shots * _compute_fisher_information(result, observed)) - confint = result.mle + norm.ppf(1 - alpha / 2) / std * np.array([-1, 1]) - - # transform the confidence interval from [0, 1] to the target interval - return result.post_processing(confint[0]), result.post_processing(confint[1]) - - -def _likelihood_ratio_confint( - result: AmplitudeEstimationResult, alpha: float -) -> tuple[float, float]: - """Compute the likelihood ratio confidence interval for the MLE of the previous run. - - Args: - result: An amplitude estimation result for which to compute the confidence interval. - alpha: Specifies the (1 - alpha) confidence level (0 < alpha < 1). - - Returns: - The likelihood ratio confidence interval. - """ - # Compute the two intervals in which we the look for values above - # the likelihood ratio: the two bubbles next to the QAE estimate - m = result.num_evaluation_qubits - M = 2**m # pylint: disable=invalid-name - qae = result.estimation - - y = int(np.round(M * np.arcsin(np.sqrt(qae)) / np.pi)) - if y == 0: - right_of_qae = np.sin(np.pi * (y + 1) / M) ** 2 - bubbles = [qae, right_of_qae] - - elif y == int(M / 2): # remember, M = 2^m is a power of 2 - left_of_qae = np.sin(np.pi * (y - 1) / M) ** 2 - bubbles = [left_of_qae, qae] - - else: - left_of_qae = np.sin(np.pi * (y - 1) / M) ** 2 - right_of_qae = np.sin(np.pi * (y + 1) / M) ** 2 - bubbles = [left_of_qae, qae, right_of_qae] - - # likelihood function - a_i = np.asarray(list(result.samples.keys())) - p_i = np.asarray(list(result.samples.values())) - - def loglikelihood(a): - return np.sum(result.shots * p_i * np.log(pdf_a(a_i, a, m))) - - # The threshold above which the likelihoods are in the - # confidence interval - loglik_mle = loglikelihood(result.mle) - thres = loglik_mle - chi2.ppf(1 - alpha, df=1) / 2 - - def cut(x): - return loglikelihood(x) - thres - - # Store the boundaries of the confidence interval - # It's valid to start off with the zero-width confidence interval, since the maximum - # of the likelihood function is guaranteed to be over the threshold, and if alpha = 0 - # that's the valid interval - lower = upper = result.mle - - # Check the two intervals/bubbles: check if they surpass the - # threshold and if yes add the part that does to the CI - for a, b in zip(bubbles[:-1], bubbles[1:]): - # Compute local maximum and perform a bisect search between - # the local maximum and the bubble boundaries - locmax, val = bisect_max(loglikelihood, a, b, retval=True) - if val >= thres: - # Bisect pre-condition is that the function has different - # signs at the boundaries of the interval we search in - if cut(a) * cut(locmax) < 0: - left = bisect(cut, a, locmax) - lower = np.minimum(lower, left) - if cut(locmax) * cut(b) < 0: - right = bisect(cut, locmax, b) - upper = np.maximum(upper, right) - - # Put together CI - return result.post_processing(lower), result.post_processing(upper) diff --git a/qiskit/algorithms/amplitude_estimators/ae_utils.py b/qiskit/algorithms/amplitude_estimators/ae_utils.py deleted file mode 100644 index bd695be45a63..000000000000 --- a/qiskit/algorithms/amplitude_estimators/ae_utils.py +++ /dev/null @@ -1,258 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Utils for the Maximum-Likelihood estimation used in ``AmplitudeEstimation``.""" - -import logging -import numpy as np - -logger = logging.getLogger(__name__) - -# pylint: disable=invalid-name - - -def bisect_max(f, a, b, steps=50, minwidth=1e-12, retval=False): - """Find the maximum of the real-valued function f in the interval [a, b] using bisection. - - Args: - f (callable): the function to find the maximum of - a (float): the lower limit of the interval - b (float): the upper limit of the interval - steps (int): the maximum number of steps in the bisection - minwidth (float): if the current interval is smaller than minwidth stop - the search - retval (bool): return value - - Returns: - float: The maximum of f in [a,b] according to this algorithm. - """ - it = 0 - m = (a + b) / 2 - fm = 0 - while it < steps and b - a > minwidth: - l, r = (a + m) / 2, (m + b) / 2 - fl, fm, fr = f(l), f(m), f(r) - - # fl is the maximum - if fl > fm and fl > fr: - b = m - m = l - # fr is the maximum - elif fr > fm and fr > fl: - a = m - m = r - # fm is the maximum - else: - a = l - b = r - - it += 1 - - if it == steps: - logger.warning("-- Warning, bisect_max didn't converge after %s steps", steps) - - if retval: - return m, fm - - return m - - -def _circ_dist(x, p): - r"""Circumferential distance function. - - For two angles :math:`x` and :math:`p` on the unit circuit this function is defined as - - .. math:: - - d(x, p) = \min_{z \in [-1, 0, 1]} |z + p - x| - - Args: - x (float): first angle - p (float): second angle - - Returns: - float: d(x, p) - """ - t = p - x - # Since x and p \in [0,1] it suffices to check not all integers - # but only -1, 0 and 1 - z = np.array([-1, 0, 1]) - - if hasattr(t, "__len__"): - d = np.empty_like(t) - for idx, ti in enumerate(t): - d[idx] = np.min(np.abs(z + ti)) - return d - - return np.min(np.abs(z + t)) - - -def _derivative_circ_dist(x, p): - """Derivative of circumferential distance function. - - Args: - x (float): first angle - p (float): second angle - - Returns: - float: The derivative. - """ - # pylint: disable=chained-comparison,misplaced-comparison-constant - t = p - x - if t < -0.5 or (0 < t and t < 0.5): - return -1 - if t > 0.5 or (-0.5 < t and t < 0): - return 1 - return 0 - - -def _amplitude_to_angle(a): - r"""Transform from the amplitude :math:`a \in [0, 1]` to the generating angle. - - In QAE, the amplitude can be written from a generating angle :math:`\omega` as - - .. math: - - a = \sin^2(\pi \omega) - - This returns the :math:`\omega` for a given :math:`a`. - - Args: - a (float): A value in :math:`[0,1]`. - - Returns: - float: :math:`\sin^{-1}(\sqrt{a}) / \pi` - """ - return np.arcsin(np.sqrt(a)) / np.pi - - -def _derivative_amplitude_to_angle(a): - """Compute the derivative of ``amplitude_to_angle``.""" - return 1 / (2 * np.pi * np.sqrt((1 - a) * a)) - - -def _alpha(x, p): - """Helper function for `pdf_a`, alpha = pi * d(omega(x), omega(p)). - - Here, omega(x) is `_amplitude_to_angle(x)`. - """ - omega = _amplitude_to_angle - return np.pi * _circ_dist(omega(x), omega(p)) - - -def _derivative_alpha(x, p): - """Compute the derivative of alpha.""" - omega = _amplitude_to_angle - d_omega = _derivative_amplitude_to_angle - return np.pi * _derivative_circ_dist(omega(x), omega(p)) * d_omega(p) - - -def _beta(x, p): - """Helper function for `pdf_a`, beta = pi * d(1 - omega(x), omega(p)).""" - omega = _amplitude_to_angle - return np.pi * _circ_dist(1 - omega(x), omega(p)) - - -def _derivative_beta(x, p): - """Compute the derivative of beta.""" - omega = _amplitude_to_angle - d_omega = _derivative_amplitude_to_angle - return np.pi * _derivative_circ_dist(1 - omega(x), omega(p)) * d_omega(p) - - -def _pdf_a_single_angle(x, p, m, pi_delta): - """Helper function for `pdf_a`.""" - M = 2**m - - d = pi_delta(x, p) - res = np.sin(M * d) ** 2 / (M * np.sin(d)) ** 2 if d != 0 else 1 - - return res - - -def pdf_a(x, p, m): - """ - Return the PDF of a, i.e. the probability of getting the estimate x - (in [0, 1]) if p (in [0, 1]) is the true value, given that we use m qubits. - - Args: - x (float): the grid point - p (float): the true value - m (float): the number of evaluation qubits - - Returns: - float: PDF(x|p) - """ - # We'll use list comprehension, so the input should be a list - scalar = False - if not hasattr(x, "__len__"): - scalar = True - x = np.asarray([x]) - - # Compute the probabilities: Add up both angles that produce the given - # value, except for the angles 0 and 0.5, which map to the unique a-values, - # 0 and 1, respectively - pr = np.array( - [ - _pdf_a_single_angle(xi, p, m, _alpha) + _pdf_a_single_angle(xi, p, m, _beta) - if (xi not in [0, 1]) - else _pdf_a_single_angle(xi, p, m, _alpha) - for xi in x - ] - ).flatten() - - # If is was a scalar return scalar otherwise the array - return pr[0] if scalar else pr - - -def derivative_log_pdf_a(x, p, m): - """ - Return the derivative of the logarithm of the PDF of a. - - Args: - x (float): the grid point - p (float): the true value - m (float): the number of evaluation qubits - - Returns: - float: d/dp log(PDF(x|p)) - """ - M = 2**m - - if x not in [0, 1]: - num_p1 = 0 - for A, dA, B, dB in zip( - [_alpha, _beta], - [_derivative_alpha, _derivative_beta], - [_beta, _alpha], - [_derivative_beta, _derivative_alpha], - ): - num_p1 += 2 * M * np.sin(M * A(x, p)) * np.cos(M * A(x, p)) * dA(x, p) * np.sin( - B(x, p) - ) ** 2 + 2 * np.sin(M * A(x, p)) ** 2 * np.sin(B(x, p)) * np.cos(B(x, p)) * dB(x, p) - - den_p1 = ( - np.sin(M * _alpha(x, p)) ** 2 * np.sin(_beta(x, p)) ** 2 - + np.sin(M * _beta(x, p)) ** 2 * np.sin(_alpha(x, p)) ** 2 - ) - - num_p2 = 0 - for A, dA, B in zip( - [_alpha, _beta], [_derivative_alpha, _derivative_beta], [_beta, _alpha] - ): - num_p2 += 2 * np.cos(A(x, p)) * dA(x, p) * np.sin(B(x, p)) - - den_p2 = np.sin(_alpha(x, p)) * np.sin(_beta(x, p)) - - return num_p1 / den_p1 - num_p2 / den_p2 - - return 2 * _derivative_alpha(x, p) * (M / np.tan(M * _alpha(x, p)) - 1 / np.tan(_alpha(x, p))) diff --git a/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py b/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py deleted file mode 100644 index 613827c6ccd6..000000000000 --- a/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py +++ /dev/null @@ -1,131 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Amplitude Estimation interface.""" - -from __future__ import annotations -from abc import abstractmethod, ABC -from collections.abc import Callable - -import numpy as np - -from .estimation_problem import EstimationProblem -from ..algorithm_result import AlgorithmResult - - -class AmplitudeEstimator(ABC): - """The Amplitude Estimation interface.""" - - @abstractmethod - def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimatorResult": - """Run the amplitude estimation algorithm. - - Args: - estimation_problem: An ``EstimationProblem`` containing all problem-relevant information - such as the state preparation and the objective qubits. - """ - raise NotImplementedError - - -class AmplitudeEstimatorResult(AlgorithmResult): - """The results object for amplitude estimation algorithms.""" - - def __init__(self) -> None: - super().__init__() - self._circuit_results: np.ndarray | dict[str, int] | None = None - self._shots: int | None = None - self._estimation: float | None = None - self._estimation_processed: float | None = None - self._num_oracle_queries: int | None = None - self._post_processing: Callable[[float], float] | None = None - self._confidence_interval: tuple[float, float] | None = None - self._confidence_interval_processed: tuple[float, float] | None = None - - @property - def circuit_results(self) -> np.ndarray | dict[str, int] | None: - """Return the circuit results. Can be a statevector or counts dictionary.""" - return self._circuit_results - - @circuit_results.setter - def circuit_results(self, value: np.ndarray | dict[str, int]) -> None: - """Set the circuit results.""" - self._circuit_results = value - - @property - def shots(self) -> int: - """Return the number of shots used. Is 1 for statevector-based simulations.""" - return self._shots - - @shots.setter - def shots(self, value: int) -> None: - """Set the number of shots used.""" - self._shots = value - - @property - def estimation(self) -> float: - r"""Return the estimation for the amplitude in :math:`[0, 1]`.""" - return self._estimation - - @estimation.setter - def estimation(self, value: float) -> None: - r"""Set the estimation for the amplitude in :math:`[0, 1]`.""" - self._estimation = value - - @property - def estimation_processed(self) -> float: - """Return the estimation for the amplitude after the post-processing has been applied.""" - return self._estimation_processed - - @estimation_processed.setter - def estimation_processed(self, value: float) -> None: - """Set the estimation for the amplitude after the post-processing has been applied.""" - self._estimation_processed = value - - @property - def num_oracle_queries(self) -> int: - """Return the number of Grover oracle queries.""" - return self._num_oracle_queries - - @num_oracle_queries.setter - def num_oracle_queries(self, value: int) -> None: - """Set the number of Grover oracle queries.""" - self._num_oracle_queries = value - - @property - def post_processing(self) -> Callable[[float], float]: - """Return a handle to the post processing function.""" - return self._post_processing - - @post_processing.setter - def post_processing(self, post_processing: Callable[[float], float]) -> None: - """Set a handle to the post processing function.""" - self._post_processing = post_processing - - @property - def confidence_interval(self) -> tuple[float, float]: - """Return the confidence interval for the amplitude (95% interval by default).""" - return self._confidence_interval - - @confidence_interval.setter - def confidence_interval(self, confidence_interval: tuple[float, float]) -> None: - """Set the confidence interval for the amplitude (95% interval by default).""" - self._confidence_interval = confidence_interval - - @property - def confidence_interval_processed(self) -> tuple[float, float]: - """Return the post-processed confidence interval (95% interval by default).""" - return self._confidence_interval_processed - - @confidence_interval_processed.setter - def confidence_interval_processed(self, confidence_interval: tuple[float, float]) -> None: - """Set the post-processed confidence interval (95% interval by default).""" - self._confidence_interval_processed = confidence_interval diff --git a/qiskit/algorithms/amplitude_estimators/estimation_problem.py b/qiskit/algorithms/amplitude_estimators/estimation_problem.py deleted file mode 100644 index 72e9418f72d6..000000000000 --- a/qiskit/algorithms/amplitude_estimators/estimation_problem.py +++ /dev/null @@ -1,274 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Estimation problem class.""" - -from __future__ import annotations -import warnings -from collections.abc import Callable - -import numpy - -from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.circuit.library import GroverOperator - - -class EstimationProblem: - """The estimation problem is the input to amplitude estimation algorithm. - - This class contains all problem-specific information required to run an amplitude estimation - algorithm. That means, it minimally contains the state preparation and the specification - of the good state. It can further hold some post processing on the estimation of the amplitude - or a custom Grover operator. - """ - - def __init__( - self, - state_preparation: QuantumCircuit, - objective_qubits: int | list[int], - grover_operator: QuantumCircuit | None = None, - post_processing: Callable[[float], float] | None = None, - is_good_state: Callable[[str], bool] | None = None, - ) -> None: - r""" - Args: - state_preparation: A circuit preparing the input state, referred to as - :math:`\mathcal{A}`. - objective_qubits: A single qubit index or a list of qubit indices to specify which - qubits to measure. The ``is_good_state`` function is applied on the bitstring of - these objective qubits. - grover_operator: The Grover operator :math:`\mathcal{Q}` used as unitary in the - phase estimation circuit. - post_processing: A mapping applied to the result of the algorithm - :math:`0 \leq a \leq 1`, usually used to map the estimate to a target interval. - Defaults to the identity. - is_good_state: A function to check whether a string represents a good state. Defaults - to all objective qubits being in state :math:`|1\rangle`. - """ - self._state_preparation = state_preparation - self._objective_qubits = objective_qubits - self._grover_operator = grover_operator - self._post_processing = post_processing - self._is_good_state = is_good_state - - @property - def state_preparation(self) -> QuantumCircuit | None: - r"""Get the :math:`\mathcal{A}` operator encoding the amplitude :math:`a`. - - Returns: - The :math:`\mathcal{A}` operator as `QuantumCircuit`. - """ - return self._state_preparation - - @state_preparation.setter - def state_preparation(self, state_preparation: QuantumCircuit) -> None: - r"""Set the :math:`\mathcal{A}` operator, that encodes the amplitude to be estimated. - - Args: - state_preparation: The new :math:`\mathcal{A}` operator. - """ - self._state_preparation = state_preparation - - @property - def objective_qubits(self) -> list[int]: - """Get the criterion for a measurement outcome to be in a 'good' state. - - Returns: - The criterion as list of qubit indices. - """ - if isinstance(self._objective_qubits, int): - return [self._objective_qubits] - - return self._objective_qubits - - @objective_qubits.setter - def objective_qubits(self, objective_qubits: int | list[int]) -> None: - """Set the criterion for a measurement outcome to be in a 'good' state. - - Args: - objective_qubits: The criterion as callable of list of qubit indices. - """ - self._objective_qubits = objective_qubits - - @property - def post_processing(self) -> Callable[[float], float]: - """Apply post processing to the input value. - - Returns: - A handle to the post processing function. Acts as identity by default. - """ - if self._post_processing is None: - return lambda x: x - - return self._post_processing - - @post_processing.setter - def post_processing(self, post_processing: Callable[[float], float] | None) -> None: - """Set the post processing function. - - Args: - post_processing: A handle to the post processing function. If set to ``None``, the - identity will be used as post processing. - """ - self._post_processing = post_processing - - @property - def has_good_state(self) -> bool: - """Check whether an :attr:`is_good_state` function is set. - - Some amplitude estimators, such as :class:`.AmplitudeEstimation` do not support - a custom implementation of the :attr:`is_good_state` function, and can only handle - the default. - - Returns: - ``True``, if a custom :attr:`is_good_state` is set, otherwise returns ``False``. - """ - return self._is_good_state is not None - - @property - def is_good_state(self) -> Callable[[str], bool]: - """Checks whether a bitstring represents a good state. - - Returns: - Handle to the ``is_good_state`` callable. - """ - if self._is_good_state is None: - return lambda x: all(bit == "1" for bit in x) - - return self._is_good_state - - @is_good_state.setter - def is_good_state(self, is_good_state: Callable[[str], bool] | None) -> None: - """Set the ``is_good_state`` function. - - Args: - is_good_state: A function to determine whether a bitstring represents a good state. - If set to ``None``, the good state will be defined as all bits being one. - """ - self._is_good_state = is_good_state - - @property - def grover_operator(self) -> QuantumCircuit | None: - r"""Get the :math:`\mathcal{Q}` operator, or Grover operator. - - If the Grover operator is not set, we try to build it from the :math:`\mathcal{A}` operator - and `objective_qubits`. This only works if `objective_qubits` is a list of integers. - - Returns: - The Grover operator, or None if neither the Grover operator nor the - :math:`\mathcal{A}` operator is set. - """ - if self._grover_operator is not None: - return self._grover_operator - - # build the reflection about the bad state: a MCZ with open controls (thus X gates - # around the controls) and X gates around the target to change from a phaseflip on - # |1> to a phaseflip on |0> - num_state_qubits = self.state_preparation.num_qubits - self.state_preparation.num_ancillas - - oracle = QuantumCircuit(num_state_qubits) - oracle.h(self.objective_qubits[-1]) - if len(self.objective_qubits) == 1: - oracle.x(self.objective_qubits[0]) - else: - oracle.mcx(self.objective_qubits[:-1], self.objective_qubits[-1]) - oracle.h(self.objective_qubits[-1]) - - # construct the grover operator - return GroverOperator(oracle, self.state_preparation) - - @grover_operator.setter - def grover_operator(self, grover_operator: QuantumCircuit | None) -> None: - r"""Set the :math:`\mathcal{Q}` operator. - - Args: - grover_operator: The new :math:`\mathcal{Q}` operator. If set to ``None``, - the default construction via ``qiskit.circuit.library.GroverOperator`` is used. - """ - self._grover_operator = grover_operator - - def rescale(self, scaling_factor: float) -> "EstimationProblem": - """Rescale the good state amplitude in the estimation problem. - - Args: - scaling_factor: The scaling factor in [0, 1]. - - Returns: - A rescaled estimation problem. - """ - if self._grover_operator is not None: - warnings.warn("Rescaling discards the Grover operator.") - - # rescale the amplitude by a factor of 1/4 by adding an auxiliary qubit - rescaled_stateprep = _rescale_amplitudes(self.state_preparation, scaling_factor) - num_qubits = self.state_preparation.num_qubits - objective_qubits = self.objective_qubits + [num_qubits] - - # add the scaling qubit to the good state qualifier - def is_good_state(bitstr): - return self.is_good_state(bitstr[1:]) and bitstr[0] == "1" - - # rescaled estimation problem - problem = EstimationProblem( - rescaled_stateprep, - objective_qubits=objective_qubits, - post_processing=self.post_processing, - is_good_state=is_good_state, - ) - - return problem - - -def _rescale_amplitudes(circuit: QuantumCircuit, scaling_factor: float) -> QuantumCircuit: - r"""Uses an auxiliary qubit to scale the amplitude of :math:`|1\rangle` by ``scaling_factor``. - - Explained in Section 2.1. of [1]. - - For example, for a scaling factor of 0.25 this turns this circuit - - .. parsed-literal:: - - ┌───┐ - state_0: ─────┤ H ├─────────■──── - ┌───┴───┴───┐ ┌───┴───┐ - obj_0: ─┤ RY(0.125) ├─┤ RY(1) ├ - └───────────┘ └───────┘ - - into - - .. parsed-literal:: - - ┌───┐ - state_0: ─────┤ H ├─────────■──── - ┌───┴───┴───┐ ┌───┴───┐ - obj_0: ─┤ RY(0.125) ├─┤ RY(1) ├ - ┌┴───────────┴┐└───────┘ - scaling_0: ┤ RY(0.50536) ├───────── - └─────────────┘ - - References: - - [1]: K. Nakaji. Faster Amplitude Estimation, 2020; - `arXiv:2002.02417 `_ - - Args: - circuit: The circuit whose amplitudes to rescale. - scaling_factor: The rescaling factor. - - Returns: - A copy of the circuit with an additional qubit and RY gate for the rescaling. - """ - qr = QuantumRegister(1, "scaling") - rescaled = QuantumCircuit(*circuit.qregs, qr) - rescaled.compose(circuit, circuit.qubits, inplace=True) - rescaled.ry(2 * numpy.arcsin(scaling_factor), qr) - return rescaled diff --git a/qiskit/algorithms/amplitude_estimators/fae.py b/qiskit/algorithms/amplitude_estimators/fae.py deleted file mode 100644 index 08fedaffa34a..000000000000 --- a/qiskit/algorithms/amplitude_estimators/fae.py +++ /dev/null @@ -1,391 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Faster Amplitude Estimation.""" - -from __future__ import annotations -import warnings -import numpy as np - -from qiskit.circuit import QuantumCircuit, ClassicalRegister -from qiskit.providers import Backend -from qiskit.primitives import BaseSampler -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_arg, deprecate_func -from qiskit.algorithms.exceptions import AlgorithmError - -from .amplitude_estimator import AmplitudeEstimator, AmplitudeEstimatorResult -from .estimation_problem import EstimationProblem - - -class FasterAmplitudeEstimation(AmplitudeEstimator): - """The Faster Amplitude Estimation algorithm. - - The Faster Amplitude Estimation (FAE) [1] algorithm is a variant of Quantum Amplitude - Estimation (QAE), where the Quantum Phase Estimation (QPE) by an iterative Grover search, - similar to [2]. - - Due to the iterative version of the QPE, this algorithm does not require any additional - qubits, as the originally proposed QAE [3] and thus the resulting circuits are less complex. - - References: - - [1]: K. Nakaji. Faster Amplitude Estimation, 2020; - `arXiv:2002.02417 `_ - [2]: D. Grinko et al. Iterative Amplitude Estimation, 2019; - `arXiv:1912.05559 `_ - [3]: G. Brassard et al. Quantum Amplitude Amplification and Estimation, 2000; - `arXiv:quant-ph/0005055 `_ - - """ - - @deprecate_arg( - "quantum_instance", - additional_msg=( - "Instead, use the ``sampler`` argument. See https://qisk.it/algo_migration for a " - "migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - delta: float, - maxiter: int, - rescale: bool = True, - quantum_instance: QuantumInstance | Backend | None = None, - sampler: BaseSampler | None = None, - ) -> None: - r""" - Args: - delta: The probability that the true value is outside of the final confidence interval. - maxiter: The number of iterations, the maximal power of Q is `2 ** (maxiter - 1)`. - rescale: Whether to rescale the problem passed to `estimate`. - quantum_instance: Deprecated: The quantum instance or backend - to run the circuits. - sampler: A sampler primitive to evaluate the circuits. - - .. note:: - - This algorithm overwrites the number of shots set in the ``quantum_instance`` - argument, but will reset them to the initial number after running. - - """ - super().__init__() - # set quantum instance - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self.quantum_instance = quantum_instance - self._shots = (int(1944 * np.log(2 / delta)), int(972 * np.log(2 / delta))) - self._rescale = rescale - self._delta = delta - self._maxiter = maxiter - self._num_oracle_calls = 0 - self._sampler = sampler - - @property - def sampler(self) -> BaseSampler | None: - """Get the sampler primitive. - - Returns: - The sampler primitive to evaluate the circuits. - """ - return self._sampler - - @sampler.setter - def sampler(self, sampler: BaseSampler) -> None: - """Set sampler primitive. - - Args: - sampler: A sampler primitive to evaluate the circuits. - """ - self._sampler = sampler - - @property - @deprecate_func( - since="0.24.0", - is_property=True, - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def quantum_instance(self) -> QuantumInstance | None: - """Deprecated. Get the quantum instance. - - Returns: - The quantum instance used to run this algorithm. - """ - return self._quantum_instance - - @quantum_instance.setter - @deprecate_func( - since="0.24.0", - is_property=True, - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: - """Deprecated. Set quantum instance. - - Args: - quantum_instance: The quantum instance used to run this algorithm. - """ - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - self._quantum_instance = quantum_instance - - def _cos_estimate(self, estimation_problem, k, shots): - if self._quantum_instance is None and self._sampler is None: - raise ValueError("A quantum instance or sampler must be provided.") - - if self._sampler is not None: - circuit = self.construct_circuit(estimation_problem, k, measurement=True) - try: - job = self._sampler.run([circuit], shots=shots) - result = job.result() - except Exception as exc: - raise AlgorithmError("The job was not completed successfully. ") from exc - - if shots is None: - shots = 1 - self._num_oracle_calls += (2 * k + 1) * shots - - # sum over all probabilities where the objective qubits are 1 - prob = 0 - for bit, probabilities in result.quasi_dists[0].binary_probabilities().items(): - # check if it is a good state - if estimation_problem.is_good_state(bit): - prob += probabilities - - cos_estimate = 1 - 2 * prob - elif self._quantum_instance.is_statevector: - circuit = self.construct_circuit(estimation_problem, k, measurement=False) - statevector = self._quantum_instance.execute(circuit).get_statevector() - - # sum over all amplitudes where the objective qubits are 1 - prob = 0 - for i, amplitude in enumerate(statevector): - # get bitstring of objective qubits - full_state = bin(i)[2:].zfill(circuit.num_qubits)[::-1] - state = "".join([full_state[i] for i in estimation_problem.objective_qubits]) - - # check if it is a good state - if estimation_problem.is_good_state(state[::-1]): - prob = prob + np.abs(amplitude) ** 2 - - cos_estimate = 1 - 2 * prob - else: - circuit = self.construct_circuit(estimation_problem, k, measurement=True) - - self._quantum_instance.run_config.shots = shots - counts = self._quantum_instance.execute(circuit).get_counts() - self._num_oracle_calls += (2 * k + 1) * shots - - good_counts = 0 - for state, count in counts.items(): - if estimation_problem.is_good_state(state): - good_counts += count - - cos_estimate = 1 - 2 * good_counts / shots - - return cos_estimate - - def _chernoff(self, cos, shots) -> list[float]: - width = np.sqrt(np.log(2 / self._delta) * 12 / shots) - confint = [np.maximum(-1, cos - width), np.minimum(1, cos + width)] - return confint - - def construct_circuit( - self, estimation_problem: EstimationProblem, k: int, measurement: bool = False - ) -> QuantumCircuit | tuple[QuantumCircuit, list[int]]: - r"""Construct the circuit :math:`Q^k X |0\rangle>`. - - The A operator is the unitary specifying the QAE problem and Q the associated Grover - operator. - - Args: - estimation_problem: The estimation problem for which to construct the circuit. - k: The power of the Q operator. - measurement: Boolean flag to indicate if measurements should be included in the - circuits. - - Returns: - The circuit :math:`Q^k X |0\rangle`. - """ - num_qubits = max( - estimation_problem.state_preparation.num_qubits, - estimation_problem.grover_operator.num_qubits, - ) - circuit = QuantumCircuit(num_qubits, name="circuit") - - # add classical register if needed - if measurement: - c = ClassicalRegister(len(estimation_problem.objective_qubits)) - circuit.add_register(c) - - # add A operator - circuit.compose(estimation_problem.state_preparation, inplace=True) - - # add Q^k - if k != 0: - circuit.compose(estimation_problem.grover_operator.power(k), inplace=True) - - # add optional measurement - if measurement: - # real hardware can currently not handle operations after measurements, which might - # happen if the circuit gets transpiled, hence we're adding a safeguard-barrier - circuit.barrier() - circuit.measure(estimation_problem.objective_qubits, c[:]) - - return circuit - - def estimate(self, estimation_problem: EstimationProblem) -> "FasterAmplitudeEstimationResult": - """Run the amplitude estimation algorithm on provided estimation problem. - - Args: - estimation_problem: The estimation problem. - - Returns: - An amplitude estimation results object. - - Raises: - ValueError: A quantum instance or Sampler must be provided. - AlgorithmError: Sampler run error. - """ - if self._quantum_instance is None and self._sampler is None: - raise ValueError("A quantum instance or sampler must be provided.") - - self._num_oracle_calls = 0 - user_defined_shots = ( - self._quantum_instance._run_config.shots if self._quantum_instance is not None else None - ) - - if self._rescale: - problem = estimation_problem.rescale(0.25) - else: - problem = estimation_problem - - if self._quantum_instance is not None and self._quantum_instance.is_statevector: - cos = self._cos_estimate(problem, k=0, shots=1) - theta = np.arccos(cos) / 2 - theta_ci = [theta, theta] - theta_cis = [theta_ci] - num_steps = num_first_stage_steps = 1 - else: - theta_ci = [0, np.arcsin(0.25)] - first_stage = True - j_0 = self._maxiter - - theta_cis = [theta_ci] - num_first_stage_steps = 0 - num_steps = 0 - - def cos_estimate(power, shots): - return self._cos_estimate(problem, power, shots) - - for j in range(1, self._maxiter + 1): - num_steps += 1 - if first_stage: - num_first_stage_steps += 1 - c = cos_estimate(2 ** (j - 1), self._shots[0]) - chernoff_ci = self._chernoff(c, self._shots[0]) - theta_ci = [np.arccos(x) / (2 ** (j + 1) + 2) for x in chernoff_ci[::-1]] - - if 2 ** (j + 1) * theta_ci[1] >= 3 * np.pi / 8 and j < self._maxiter: - j_0 = j - v = 2**j * np.sum(theta_ci) - first_stage = False - else: - cos = cos_estimate(2 ** (j - 1), self._shots[1]) - cos_2 = cos_estimate(2 ** (j - 1) + 2 ** (j_0 - 1), self._shots[1]) - sin = (cos * np.cos(v) - cos_2) / np.sin(v) - rho = np.arctan2(sin, cos) - n = int(((2 ** (j + 1) + 2) * theta_ci[1] - rho + np.pi / 3) / (2 * np.pi)) - - theta_ci = [ - (2 * np.pi * n + rho + sign * np.pi / 3) / (2 ** (j + 1) + 2) - for sign in [-1, 1] - ] - theta_cis.append(theta_ci) - - theta = np.mean(theta_ci) - rescaling = 4 if self._rescale else 1 - value = (rescaling * np.sin(theta)) ** 2 - value_ci = ((rescaling * np.sin(theta_ci[0])) ** 2, (rescaling * np.sin(theta_ci[1])) ** 2) - - result = FasterAmplitudeEstimationResult() - result.num_oracle_queries = self._num_oracle_calls - result.num_steps = num_steps - result.num_first_state_steps = num_first_stage_steps - if self._quantum_instance is not None and self._quantum_instance.is_statevector: - result.success_probability = 1.0 - else: - result.success_probability = 1 - (2 * self._maxiter - j_0) * self._delta - - result.estimation = value - result.estimation_processed = problem.post_processing(value) - result.confidence_interval = value_ci - result.confidence_interval_processed = tuple(problem.post_processing(x) for x in value_ci) - result.theta_intervals = theta_cis - - # reset shots to what the user had defined - if self._quantum_instance is not None: - self._quantum_instance._run_config.shots = user_defined_shots - - return result - - -class FasterAmplitudeEstimationResult(AmplitudeEstimatorResult): - """The result object for the Faster Amplitude Estimation algorithm.""" - - def __init__(self) -> None: - super().__init__() - self._success_probability: float | None = None - self._num_steps: int | None = None - self._num_first_state_steps: int | None = None - self._theta_intervals: list[list[float]] | None = None - - @property - def success_probability(self) -> float: - """Return the success probability of the algorithm.""" - return self._success_probability - - @success_probability.setter - def success_probability(self, probability: float) -> None: - """Set the success probability of the algorithm.""" - self._success_probability = probability - - @property - def num_steps(self) -> int: - """Return the total number of steps taken in the algorithm.""" - return self._num_steps - - @num_steps.setter - def num_steps(self, num_steps: int) -> None: - """Set the total number of steps taken in the algorithm.""" - self._num_steps = num_steps - - @property - def num_first_state_steps(self) -> int: - """Return the number of steps taken in the first step of algorithm.""" - return self._num_first_state_steps - - @num_first_state_steps.setter - def num_first_state_steps(self, num_steps: int) -> None: - """Set the number of steps taken in the first step of algorithm.""" - self._num_first_state_steps = num_steps - - @property - def theta_intervals(self) -> list[list[float]]: - """Return the confidence intervals for the angles in each iteration.""" - return self._theta_intervals - - @theta_intervals.setter - def theta_intervals(self, value: list[list[float]]) -> None: - """Set the confidence intervals for the angles in each iteration.""" - self._theta_intervals = value diff --git a/qiskit/algorithms/amplitude_estimators/iae.py b/qiskit/algorithms/amplitude_estimators/iae.py deleted file mode 100644 index e420b76e6a4e..000000000000 --- a/qiskit/algorithms/amplitude_estimators/iae.py +++ /dev/null @@ -1,684 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Iterative Quantum Amplitude Estimation Algorithm.""" - -from __future__ import annotations -from typing import cast -import warnings -import numpy as np -from scipy.stats import beta - -from qiskit import ClassicalRegister, QuantumCircuit -from qiskit.providers import Backend -from qiskit.primitives import BaseSampler -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_arg, deprecate_func - -from .amplitude_estimator import AmplitudeEstimator, AmplitudeEstimatorResult -from .estimation_problem import EstimationProblem -from ..exceptions import AlgorithmError - - -class IterativeAmplitudeEstimation(AmplitudeEstimator): - r"""The Iterative Amplitude Estimation algorithm. - - This class implements the Iterative Quantum Amplitude Estimation (IQAE) algorithm, proposed - in [1]. The output of the algorithm is an estimate that, - with at least probability :math:`1 - \alpha`, differs by epsilon to the target value, where - both alpha and epsilon can be specified. - - It differs from the original QAE algorithm proposed by Brassard [2] in that it does not rely on - Quantum Phase Estimation, but is only based on Grover's algorithm. IQAE iteratively - applies carefully selected Grover iterations to find an estimate for the target amplitude. - - References: - [1]: Grinko, D., Gacon, J., Zoufal, C., & Woerner, S. (2019). - Iterative Quantum Amplitude Estimation. - `arXiv:1912.05559 `_. - [2]: Brassard, G., Hoyer, P., Mosca, M., & Tapp, A. (2000). - Quantum Amplitude Amplification and Estimation. - `arXiv:quant-ph/0005055 `_. - """ - - @deprecate_arg( - "quantum_instance", - additional_msg=( - "Instead, use the ``sampler`` argument. See https://qisk.it/algo_migration for a " - "migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - epsilon_target: float, - alpha: float, - confint_method: str = "beta", - min_ratio: float = 2, - quantum_instance: QuantumInstance | Backend | None = None, - sampler: BaseSampler | None = None, - ) -> None: - r""" - The output of the algorithm is an estimate for the amplitude `a`, that with at least - probability 1 - alpha has an error of epsilon. The number of A operator calls scales - linearly in 1/epsilon (up to a logarithmic factor). - - Args: - epsilon_target: Target precision for estimation target `a`, has values between 0 and 0.5 - alpha: Confidence level, the target probability is 1 - alpha, has values between 0 and 1 - confint_method: Statistical method used to estimate the confidence intervals in - each iteration, can be 'chernoff' for the Chernoff intervals or 'beta' for the - Clopper-Pearson intervals (default) - min_ratio: Minimal q-ratio (:math:`K_{i+1} / K_i`) for FindNextK - quantum_instance: Deprecated: Quantum Instance or Backend - sampler: A sampler primitive to evaluate the circuits. - - Raises: - AlgorithmError: if the method to compute the confidence intervals is not supported - ValueError: If the target epsilon is not in (0, 0.5] - ValueError: If alpha is not in (0, 1) - ValueError: If confint_method is not supported - """ - # validate ranges of input arguments - if not 0 < epsilon_target <= 0.5: - raise ValueError(f"The target epsilon must be in (0, 0.5], but is {epsilon_target}.") - - if not 0 < alpha < 1: - raise ValueError(f"The confidence level alpha must be in (0, 1), but is {alpha}") - - if confint_method not in {"chernoff", "beta"}: - raise ValueError( - f"The confidence interval method must be chernoff or beta, but is {confint_method}." - ) - - super().__init__() - - # set quantum instance - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self.quantum_instance = quantum_instance - - # store parameters - self._epsilon = epsilon_target - self._alpha = alpha - self._min_ratio = min_ratio - self._confint_method = confint_method - self._sampler = sampler - - @property - def sampler(self) -> BaseSampler | None: - """Get the sampler primitive. - - Returns: - The sampler primitive to evaluate the circuits. - """ - return self._sampler - - @sampler.setter - def sampler(self, sampler: BaseSampler) -> None: - """Set sampler primitive. - - Args: - sampler: A sampler primitive to evaluate the circuits. - """ - self._sampler = sampler - - @property - @deprecate_func( - since="0.24.0", - is_property=True, - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def quantum_instance(self) -> QuantumInstance | None: - """Deprecated. Get the quantum instance. - - Returns: - The quantum instance used to run this algorithm. - """ - return self._quantum_instance - - @quantum_instance.setter - @deprecate_func( - since="0.24.0", - is_property=True, - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: - """Deprecated. Set quantum instance. - - Args: - quantum_instance: The quantum instance used to run this algorithm. - """ - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - self._quantum_instance = quantum_instance - - @property - def epsilon_target(self) -> float: - """Returns the target precision ``epsilon_target`` of the algorithm. - - Returns: - The target precision (which is half the width of the confidence interval). - """ - return self._epsilon - - @epsilon_target.setter - def epsilon_target(self, epsilon: float) -> None: - """Set the target precision of the algorithm. - - Args: - epsilon: Target precision for estimation target `a`. - """ - self._epsilon = epsilon - - def _find_next_k( - self, - k: int, - upper_half_circle: bool, - theta_interval: tuple[float, float], - min_ratio: float = 2.0, - ) -> tuple[int, bool]: - """Find the largest integer k_next, such that the interval (4 * k_next + 2)*theta_interval - lies completely in [0, pi] or [pi, 2pi], for theta_interval = (theta_lower, theta_upper). - - Args: - k: The current power of the Q operator. - upper_half_circle: Boolean flag of whether theta_interval lies in the - upper half-circle [0, pi] or in the lower one [pi, 2pi]. - theta_interval: The current confidence interval for the angle theta, - i.e. (theta_lower, theta_upper). - min_ratio: Minimal ratio K/K_next allowed in the algorithm. - - Returns: - The next power k, and boolean flag for the extrapolated interval. - - Raises: - AlgorithmError: if min_ratio is smaller or equal to 1 - """ - if min_ratio <= 1: - raise AlgorithmError("min_ratio must be larger than 1 to ensure convergence") - - # initialize variables - theta_l, theta_u = theta_interval - old_scaling = 4 * k + 2 # current scaling factor, called K := (4k + 2) - - # the largest feasible scaling factor K cannot be larger than K_max, - # which is bounded by the length of the current confidence interval - max_scaling = int(1 / (2 * (theta_u - theta_l))) - scaling = max_scaling - (max_scaling - 2) % 4 # bring into the form 4 * k_max + 2 - - # find the largest feasible scaling factor K_next, and thus k_next - while scaling >= min_ratio * old_scaling: - theta_min = scaling * theta_l - int(scaling * theta_l) - theta_max = scaling * theta_u - int(scaling * theta_u) - - if theta_min <= theta_max <= 0.5 and theta_min <= 0.5: - # the extrapolated theta interval is in the upper half-circle - upper_half_circle = True - return int((scaling - 2) / 4), upper_half_circle - - elif theta_max >= 0.5 and theta_max >= theta_min >= 0.5: - # the extrapolated theta interval is in the upper half-circle - upper_half_circle = False - return int((scaling - 2) / 4), upper_half_circle - - scaling -= 4 - - # if we do not find a feasible k, return the old one - return int(k), upper_half_circle - - def construct_circuit( - self, estimation_problem: EstimationProblem, k: int = 0, measurement: bool = False - ) -> QuantumCircuit: - r"""Construct the circuit :math:`\mathcal{Q}^k \mathcal{A} |0\rangle`. - - The A operator is the unitary specifying the QAE problem and Q the associated Grover - operator. - - Args: - estimation_problem: The estimation problem for which to construct the QAE circuit. - k: The power of the Q operator. - measurement: Boolean flag to indicate if measurements should be included in the - circuits. - - Returns: - The circuit implementing :math:`\mathcal{Q}^k \mathcal{A} |0\rangle`. - """ - num_qubits = max( - estimation_problem.state_preparation.num_qubits, - estimation_problem.grover_operator.num_qubits, - ) - circuit = QuantumCircuit(num_qubits, name="circuit") - - # add classical register if needed - if measurement: - c = ClassicalRegister(len(estimation_problem.objective_qubits)) - circuit.add_register(c) - - # add A operator - circuit.compose(estimation_problem.state_preparation, inplace=True) - - # add Q^k - if k != 0: - circuit.compose(estimation_problem.grover_operator.power(k), inplace=True) - - # add optional measurement - if measurement: - # real hardware can currently not handle operations after measurements, which might - # happen if the circuit gets transpiled, hence we're adding a safeguard-barrier - circuit.barrier() - circuit.measure(estimation_problem.objective_qubits, c[:]) - - return circuit - - def _good_state_probability( - self, - problem: EstimationProblem, - counts_or_statevector: dict[str, int] | np.ndarray, - num_state_qubits: int, - ) -> tuple[int, float] | float: - """Get the probability to measure '1' in the last qubit. - - Args: - problem: The estimation problem, used to obtain the number of objective qubits and - the ``is_good_state`` function. - counts_or_statevector: Either a counts-dictionary (with one measured qubit only!) or - the statevector returned from the statevector_simulator. - num_state_qubits: The number of state qubits. - - Returns: - If a dict is given, return (#one-counts, #one-counts/#all-counts), - otherwise Pr(measure '1' in the last qubit). - """ - if isinstance(counts_or_statevector, dict): - one_counts = 0 - for state, counts in counts_or_statevector.items(): - if problem.is_good_state(state): - one_counts += counts - - return int(one_counts), one_counts / sum(counts_or_statevector.values()) - else: - statevector = counts_or_statevector - num_qubits = int(np.log2(len(statevector))) # the total number of qubits - - # sum over all amplitudes where the objective qubit is 1 - prob = 0 - for i, amplitude in enumerate(statevector): - # consider only state qubits and revert bit order - bitstr = bin(i)[2:].zfill(num_qubits)[-num_state_qubits:][::-1] - objectives = [bitstr[index] for index in problem.objective_qubits] - if problem.is_good_state(objectives): - prob = prob + np.abs(amplitude) ** 2 - - return prob - - def estimate( - self, estimation_problem: EstimationProblem - ) -> "IterativeAmplitudeEstimationResult": - """Run the amplitude estimation algorithm on provided estimation problem. - - Args: - estimation_problem: The estimation problem. - - Returns: - An amplitude estimation results object. - - Raises: - ValueError: A quantum instance or Sampler must be provided. - AlgorithmError: Sampler job run error. - """ - if self._quantum_instance is None and self._sampler is None: - raise ValueError("A quantum instance or sampler must be provided.") - - # initialize memory variables - powers = [0] # list of powers k: Q^k, (called 'k' in paper) - ratios = [] # list of multiplication factors (called 'q' in paper) - theta_intervals = [[0, 1 / 4]] # a priori knowledge of theta / 2 / pi - a_intervals = [[0.0, 1.0]] # a priori knowledge of the confidence interval of the estimate - num_oracle_queries = 0 - num_one_shots = [] - - # maximum number of rounds - max_rounds = ( - int(np.log(self._min_ratio * np.pi / 8 / self._epsilon) / np.log(self._min_ratio)) + 1 - ) - upper_half_circle = True # initially theta is in the upper half-circle - - if self._quantum_instance is not None and self._quantum_instance.is_statevector: - # for statevector we can directly return the probability to measure 1 - # note, that no iterations here are necessary - # simulate circuit - circuit = self.construct_circuit(estimation_problem, k=0, measurement=False) - ret = self._quantum_instance.execute(circuit) - - # get statevector - statevector = ret.get_statevector(circuit) - - # calculate the probability of measuring '1' - num_qubits = circuit.num_qubits - circuit.num_ancillas - prob = self._good_state_probability(estimation_problem, statevector, num_qubits) - prob = cast(float, prob) # tell MyPy it's a float and not Tuple[int, float ] - - a_confidence_interval = [prob, prob] # type: list[float] - a_intervals.append(a_confidence_interval) - - theta_i_interval = [ - np.arccos(1 - 2 * a_i) / 2 / np.pi for a_i in a_confidence_interval # type: ignore - ] - theta_intervals.append(theta_i_interval) - num_oracle_queries = 0 # no Q-oracle call, only a single one to A - - else: - num_iterations = 0 # keep track of the number of iterations - # number of shots per iteration - shots = 0 - # do while loop, keep in mind that we scaled theta mod 2pi such that it lies in [0,1] - while theta_intervals[-1][1] - theta_intervals[-1][0] > self._epsilon / np.pi: - num_iterations += 1 - - # get the next k - k, upper_half_circle = self._find_next_k( - powers[-1], - upper_half_circle, - theta_intervals[-1], # type: ignore - min_ratio=self._min_ratio, - ) - - # store the variables - powers.append(k) - ratios.append((2 * powers[-1] + 1) / (2 * powers[-2] + 1)) - - # run measurements for Q^k A|0> circuit - circuit = self.construct_circuit(estimation_problem, k, measurement=True) - counts = {} - if self._quantum_instance is not None: - ret = self._quantum_instance.execute(circuit) - # get the counts and store them - counts = ret.get_counts(circuit) - shots = self._quantum_instance._run_config.shots - else: - try: - job = self._sampler.run([circuit]) - ret = job.result() - except Exception as exc: - raise AlgorithmError("The job was not completed successfully. ") from exc - - shots = ret.metadata[0].get("shots") - if shots is None: - circuit = self.construct_circuit(estimation_problem, k=0, measurement=True) - try: - job = self._sampler.run([circuit]) - ret = job.result() - except Exception as exc: - raise AlgorithmError( - "The job was not completed successfully. " - ) from exc - - # calculate the probability of measuring '1' - prob = 0.0 - for bit, probabilities in ret.quasi_dists[0].binary_probabilities().items(): - # check if it is a good state - if estimation_problem.is_good_state(bit): - prob += probabilities - - a_confidence_interval = [prob, prob] - a_intervals.append(a_confidence_interval) - - theta_i_interval = [ - np.arccos(1 - 2 * a_i) / 2 / np.pi for a_i in a_confidence_interval - ] - theta_intervals.append(theta_i_interval) - num_oracle_queries = 0 # no Q-oracle call, only a single one to A - break - - counts = { - k: round(v * shots) - for k, v in ret.quasi_dists[0].binary_probabilities().items() - } - - # calculate the probability of measuring '1', 'prob' is a_i in the paper - num_qubits = circuit.num_qubits - circuit.num_ancillas - # type: ignore - one_counts, prob = self._good_state_probability( - estimation_problem, counts, num_qubits - ) - - num_one_shots.append(one_counts) - - # track number of Q-oracle calls - num_oracle_queries += shots * k - - # if on the previous iterations we have K_{i-1} == K_i, we sum these samples up - j = 1 # number of times we stayed fixed at the same K - round_shots = shots - round_one_counts = one_counts - if num_iterations > 1: - while ( - powers[num_iterations - j] == powers[num_iterations] - and num_iterations >= j + 1 - ): - j = j + 1 - round_shots += shots - round_one_counts += num_one_shots[-j] - - # compute a_min_i, a_max_i - if self._confint_method == "chernoff": - a_i_min, a_i_max = _chernoff_confint(prob, round_shots, max_rounds, self._alpha) - else: # 'beta' - a_i_min, a_i_max = _clopper_pearson_confint( - round_one_counts, round_shots, self._alpha / max_rounds - ) - - # compute theta_min_i, theta_max_i - if upper_half_circle: - theta_min_i = np.arccos(1 - 2 * a_i_min) / 2 / np.pi - theta_max_i = np.arccos(1 - 2 * a_i_max) / 2 / np.pi - else: - theta_min_i = 1 - np.arccos(1 - 2 * a_i_max) / 2 / np.pi - theta_max_i = 1 - np.arccos(1 - 2 * a_i_min) / 2 / np.pi - - # compute theta_u, theta_l of this iteration - scaling = 4 * k + 2 # current K_i factor - theta_u = (int(scaling * theta_intervals[-1][1]) + theta_max_i) / scaling - theta_l = (int(scaling * theta_intervals[-1][0]) + theta_min_i) / scaling - theta_intervals.append([theta_l, theta_u]) - - # compute a_u_i, a_l_i - a_u = np.sin(2 * np.pi * theta_u) ** 2 - a_l = np.sin(2 * np.pi * theta_l) ** 2 - a_u = cast(float, a_u) - a_l = cast(float, a_l) - a_intervals.append([a_l, a_u]) - - # get the latest confidence interval for the estimate of a - confidence_interval = tuple(a_intervals[-1]) - - # the final estimate is the mean of the confidence interval - estimation = np.mean(confidence_interval) - - result = IterativeAmplitudeEstimationResult() - result.alpha = self._alpha - result.post_processing = estimation_problem.post_processing - result.num_oracle_queries = num_oracle_queries - - result.estimation = estimation - result.epsilon_estimated = (confidence_interval[1] - confidence_interval[0]) / 2 - result.confidence_interval = confidence_interval - - result.estimation_processed = estimation_problem.post_processing(estimation) - confidence_interval = tuple( - estimation_problem.post_processing(x) for x in confidence_interval - ) - result.confidence_interval_processed = confidence_interval - result.epsilon_estimated_processed = (confidence_interval[1] - confidence_interval[0]) / 2 - result.estimate_intervals = a_intervals - result.theta_intervals = theta_intervals - result.powers = powers - result.ratios = ratios - - return result - - -class IterativeAmplitudeEstimationResult(AmplitudeEstimatorResult): - """The ``IterativeAmplitudeEstimation`` result object.""" - - def __init__(self) -> None: - super().__init__() - self._alpha: float | None = None - self._epsilon_target: float | None = None - self._epsilon_estimated: float | None = None - self._epsilon_estimated_processed: float | None = None - self._estimate_intervals: list[list[float]] | None = None - self._theta_intervals: list[list[float]] | None = None - self._powers: list[int] | None = None - self._ratios: list[float] | None = None - self._confidence_interval_processed: tuple[float, float] | None = None - - @property - def alpha(self) -> float: - r"""Return the confidence level :math:`\alpha`.""" - return self._alpha - - @alpha.setter - def alpha(self, value: float) -> None: - r"""Set the confidence level :math:`\alpha`.""" - self._alpha = value - - @property - def epsilon_target(self) -> float: - """Return the target half-width of the confidence interval.""" - return self._epsilon_target - - @epsilon_target.setter - def epsilon_target(self, value: float) -> None: - """Set the target half-width of the confidence interval.""" - self._epsilon_target = value - - @property - def epsilon_estimated(self) -> float: - """Return the estimated half-width of the confidence interval.""" - return self._epsilon_estimated - - @epsilon_estimated.setter - def epsilon_estimated(self, value: float) -> None: - """Set the estimated half-width of the confidence interval.""" - self._epsilon_estimated = value - - @property - def epsilon_estimated_processed(self) -> float: - """Return the post-processed estimated half-width of the confidence interval.""" - return self._epsilon_estimated_processed - - @epsilon_estimated_processed.setter - def epsilon_estimated_processed(self, value: float) -> None: - """Set the post-processed estimated half-width of the confidence interval.""" - self._epsilon_estimated_processed = value - - @property - def estimate_intervals(self) -> list[list[float]]: - """Return the confidence intervals for the estimate in each iteration.""" - return self._estimate_intervals - - @estimate_intervals.setter - def estimate_intervals(self, value: list[list[float]]) -> None: - """Set the confidence intervals for the estimate in each iteration.""" - self._estimate_intervals = value - - @property - def theta_intervals(self) -> list[list[float]]: - """Return the confidence intervals for the angles in each iteration.""" - return self._theta_intervals - - @theta_intervals.setter - def theta_intervals(self, value: list[list[float]]) -> None: - """Set the confidence intervals for the angles in each iteration.""" - self._theta_intervals = value - - @property - def powers(self) -> list[int]: - """Return the powers of the Grover operator in each iteration.""" - return self._powers - - @powers.setter - def powers(self, value: list[int]) -> None: - """Set the powers of the Grover operator in each iteration.""" - self._powers = value - - @property - def ratios(self) -> list[float]: - r"""Return the ratios :math:`K_{i+1}/K_{i}` for each iteration :math:`i`.""" - return self._ratios - - @ratios.setter - def ratios(self, value: list[float]) -> None: - r"""Set the ratios :math:`K_{i+1}/K_{i}` for each iteration :math:`i`.""" - self._ratios = value - - @property - def confidence_interval_processed(self) -> tuple[float, float]: - """Return the post-processed confidence interval.""" - return self._confidence_interval_processed - - @confidence_interval_processed.setter - def confidence_interval_processed(self, value: tuple[float, float]) -> None: - """Set the post-processed confidence interval.""" - self._confidence_interval_processed = value - - -def _chernoff_confint( - value: float, shots: int, max_rounds: int, alpha: float -) -> tuple[float, float]: - """Compute the Chernoff confidence interval for `shots` i.i.d. Bernoulli trials. - - The confidence interval is - - [value - eps, value + eps], where eps = sqrt(3 * log(2 * max_rounds/ alpha) / shots) - - but at most [0, 1]. - - Args: - value: The current estimate. - shots: The number of shots. - max_rounds: The maximum number of rounds, used to compute epsilon_a. - alpha: The confidence level, used to compute epsilon_a. - - Returns: - The Chernoff confidence interval. - """ - eps = np.sqrt(3 * np.log(2 * max_rounds / alpha) / shots) - lower = np.maximum(0, value - eps) - upper = np.minimum(1, value + eps) - return lower, upper - - -def _clopper_pearson_confint(counts: int, shots: int, alpha: float) -> tuple[float, float]: - """Compute the Clopper-Pearson confidence interval for `shots` i.i.d. Bernoulli trials. - - Args: - counts: The number of positive counts. - shots: The number of shots. - alpha: The confidence level for the confidence interval. - - Returns: - The Clopper-Pearson confidence interval. - """ - lower, upper = 0, 1 - - # if counts == 0, the beta quantile returns nan - if counts != 0: - lower = beta.ppf(alpha / 2, counts, shots - counts + 1) - - # if counts == shots, the beta quantile returns nan - if counts != shots: - upper = beta.ppf(1 - alpha / 2, counts + 1, shots - counts) - - return lower, upper diff --git a/qiskit/algorithms/amplitude_estimators/mlae.py b/qiskit/algorithms/amplitude_estimators/mlae.py deleted file mode 100644 index aa9f600b700e..000000000000 --- a/qiskit/algorithms/amplitude_estimators/mlae.py +++ /dev/null @@ -1,667 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Maximum Likelihood Amplitude Estimation algorithm.""" - -from __future__ import annotations -import warnings -from collections.abc import Sequence -from typing import Callable, List, Tuple - -import numpy as np -from scipy.optimize import brute -from scipy.stats import norm, chi2 - -from qiskit.providers import Backend -from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit -from qiskit.utils import QuantumInstance -from qiskit.primitives import BaseSampler -from qiskit.utils.deprecation import deprecate_arg, deprecate_func - -from .amplitude_estimator import AmplitudeEstimator, AmplitudeEstimatorResult -from .estimation_problem import EstimationProblem -from ..exceptions import AlgorithmError - -MINIMIZER = Callable[[Callable[[float], float], List[Tuple[float, float]]], float] - - -class MaximumLikelihoodAmplitudeEstimation(AmplitudeEstimator): - """The Maximum Likelihood Amplitude Estimation algorithm. - - This class implements the quantum amplitude estimation (QAE) algorithm without phase - estimation, as introduced in [1]. In comparison to the original QAE algorithm [2], - this implementation relies solely on different powers of the Grover operator and does not - require additional evaluation qubits. - Finally, the estimate is determined via a maximum likelihood estimation, which is why this - class in named ``MaximumLikelihoodAmplitudeEstimation``. - - References: - [1]: Suzuki, Y., Uno, S., Raymond, R., Tanaka, T., Onodera, T., & Yamamoto, N. (2019). - Amplitude Estimation without Phase Estimation. - `arXiv:1904.10246 `_. - [2]: Brassard, G., Hoyer, P., Mosca, M., & Tapp, A. (2000). - Quantum Amplitude Amplification and Estimation. - `arXiv:quant-ph/0005055 `_. - """ - - @deprecate_arg( - "quantum_instance", - additional_msg=( - "Instead, use the ``sampler`` argument. See https://qisk.it/algo_migration for a " - "migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - evaluation_schedule: list[int] | int, - minimizer: MINIMIZER | None = None, - quantum_instance: QuantumInstance | Backend | None = None, - sampler: BaseSampler | None = None, - ) -> None: - r""" - Args: - evaluation_schedule: If a list, the powers applied to the Grover operator. The list - element must be non-negative. If a non-negative integer, an exponential schedule is - used where the highest power is 2 to the integer minus 1: - `[id, Q^2^0, ..., Q^2^(evaluation_schedule-1)]`. - minimizer: A minimizer used to find the minimum of the likelihood function. - Defaults to a brute search where the number of evaluation points is determined - according to ``evaluation_schedule``. The minimizer takes a function as first - argument and a list of (float, float) tuples (as bounds) as second argument and - returns a single float which is the found minimum. - quantum_instance: Deprecated: Quantum Instance or Backend - sampler: A sampler primitive to evaluate the circuits. - - Raises: - ValueError: If the number of oracle circuits is smaller than 1. - """ - - super().__init__() - - # set quantum instance - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self.quantum_instance = quantum_instance - - # get parameters - if isinstance(evaluation_schedule, int): - if evaluation_schedule < 0: - raise ValueError("The evaluation schedule cannot be < 0.") - - self._evaluation_schedule = [0] + [2**j for j in range(evaluation_schedule)] - else: - if any(value < 0 for value in evaluation_schedule): - raise ValueError("The elements of the evaluation schedule cannot be < 0.") - - self._evaluation_schedule = evaluation_schedule - - if minimizer is None: - # default number of evaluations is max(10^4, pi/2 * 10^3 * 2^(m)) - nevals = max(10000, int(np.pi / 2 * 1000 * 2 * self._evaluation_schedule[-1])) - - def default_minimizer(objective_fn, bounds): - return brute(objective_fn, bounds, Ns=nevals)[0] - - self._minimizer = default_minimizer - else: - self._minimizer = minimizer - - self._sampler = sampler - - @property - def sampler(self) -> BaseSampler | None: - """Get the sampler primitive. - - Returns: - The sampler primitive to evaluate the circuits. - """ - return self._sampler - - @sampler.setter - def sampler(self, sampler: BaseSampler) -> None: - """Set sampler primitive. - - Args: - sampler: A sampler primitive to evaluate the circuits. - """ - self._sampler = sampler - - @property - @deprecate_func( - since="0.24.0", - is_property=True, - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def quantum_instance(self) -> QuantumInstance | None: - """Deprecated. Get the quantum instance. - - Returns: - The quantum instance used to run this algorithm. - """ - return self._quantum_instance - - @quantum_instance.setter - @deprecate_func( - since="0.24.0", - is_property=True, - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: - """Deprecated. Set quantum instance. - - Args: - quantum_instance: The quantum instance used to run this algorithm. - """ - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - self._quantum_instance = quantum_instance - - def construct_circuits( - self, estimation_problem: EstimationProblem, measurement: bool = False - ) -> list[QuantumCircuit]: - """Construct the Amplitude Estimation w/o QPE quantum circuits. - - Args: - estimation_problem: The estimation problem for which to construct the QAE circuit. - measurement: Boolean flag to indicate if measurement should be included in the circuits. - - Returns: - A list with the QuantumCircuit objects for the algorithm. - """ - # keep track of the Q-oracle queries - circuits = [] - - num_qubits = max( - estimation_problem.state_preparation.num_qubits, - estimation_problem.grover_operator.num_qubits, - ) - q = QuantumRegister(num_qubits, "q") - qc_0 = QuantumCircuit(q, name="qc_a") # 0 applications of Q, only a single A operator - - # add classical register if needed - if measurement: - c = ClassicalRegister(len(estimation_problem.objective_qubits)) - qc_0.add_register(c) - - qc_0.compose(estimation_problem.state_preparation, inplace=True) - - for k in self._evaluation_schedule: - qc_k = qc_0.copy(name=f"qc_a_q_{k}") - - if k != 0: - qc_k.compose(estimation_problem.grover_operator.power(k), inplace=True) - - if measurement: - # real hardware can currently not handle operations after measurements, - # which might happen if the circuit gets transpiled, hence we're adding - # a safeguard-barrier - qc_k.barrier() - qc_k.measure(estimation_problem.objective_qubits, c[:]) - - circuits += [qc_k] - - return circuits - - @staticmethod - def compute_confidence_interval( - result: "MaximumLikelihoodAmplitudeEstimationResult", - alpha: float, - kind: str = "fisher", - apply_post_processing: bool = False, - ) -> tuple[float, float]: - """Compute the `alpha` confidence interval using the method `kind`. - - The confidence level is (1 - `alpha`) and supported kinds are 'fisher', - 'likelihood_ratio' and 'observed_fisher' with shorthand - notations 'fi', 'lr' and 'oi', respectively. - - Args: - result: A maximum likelihood amplitude estimation result. - alpha: The confidence level. - kind: The method to compute the confidence interval. Defaults to 'fisher', which - computes the theoretical Fisher information. - apply_post_processing: If True, apply post-processing to the confidence interval. - - Returns: - The specified confidence interval. - - Raises: - AlgorithmError: If `run()` hasn't been called yet. - NotImplementedError: If the method `kind` is not supported. - """ - interval: tuple[float, float] | None = None - - # if statevector simulator the estimate is exact - if all(isinstance(data, (list, np.ndarray)) for data in result.circuit_results): - interval = (result.estimation, result.estimation) - - elif kind in ["likelihood_ratio", "lr"]: - interval = _likelihood_ratio_confint(result, alpha) - - elif kind in ["fisher", "fi"]: - interval = _fisher_confint(result, alpha, observed=False) - - elif kind in ["observed_fisher", "observed_information", "oi"]: - interval = _fisher_confint(result, alpha, observed=True) - - if interval is None: - raise NotImplementedError(f"CI `{kind}` is not implemented.") - - if apply_post_processing: - return result.post_processing(interval[0]), result.post_processing(interval[1]) - - return interval - - def compute_mle( - self, - circuit_results: list[dict[str, int] | np.ndarray], - estimation_problem: EstimationProblem, - num_state_qubits: int | None = None, - return_counts: bool = False, - ) -> float | tuple[float, list[float]]: - """Compute the MLE via a grid-search. - - This is a stable approach if sufficient gridpoints are used. - - Args: - circuit_results: A list of circuit outcomes. Can be counts or statevectors. - estimation_problem: The estimation problem containing the evaluation schedule and the - number of likelihood function evaluations used to find the minimum. - num_state_qubits: The number of state qubits, required for statevector simulations. - return_counts: If True, returns the good counts. - - Returns: - The MLE for the provided result object. - """ - good_counts, all_counts = _get_counts(circuit_results, estimation_problem, num_state_qubits) - - # search range - eps = 1e-15 # to avoid invalid value in log - search_range = [0 + eps, np.pi / 2 - eps] - - def loglikelihood(theta): - # loglik contains the first `it` terms of the full loglikelihood - loglik = 0 - for i, k in enumerate(self._evaluation_schedule): - angle = (2 * k + 1) * theta - loglik += np.log(np.sin(angle) ** 2) * good_counts[i] - loglik += np.log(np.cos(angle) ** 2) * (all_counts[i] - good_counts[i]) - return -loglik - - est_theta = self._minimizer(loglikelihood, [search_range]) - - if return_counts: - return est_theta, good_counts - return est_theta - - def estimate( - self, estimation_problem: EstimationProblem - ) -> "MaximumLikelihoodAmplitudeEstimationResult": - """Run the amplitude estimation algorithm on provided estimation problem. - - Args: - estimation_problem: The estimation problem. - - Returns: - An amplitude estimation results object. - - Raises: - ValueError: A quantum instance or Sampler must be provided. - AlgorithmError: If `state_preparation` is not set in - `estimation_problem`. - AlgorithmError: Sampler job run error - """ - if self._quantum_instance is None and self._sampler is None: - raise ValueError("A quantum instance or sampler must be provided.") - if estimation_problem.state_preparation is None: - raise AlgorithmError( - "The state_preparation property of the estimation problem must be set." - ) - - result = MaximumLikelihoodAmplitudeEstimationResult() - result.evaluation_schedule = self._evaluation_schedule - result.minimizer = self._minimizer - result.post_processing = estimation_problem.post_processing - - shots = 0 - if self._quantum_instance is not None and self._quantum_instance.is_statevector: - # run circuit on statevector simulator - circuits = self.construct_circuits(estimation_problem, measurement=False) - ret = self._quantum_instance.execute(circuits) - - # get statevectors and construct MLE input - statevectors = [np.asarray(ret.get_statevector(circuit)) for circuit in circuits] - result.circuit_results = statevectors - - # to count the number of Q-oracle calls (don't count shots) - result.shots = 1 - else: - circuits = self.construct_circuits(estimation_problem, measurement=True) - if self._quantum_instance is not None: - # run circuit on QASM simulator - ret = self._quantum_instance.execute(circuits) - # get counts and construct MLE input - result.circuit_results = [ret.get_counts(circuit) for circuit in circuits] - shots = self._quantum_instance._run_config.shots - else: - try: - job = self._sampler.run(circuits) - ret = job.result() - except Exception as exc: - raise AlgorithmError("The job was not completed successfully. ") from exc - - result.circuit_results = [] - shots = ret.metadata[0].get("shots") - if shots is None: - for quasi_dist in ret.quasi_dists: - circuit_result = quasi_dist.binary_probabilities() - result.circuit_results.append(circuit_result) - shots = 1 - else: - # get counts and construct MLE input - for quasi_dist in ret.quasi_dists: - counts = { - k: round(v * shots) - for k, v in quasi_dist.binary_probabilities().items() - } - result.circuit_results.append(counts) - - result.shots = shots - - # run maximum likelihood estimation - num_state_qubits = circuits[0].num_qubits - circuits[0].num_ancillas - theta, good_counts = self.compute_mle( - result.circuit_results, estimation_problem, num_state_qubits, True - ) - - # store results - result.theta = theta - result.good_counts = good_counts - result.estimation = np.sin(result.theta) ** 2 - - # not sure why pylint complains, this is a callable and the tests pass - # pylint: disable=not-callable - result.estimation_processed = result.post_processing(result.estimation) - - result.fisher_information = _compute_fisher_information(result) - result.num_oracle_queries = result.shots * sum(k for k in result.evaluation_schedule) - - # compute and store confidence interval - confidence_interval = self.compute_confidence_interval(result, alpha=0.05, kind="fisher") - result.confidence_interval = confidence_interval - result.confidence_interval_processed = tuple( - estimation_problem.post_processing(value) for value in confidence_interval - ) - - return result - - -class MaximumLikelihoodAmplitudeEstimationResult(AmplitudeEstimatorResult): - """The ``MaximumLikelihoodAmplitudeEstimation`` result object.""" - - def __init__(self) -> None: - super().__init__() - self._theta: float | None = None - self._minimizer: Callable | None = None - self._good_counts: list[float] | None = None - self._evaluation_schedule: list[int] | None = None - self._fisher_information: float | None = None - - @property - def theta(self) -> float: - r"""Return the estimate for the angle :math:`\theta`.""" - return self._theta - - @theta.setter - def theta(self, value: float) -> None: - r"""Set the estimate for the angle :math:`\theta`.""" - self._theta = value - - @property - def minimizer(self) -> Callable: - """Return the minimizer used for the search of the likelihood function.""" - return self._minimizer - - @minimizer.setter - def minimizer(self, value: Callable) -> None: - """Set the number minimizer used for the search of the likelihood function.""" - self._minimizer = value - - @property - def good_counts(self) -> list[float]: - """Return the percentage of good counts per circuit power.""" - return self._good_counts - - @good_counts.setter - def good_counts(self, counts: list[float]) -> None: - """Set the percentage of good counts per circuit power.""" - self._good_counts = counts - - @property - def evaluation_schedule(self) -> list[int]: - """Return the evaluation schedule for the powers of the Grover operator.""" - return self._evaluation_schedule - - @evaluation_schedule.setter - def evaluation_schedule(self, evaluation_schedule: list[int]) -> None: - """Set the evaluation schedule for the powers of the Grover operator.""" - self._evaluation_schedule = evaluation_schedule - - @property - def fisher_information(self) -> float: - """Return the Fisher information for the estimated amplitude.""" - return self._fisher_information - - @fisher_information.setter - def fisher_information(self, value: float) -> None: - """Set the Fisher information for the estimated amplitude.""" - self._fisher_information = value - - -def _safe_min(array, default=0): - if len(array) == 0: - return default - return np.min(array) - - -def _safe_max(array, default=(np.pi / 2)): - if len(array) == 0: - return default - return np.max(array) - - -def _compute_fisher_information( - result: "MaximumLikelihoodAmplitudeEstimationResult", - num_sum_terms: int | None = None, - observed: bool = False, -) -> float: - """Compute the Fisher information. - - Args: - result: A maximum likelihood amplitude estimation result. - num_sum_terms: The number of sum terms to be included in the calculation of the - Fisher information. By default all values are included. - observed: If True, compute the observed Fisher information, otherwise the theoretical - one. - - Returns: - The computed Fisher information, or np.inf if statevector simulation was used. - - Raises: - KeyError: Call run() first! - """ - a = result.estimation - - # Corresponding angle to the value a (only use real part of 'a') - theta_a = np.arcsin(np.sqrt(np.real(a))) - - # Get the number of hits (shots_k) and one-hits (h_k) - one_hits = result.good_counts - all_hits = [result.shots] * len(one_hits) - - # Include all sum terms or just up to a certain term? - evaluation_schedule = result.evaluation_schedule - if num_sum_terms is not None: - evaluation_schedule = evaluation_schedule[:num_sum_terms] - # not necessary since zip goes as far as shortest list: - # all_hits = all_hits[:num_sum_terms] - # one_hits = one_hits[:num_sum_terms] - - # Compute the Fisher information - fisher_information = None - if observed: - # Note, that the observed Fisher information is very unreliable in this algorithm! - d_loglik = 0 - for shots_k, h_k, m_k in zip(all_hits, one_hits, evaluation_schedule): - tan = np.tan((2 * m_k + 1) * theta_a) - d_loglik += (2 * m_k + 1) * (h_k / tan + (shots_k - h_k) * tan) - - d_loglik /= np.sqrt(a * (1 - a)) - fisher_information = d_loglik**2 / len(all_hits) - - else: - fisher_information = sum( - shots_k * (2 * m_k + 1) ** 2 for shots_k, m_k in zip(all_hits, evaluation_schedule) - ) - fisher_information /= a * (1 - a) - - return fisher_information - - -def _fisher_confint( - result: MaximumLikelihoodAmplitudeEstimationResult, alpha: float = 0.05, observed: bool = False -) -> tuple[float, float]: - """Compute the `alpha` confidence interval based on the Fisher information. - - Args: - result: A maximum likelihood amplitude estimation results object. - alpha: The level of the confidence interval (must be <= 0.5), default to 0.05. - observed: If True, use observed Fisher information. - - Returns: - float: The alpha confidence interval based on the Fisher information - Raises: - AssertionError: Call run() first! - """ - # Get the (observed) Fisher information - fisher_information = None - try: - fisher_information = result.fisher_information - except KeyError as ex: - raise AssertionError("Call run() first!") from ex - - if observed: - fisher_information = _compute_fisher_information(result, observed=True) - - normal_quantile = norm.ppf(1 - alpha / 2) - confint = np.real(result.estimation) + normal_quantile / np.sqrt(fisher_information) * np.array( - [-1, 1] - ) - return result.post_processing(confint[0]), result.post_processing(confint[1]) - - -def _likelihood_ratio_confint( - result: MaximumLikelihoodAmplitudeEstimationResult, - alpha: float = 0.05, - nevals: int | None = None, -) -> tuple[float, float]: - """Compute the likelihood-ratio confidence interval. - - Args: - result: A maximum likelihood amplitude estimation results object. - alpha: The level of the confidence interval (< 0.5), defaults to 0.05. - nevals: The number of evaluations to find the intersection with the loglikelihood - function. Defaults to an adaptive value based on the maximal power of Q. - - Returns: - The alpha-likelihood-ratio confidence interval. - """ - if nevals is None: - nevals = max(10000, int(np.pi / 2 * 1000 * 2 * result.evaluation_schedule[-1])) - - def loglikelihood(theta, one_counts, all_counts): - loglik = 0 - for i, k in enumerate(result.evaluation_schedule): - loglik += np.log(np.sin((2 * k + 1) * theta) ** 2) * one_counts[i] - loglik += np.log(np.cos((2 * k + 1) * theta) ** 2) * (all_counts[i] - one_counts[i]) - return loglik - - one_counts = result.good_counts - all_counts = [result.shots] * len(one_counts) - - eps = 1e-15 # to avoid invalid value in log - thetas = np.linspace(0 + eps, np.pi / 2 - eps, nevals) - values = np.zeros(len(thetas)) - for i, theta in enumerate(thetas): - values[i] = loglikelihood(theta, one_counts, all_counts) - - loglik_mle = loglikelihood(result.theta, one_counts, all_counts) - chi2_quantile = chi2.ppf(1 - alpha, df=1) - thres = loglik_mle - chi2_quantile / 2 - - # the (outer) LR confidence interval - above_thres = thetas[values >= thres] - - # it might happen that the `above_thres` array is empty, - # to still provide a valid result use safe_min/max which - # then yield [0, pi/2] - confint = [_safe_min(above_thres, default=0), _safe_max(above_thres, default=np.pi / 2)] - mapped_confint = tuple(result.post_processing(np.sin(bound) ** 2) for bound in confint) - - return mapped_confint - - -def _get_counts( - circuit_results: Sequence[np.ndarray | list[float] | dict[str, int]], - estimation_problem: EstimationProblem, - num_state_qubits: int, -) -> tuple[list[float], list[int]]: - """Get the good and total counts. - - Returns: - A pair of two lists, ([1-counts per experiment], [shots per experiment]). - - Raises: - AlgorithmError: If self.run() has not been called yet. - """ - one_hits = [] # h_k: how often 1 has been measured, for a power Q^(m_k) - # shots_k: how often has been measured at a power Q^(m_k) - all_hits: np.ndarray | list[float] = [] - if all(isinstance(data, (list, np.ndarray)) for data in circuit_results): - probabilities = [] - num_qubits = int(np.log2(len(circuit_results[0]))) # the total number of qubits - for statevector in circuit_results: - p_k = 0.0 - for i, amplitude in enumerate(statevector): - probability = np.abs(amplitude) ** 2 - # consider only state qubits and revert bit order - bitstr = bin(i)[2:].zfill(num_qubits)[-num_state_qubits:][::-1] - objectives = [bitstr[index] for index in estimation_problem.objective_qubits] - if estimation_problem.is_good_state(objectives): - p_k += probability - probabilities += [p_k] - - one_hits = probabilities - all_hits = np.ones_like(one_hits) - else: - for counts in circuit_results: - all_hits.append(sum(counts.values())) - one_hits.append( - sum( - count - for bitstr, count in counts.items() - if estimation_problem.is_good_state(bitstr) - ) - ) - - return one_hits, all_hits diff --git a/qiskit/algorithms/aux_ops_evaluator.py b/qiskit/algorithms/aux_ops_evaluator.py deleted file mode 100644 index 788b66d6e43f..000000000000 --- a/qiskit/algorithms/aux_ops_evaluator.py +++ /dev/null @@ -1,195 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Evaluator of auxiliary operators for algorithms.""" - -from __future__ import annotations - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.opflow import ( - CircuitSampler, - ListOp, - StateFn, - OperatorBase, - ExpectationBase, -) -from qiskit.providers import Backend -from qiskit.quantum_info import Statevector -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_func - -from .list_or_dict import ListOrDict - - -@deprecate_func( - additional_msg=( - "Instead, use the function " - "``qiskit.algorithms.observables_evaluator.estimate_observables``. See " - "https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", -) -def eval_observables( - quantum_instance: QuantumInstance | Backend, - quantum_state: Statevector | QuantumCircuit | OperatorBase, - observables: ListOrDict[OperatorBase], - expectation: ExpectationBase, - threshold: float = 1e-12, -) -> ListOrDict[tuple[complex, complex]]: - """ - Deprecated: Accepts a list or a dictionary of operators and calculates - their expectation values - means - and standard deviations. They are calculated with respect to a quantum state provided. A user - can optionally provide a threshold value which filters mean values falling below the threshold. - - This function has been superseded by the - :func:`qiskit.algorithms.observables_evaluator.eval_observables` function. - It will be deprecated in a future release and subsequently - removed after that. - - Args: - quantum_instance: A quantum instance used for calculations. - quantum_state: An unparametrized quantum circuit representing a quantum state that - expectation values are computed against. - observables: A list or a dictionary of operators whose expectation values are to be - calculated. - expectation: An instance of ExpectationBase which defines a method for calculating - expectation values. - threshold: A threshold value that defines which mean values should be neglected (helpful for - ignoring numerical instabilities close to 0). - - Returns: - A list or a dictionary of tuples (mean, standard deviation). - - Raises: - ValueError: If a ``quantum_state`` with free parameters is provided. - """ - - if ( - isinstance( - quantum_state, (QuantumCircuit, OperatorBase) - ) # Statevector cannot be parametrized - and len(quantum_state.parameters) > 0 - ): - raise ValueError( - "A parametrized representation of a quantum_state was provided. It is not " - "allowed - it cannot have free parameters." - ) - - # Create new CircuitSampler to avoid breaking existing one's caches. - sampler = CircuitSampler(quantum_instance) - - list_op = _prepare_list_op(quantum_state, observables) - observables_expect = expectation.convert(list_op) - observables_expect_sampled = sampler.convert(observables_expect) - - # compute means - values = np.real(observables_expect_sampled.eval()) - - # compute standard deviations - # We use sampler.quantum_instance to take care of case in which quantum_instance is Backend - std_devs = _compute_std_devs( - observables_expect_sampled, observables, expectation, sampler.quantum_instance - ) - - # Discard values below threshold - observables_means = values * (np.abs(values) > threshold) - # zip means and standard deviations into tuples - observables_results = list(zip(observables_means, std_devs)) - - # Return None eigenvalues for None operators if observables is a list. - # None operators are already dropped in compute_minimum_eigenvalue if observables is a dict. - - return _prepare_result(observables_results, observables) - - -def _prepare_list_op( - quantum_state: Statevector | QuantumCircuit | OperatorBase, - observables: ListOrDict[OperatorBase], -) -> ListOp: - """ - Accepts a list or a dictionary of operators and converts them to a ``ListOp``. - - Args: - quantum_state: An unparametrized quantum circuit representing a quantum state that - expectation values are computed against. - observables: A list or a dictionary of operators. - - Returns: - A ``ListOp`` that includes all provided observables. - """ - if isinstance(observables, dict): - observables = list(observables.values()) - - if not isinstance(quantum_state, StateFn): - quantum_state = StateFn(quantum_state) - - return ListOp([StateFn(obs, is_measurement=True).compose(quantum_state) for obs in observables]) - - -def _prepare_result( - observables_results: list[tuple[complex, complex]], - observables: ListOrDict[OperatorBase], -) -> ListOrDict[tuple[complex, complex]]: - """ - Prepares a list or a dictionary of eigenvalues from ``observables_results`` and - ``observables``. - - Args: - observables_results: A list of of tuples (mean, standard deviation). - observables: A list or a dictionary of operators whose expectation values are to be - calculated. - - Returns: - A list or a dictionary of tuples (mean, standard deviation). - """ - if isinstance(observables, list): - observables_eigenvalues: ListOrDict[tuple[complex, complex]] = [None] * len(observables) - key_value_iterator = enumerate(observables_results) - else: - observables_eigenvalues = {} - key_value_iterator = zip(observables.keys(), observables_results) - for key, value in key_value_iterator: - if observables[key] is not None: - observables_eigenvalues[key] = value - return observables_eigenvalues - - -def _compute_std_devs( - observables_expect_sampled: OperatorBase, - observables: ListOrDict[OperatorBase], - expectation: ExpectationBase, - quantum_instance: QuantumInstance | Backend, -) -> list[complex]: - """ - Calculates a list of standard deviations from expectation values of observables provided. - - Args: - observables_expect_sampled: Expected values of observables. - observables: A list or a dictionary of operators whose expectation values are to be - calculated. - expectation: An instance of ExpectationBase which defines a method for calculating - expectation values. - quantum_instance: A quantum instance used for calculations. - - Returns: - A list of standard deviations. - """ - variances = np.real(expectation.compute_variance(observables_expect_sampled)) - if not isinstance(variances, np.ndarray) and variances == 0.0: - # when `variances` is a single value equal to 0., our expectation value is exact and we - # manually ensure the variances to be a list of the correct length - variances = np.zeros(len(observables), dtype=float) - # TODO: this will crash if quantum_instance is a backend - std_devs = np.sqrt(variances / quantum_instance.run_config.shots) - return std_devs diff --git a/qiskit/algorithms/eigen_solvers/__init__.py b/qiskit/algorithms/eigen_solvers/__init__.py deleted file mode 100644 index 90cab015b8f5..000000000000 --- a/qiskit/algorithms/eigen_solvers/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Eigen Solvers Package""" - -from .numpy_eigen_solver import NumPyEigensolver -from .eigen_solver import Eigensolver, EigensolverResult -from .vqd import VQD, VQDResult - -__all__ = ["NumPyEigensolver", "Eigensolver", "EigensolverResult", "VQD", "VQDResult"] diff --git a/qiskit/algorithms/eigen_solvers/eigen_solver.py b/qiskit/algorithms/eigen_solvers/eigen_solver.py deleted file mode 100644 index 5fd59b3e023c..000000000000 --- a/qiskit/algorithms/eigen_solvers/eigen_solver.py +++ /dev/null @@ -1,134 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Eigensolver interface""" -from __future__ import annotations -from abc import ABC, abstractmethod - -import numpy as np - -from qiskit.opflow import OperatorBase -from qiskit.utils.deprecation import deprecate_func -from ..algorithm_result import AlgorithmResult -from ..list_or_dict import ListOrDict - - -class Eigensolver(ABC): - """Deprecated: Eigensolver Interface. - - The Eigensolver interface has been superseded by the - :class:`qiskit.algorithms.eigensolvers.Eigensolver` interface. - This interface will be deprecated in a future release and subsequently - removed after that. - - Algorithms that can compute eigenvalues for an operator - may implement this interface to allow different algorithms to be - used interchangeably. - """ - - @deprecate_func( - additional_msg=( - "Instead, use the interface ``qiskit.algorithms.eigensolvers.Eigensolver``. See " - "https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__(self) -> None: - pass - - @abstractmethod - def compute_eigenvalues( - self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None - ) -> "EigensolverResult": - """ - Computes eigenvalues. Operator and aux_operators can be supplied here and - if not None will override any already set into algorithm so it can be reused with - different operators. While an operator is required by algorithms, aux_operators - are optional. To 'remove' a previous aux_operators array use an empty list here. - - Args: - operator: Qubit operator of the Observable - aux_operators: Optional list of auxiliary operators to be evaluated with the - eigenstate of the minimum eigenvalue main result and their expectation values - returned. For instance in chemistry these can be dipole operators, total particle - count operators so we can get values for these at the ground state. - - Returns: - EigensolverResult - """ - return EigensolverResult() - - @classmethod - def supports_aux_operators(cls) -> bool: - """Whether computing the expectation value of auxiliary operators is supported. - - Returns: - True if aux_operator expectations can be evaluated, False otherwise - """ - return False - - -class EigensolverResult(AlgorithmResult): - """Deprecated: Eigensolver Result. - - The EigensolverResult class has been superseded by the - :class:`qiskit.algorithms.eigensolvers.EigensolverResult` class. - This class will be deprecated in a future release and subsequently - removed after that. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.eigensolvers.EigensolverResult``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__(self) -> None: - super().__init__() - self._eigenvalues: np.ndarray | None = None - self._eigenstates: np.ndarray | None = None - self._aux_operator_eigenvalues: list[ListOrDict[tuple[complex, complex]]] | None = None - - @property - def eigenvalues(self) -> np.ndarray | None: - """returns eigen values""" - return self._eigenvalues - - @eigenvalues.setter - def eigenvalues(self, value: np.ndarray) -> None: - """set eigen values""" - self._eigenvalues = value - - @property - def eigenstates(self) -> np.ndarray | None: - """return eigen states""" - return self._eigenstates - - @eigenstates.setter - def eigenstates(self, value: np.ndarray) -> None: - """set eigen states""" - self._eigenstates = value - - @property - def aux_operator_eigenvalues(self) -> list[ListOrDict[tuple[complex, complex]]] | None: - """Return aux operator expectation values. - - These values are in fact tuples formatted as (mean, standard deviation). - """ - return self._aux_operator_eigenvalues - - @aux_operator_eigenvalues.setter - def aux_operator_eigenvalues(self, value: list[ListOrDict[tuple[complex, complex]]]) -> None: - """set aux operator eigen values""" - self._aux_operator_eigenvalues = value diff --git a/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py b/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py deleted file mode 100644 index 6b0536330441..000000000000 --- a/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py +++ /dev/null @@ -1,278 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Eigensolver algorithm.""" -from __future__ import annotations - -import logging -import warnings -from collections.abc import Callable - -import numpy as np -from scipy import sparse as scisparse - -from qiskit.opflow import I, ListOp, OperatorBase, StateFn -from qiskit.utils.validation import validate_min -from qiskit.utils.deprecation import deprecate_func -from ..exceptions import AlgorithmError -from .eigen_solver import Eigensolver, EigensolverResult -from ..list_or_dict import ListOrDict - -logger = logging.getLogger(__name__) - - -class NumPyEigensolver(Eigensolver): - r""" - Deprecated: NumPy Eigensolver algorithm. - - The NumPyEigensolver class has been superseded by the - :class:`qiskit.algorithms.eigensolvers.NumPyEigensolver` class. - This class will be deprecated in a future release and subsequently - removed after that. - - NumPy Eigensolver computes up to the first :math:`k` eigenvalues of a complex-valued square - matrix of dimension :math:`n \times n`, with :math:`k \leq n`. - - Note: - Operators are automatically converted to SciPy's ``spmatrix`` - as needed and this conversion can be costly in terms of memory and performance as the - operator size, mostly in terms of number of qubits it represents, gets larger. - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.eigensolvers.NumPyEigensolver``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - k: int = 1, - filter_criterion: Callable[ - [list | np.ndarray, float, ListOrDict[float] | None], bool - ] = None, - ) -> None: - """ - Args: - k: How many eigenvalues are to be computed, has a min. value of 1. - filter_criterion: callable that allows to filter eigenvalues/eigenstates, only feasible - eigenstates are returned in the results. The callable has the signature - `filter(eigenstate, eigenvalue, aux_values)` and must return a boolean to indicate - whether to keep this value in the final returned result or not. If the number of - elements that satisfies the criterion is smaller than `k` then the returned list has - fewer elements and can even be empty. - """ - validate_min("k", k, 1) - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__init__() - - self._in_k = k - self._k = k - - self._filter_criterion = filter_criterion - - self._ret = EigensolverResult() - - @property - def k(self) -> int: - """returns k (number of eigenvalues requested)""" - return self._in_k - - @k.setter - def k(self, k: int) -> None: - """set k (number of eigenvalues requested)""" - validate_min("k", k, 1) - self._in_k = k - self._k = k - - @property - def filter_criterion( - self, - ) -> Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] | None: - """returns the filter criterion if set""" - return self._filter_criterion - - @filter_criterion.setter - def filter_criterion( - self, - filter_criterion: Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] - | None, - ) -> None: - """set the filter criterion""" - self._filter_criterion = filter_criterion - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - def _check_set_k(self, operator: OperatorBase) -> None: - if operator is not None: - if self._in_k > 2**operator.num_qubits: - self._k = 2**operator.num_qubits - logger.debug( - "WARNING: Asked for %s eigenvalues but max possible is %s.", self._in_k, self._k - ) - else: - self._k = self._in_k - - def _solve(self, operator: OperatorBase) -> None: - sp_mat = operator.to_spmatrix() - # If matrix is diagonal, the elements on the diagonal are the eigenvalues. Solve by sorting. - if scisparse.csr_matrix(sp_mat.diagonal()).nnz == sp_mat.nnz: - diag = sp_mat.diagonal() - indices = np.argsort(diag)[: self._k] - eigval = diag[indices] - eigvec = np.zeros((sp_mat.shape[0], self._k)) - for i, idx in enumerate(indices): - eigvec[idx, i] = 1.0 - else: - if self._k >= 2**operator.num_qubits - 1: - logger.debug("SciPy doesn't support to get all eigenvalues, using NumPy instead.") - if operator.is_hermitian(): - eigval, eigvec = np.linalg.eigh(operator.to_matrix()) - else: - eigval, eigvec = np.linalg.eig(operator.to_matrix()) - else: - if operator.is_hermitian(): - eigval, eigvec = scisparse.linalg.eigsh(sp_mat, k=self._k, which="SA") - else: - eigval, eigvec = scisparse.linalg.eigs(sp_mat, k=self._k, which="SR") - indices = np.argsort(eigval)[: self._k] - eigval = eigval[indices] - eigvec = eigvec[:, indices] - self._ret.eigenvalues = eigval - self._ret.eigenstates = eigvec.T - - def _get_ground_state_energy(self, operator: OperatorBase) -> None: - if self._ret.eigenvalues is None or self._ret.eigenstates is None: - self._solve(operator) - - def _get_energies( - self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None - ) -> None: - if self._ret.eigenvalues is None or self._ret.eigenstates is None: - self._solve(operator) - - if aux_operators is not None: - aux_op_vals = [] - for i in range(self._k): - aux_op_vals.append( - self._eval_aux_operators(aux_operators, self._ret.eigenstates[i]) - ) - self._ret.aux_operator_eigenvalues = aux_op_vals - - @staticmethod - def _eval_aux_operators( - aux_operators: ListOrDict[OperatorBase], wavefn, threshold: float = 1e-12 - ) -> ListOrDict[tuple[complex, complex]]: - - values: ListOrDict[tuple[complex, complex]] - - # As a list, aux_operators can contain None operators for which None values are returned. - # As a dict, the None operators in aux_operators have been dropped in compute_eigenvalues. - if isinstance(aux_operators, list): - values = [None] * len(aux_operators) - key_op_iterator = enumerate(aux_operators) - else: - values = {} - key_op_iterator = aux_operators.items() - for key, operator in key_op_iterator: - if operator is None: - continue - value = 0.0 - if operator.coeff != 0: - mat = operator.to_spmatrix() - # Terra doesn't support sparse yet, so do the matmul directly if so - # This is necessary for the particle_hole and other chemistry tests because the - # pauli conversions are 2^12th large and will OOM error if not sparse. - if isinstance(mat, scisparse.spmatrix): - value = mat.dot(wavefn).dot(np.conj(wavefn)) - else: - value = StateFn(operator, is_measurement=True).eval(wavefn) - value = value if np.abs(value) > threshold else 0.0 - # The value get's wrapped into a tuple: (mean, standard deviation). - # Since this is an exact computation, the standard deviation is known to be zero. - values[key] = (value, 0.0) - return values - - def compute_eigenvalues( - self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None - ) -> EigensolverResult: - super().compute_eigenvalues(operator, aux_operators) - - if operator is None: - raise AlgorithmError("Operator was never provided") - - self._check_set_k(operator) - zero_op = I.tensorpower(operator.num_qubits) * 0.0 - if isinstance(aux_operators, list) and len(aux_operators) > 0: - # For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. - aux_operators = [zero_op if op == 0 else op for op in aux_operators] - elif isinstance(aux_operators, dict) and len(aux_operators) > 0: - aux_operators = { - key: zero_op if op == 0 else op # Convert zero values to zero operators - for key, op in aux_operators.items() - if op is not None # Discard None values - } - else: - aux_operators = None - - k_orig = self._k - if self._filter_criterion: - # need to consider all elements if a filter is set - self._k = 2**operator.num_qubits - - self._ret = EigensolverResult() - self._solve(operator) - - # compute energies before filtering, as this also evaluates the aux operators - self._get_energies(operator, aux_operators) - - # if a filter is set, loop over the given values and only keep - if self._filter_criterion: - - eigvecs = [] - eigvals = [] - aux_ops = [] - cnt = 0 - for i in range(len(self._ret.eigenvalues)): - eigvec = self._ret.eigenstates[i] - eigval = self._ret.eigenvalues[i] - if self._ret.aux_operator_eigenvalues is not None: - aux_op = self._ret.aux_operator_eigenvalues[i] - else: - aux_op = None - if self._filter_criterion(eigvec, eigval, aux_op): - cnt += 1 - eigvecs += [eigvec] - eigvals += [eigval] - if self._ret.aux_operator_eigenvalues is not None: - aux_ops += [aux_op] - if cnt == k_orig: - break - - self._ret.eigenstates = np.array(eigvecs) - self._ret.eigenvalues = np.array(eigvals) - # conversion to np.array breaks in case of aux_ops - self._ret.aux_operator_eigenvalues = aux_ops - - self._k = k_orig - - # evaluate ground state after filtering (in case a filter is set) - self._get_ground_state_energy(operator) - if self._ret.eigenstates is not None: - self._ret.eigenstates = ListOp([StateFn(vec) for vec in self._ret.eigenstates]) - - logger.debug("EigensolverResult:\n%s", self._ret) - return self._ret diff --git a/qiskit/algorithms/eigen_solvers/vqd.py b/qiskit/algorithms/eigen_solvers/vqd.py deleted file mode 100644 index 068cdd08cd14..000000000000 --- a/qiskit/algorithms/eigen_solvers/vqd.py +++ /dev/null @@ -1,807 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Variational Quantum Deflation Algorithm for computing higher energy states. - -See https://arxiv.org/abs/1805.08138. -""" -from __future__ import annotations - -import logging -import warnings -from collections.abc import Callable -from time import time -import numpy as np - -from qiskit.circuit import QuantumCircuit, Parameter -from qiskit.circuit.library import RealAmplitudes -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.providers import Backend -from qiskit.opflow import ( - OperatorBase, - ExpectationBase, - ExpectationFactory, - StateFn, - CircuitStateFn, - ListOp, - CircuitSampler, - PauliSumOp, -) -from qiskit.opflow.gradients import GradientBase -from qiskit.utils.validation import validate_min -from qiskit.utils.backend_utils import is_aer_provider -from qiskit.utils.deprecation import deprecate_func -from qiskit.utils import QuantumInstance -from ..list_or_dict import ListOrDict -from ..optimizers import Optimizer, SLSQP, Minimizer -from ..variational_algorithm import VariationalAlgorithm, VariationalResult -from .eigen_solver import Eigensolver, EigensolverResult -from ..minimum_eigen_solvers.vqe import _validate_bounds, _validate_initial_point -from ..exceptions import AlgorithmError -from ..aux_ops_evaluator import eval_observables - -logger = logging.getLogger(__name__) - - -class VQD(VariationalAlgorithm, Eigensolver): - r"""Deprecated: Variational Quantum Deflation algorithm. - - The VQD class has been superseded by the - :class:`qiskit.algorithms.eigensolvers.VQD` class. - This class will be deprecated in a future release and subsequently - removed after that. - - `VQD `__ is a quantum algorithm that uses a - variational technique to find - the k eigenvalues of the Hamiltonian :math:`H` of a given system. - - The algorithm computes excited state energies of generalised hamiltonians - by optimising over a modified cost function where each succesive eigen value - is calculated iteratively by introducing an overlap term with all - the previously computed eigenstaes that must be minimised, thus ensuring - higher energy eigen states are found. - - An instance of VQD requires defining three algorithmic sub-components: - an integer k denoting the number of eigenstates to calculate, a trial - state (a.k.a. ansatz)which is a :class:`QuantumCircuit`, - and one of the classical :mod:`~qiskit.algorithms.optimizers`. - The ansatz is varied, via its set of parameters, by the optimizer, - such that it works towards a state, as determined by the parameters - applied to the ansatz, that will result in the minimum expectation values - being measured of the input operator (Hamiltonian). The algorithm does - this by iteratively refining each excited state to be orthogonal to all - the previous excited states. - - An optional array of parameter values, via the *initial_point*, may be provided as the - starting point for the search of the minimum eigenvalue. This feature is particularly useful - such as when there are reasons to believe that the solution point is close to a particular - point. - - The length of the *initial_point* list value must match the number of the parameters - expected by the ansatz being used. If the *initial_point* is left at the default - of ``None``, then VQD will look to the ansatz for a preferred value, based on its - given initial state. If the ansatz returns ``None``, - then a random point will be generated within the parameter bounds set, as per above. - If the ansatz provides ``None`` as the lower bound, then VQD - will default it to :math:`-2\pi`; similarly, if the ansatz returns ``None`` - as the upper bound, the default value will be :math:`2\pi`. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.eigensolvers.VQD``." - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - ansatz: QuantumCircuit | None = None, - k: int = 2, - betas: list[float] | None = None, - optimizer: Optimizer | Minimizer | None = None, - initial_point: np.ndarray | None = None, - gradient: GradientBase | Callable | None = None, - expectation: ExpectationBase | None = None, - include_custom: bool = False, - max_evals_grouped: int = 1, - callback: Callable[[int, np.ndarray, float, float, int], None] | None = None, - quantum_instance: QuantumInstance | Backend | None = None, - ) -> None: - """ - - Args: - ansatz: A parameterized circuit used as ansatz for the wave function. - k: the number of eigenvalues to return. Returns the lowest k eigenvalues. - betas: beta parameters in the VQD paper. - Should have length k - 1, with k the number of excited states. - These hyperparameters balance the contribution of each overlap term to the cost - function and have a default value computed as the mean square sum of the - coefficients of the observable. - optimizer: A classical optimizer. Can either be a Qiskit optimizer or a callable - that takes an array as input and returns a Qiskit or SciPy optimization result. - initial_point: An optional initial point (i.e. initial parameter values) - for the optimizer. If ``None`` then VQD will look to the ansatz for a preferred - point and if not will simply compute a random one. - gradient: An optional gradient function or operator for optimizer. - Only used to compute the ground state at the moment. - expectation: The Expectation converter for taking the average value of the - Observable over the ansatz state function. When ``None`` (the default) an - :class:`~qiskit.opflow.expectations.ExpectationFactory` is used to select - an appropriate expectation based on the operator and backend. When using Aer - qasm_simulator backend, with paulis, it is however much faster to leverage custom - Aer function for the computation but, although VQD performs much faster - with it, the outcome is ideal, with no shot noise, like using a state vector - simulator. If you are just looking for the quickest performance when choosing Aer - qasm_simulator and the lack of shot noise is not an issue then set `include_custom` - parameter here to ``True`` (defaults to ``False``). - include_custom: When `expectation` parameter here is None setting this to ``True`` will - allow the factory to include the custom Aer pauli expectation. - max_evals_grouped: Max number of evaluations performed simultaneously. Signals the - given optimizer that more than one set of parameters can be supplied so that - multiple points to compute the gradient can be passed and if computed in parallel - potentially the expectation values can be computed in parallel. Typically this is - possible when a finite difference gradient is used by the optimizer such that - improve overall execution time. Deprecated if a gradient operator or function is - given. - callback: a callback that can access the intermediate data during the optimization. - Four parameter values are passed to the callback as follows during each evaluation - by the optimizer for its current set of parameters as it works towards the minimum. - These are: the evaluation count, the optimizer parameters for the ansatz, the - evaluated mean, the evaluated standard deviation, and the current step. - quantum_instance: Quantum Instance or Backend - - """ - validate_min("max_evals_grouped", max_evals_grouped, 1) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__init__() - - self._max_evals_grouped = max_evals_grouped - self._circuit_sampler: CircuitSampler | None = None - self._expectation = None - self.expectation = expectation - self._include_custom = include_custom - - # set ansatz -- still supporting pre 0.18.0 sorting - - self._ansatz: QuantumCircuit | None = None - self.ansatz = ansatz - - self.k = k - self.betas = betas - - self._optimizer: Optimizer | None = None - self.optimizer = optimizer - - self._initial_point: np.ndarray | None = None - self.initial_point = initial_point - self._gradient: GradientBase | Callable | None = None - self.gradient = gradient - self._quantum_instance: QuantumInstance | None = None - - if quantum_instance is not None: - self.quantum_instance = quantum_instance - - self._eval_time = None - self._eval_count = 0 - self._callback: Callable[[int, np.ndarray, float, float, int], None] | None = None - self.callback = callback - - logger.info(self.print_settings()) - - @property - def ansatz(self) -> QuantumCircuit: - """Returns the ansatz.""" - return self._ansatz - - @ansatz.setter - def ansatz(self, ansatz: QuantumCircuit | None): - """Sets the ansatz. - - Args: - ansatz: The parameterized circuit used as an ansatz. - If None is passed, RealAmplitudes is used by default. - - """ - if ansatz is None: - ansatz = RealAmplitudes() - - self._ansatz = ansatz - - @property - def gradient(self) -> GradientBase | Callable | None: - """Returns the gradient.""" - return self._gradient - - @gradient.setter - def gradient(self, gradient: GradientBase | Callable | None): - """Sets the gradient.""" - self._gradient = gradient - - @property - def quantum_instance(self) -> QuantumInstance | None: - """Returns quantum instance.""" - return self._quantum_instance - - @quantum_instance.setter - def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: - """Sets a quantum_instance.""" - if not isinstance(quantum_instance, QuantumInstance): - quantum_instance = QuantumInstance(quantum_instance) - - self._quantum_instance = quantum_instance - self._circuit_sampler = CircuitSampler( - quantum_instance, param_qobj=is_aer_provider(quantum_instance.backend) - ) - - @property - def initial_point(self) -> np.ndarray | None: - """Returns initial point.""" - return self._initial_point - - @initial_point.setter - def initial_point(self, initial_point: np.ndarray): - """Sets initial point""" - self._initial_point = initial_point - - @property - def max_evals_grouped(self) -> int: - """Returns max_evals_grouped""" - return self._max_evals_grouped - - @max_evals_grouped.setter - def max_evals_grouped(self, max_evals_grouped: int): - """Sets max_evals_grouped""" - self._max_evals_grouped = max_evals_grouped - self.optimizer.set_max_evals_grouped(max_evals_grouped) - - @property - def include_custom(self) -> bool: - """Returns include_custom""" - return self._include_custom - - @include_custom.setter - def include_custom(self, include_custom: bool): - """Sets include_custom. If set to another value than the one that was previsously set, - the expectation attribute is reset to None. - """ - if include_custom != self._include_custom: - self._include_custom = include_custom - self.expectation = None - - @property - def callback(self) -> Callable[[int, np.ndarray, float, float, int], None] | None: - """Returns callback""" - return self._callback - - @callback.setter - def callback(self, callback: Callable[[int, np.ndarray, float, float, int], None] | None): - """Sets callback""" - self._callback = callback - - @property - def expectation(self) -> ExpectationBase | None: - """The expectation value algorithm used to construct the expectation measurement from - the observable.""" - return self._expectation - - @expectation.setter - def expectation(self, exp: ExpectationBase | None) -> None: - self._expectation = exp - - def _check_operator_ansatz(self, operator: OperatorBase): - """Check that the number of qubits of operator and ansatz match.""" - if operator is not None and self.ansatz is not None: - if operator.num_qubits != self.ansatz.num_qubits: - # try to set the number of qubits on the ansatz, if possible - try: - self.ansatz.num_qubits = operator.num_qubits - except AttributeError as ex: - raise AlgorithmError( - "The number of qubits of the ansatz does not match the " - "operator, and the ansatz does not allow setting the " - "number of qubits using `num_qubits`." - ) from ex - - @property - def optimizer(self) -> Optimizer: - """Returns optimizer""" - return self._optimizer - - @optimizer.setter - def optimizer(self, optimizer: Optimizer | None): - """Sets the optimizer attribute. - - Args: - optimizer: The optimizer to be used. If None is passed, SLSQP is used by default. - - """ - if optimizer is None: - optimizer = SLSQP() - - if isinstance(optimizer, Optimizer): - optimizer.set_max_evals_grouped(self.max_evals_grouped) - - self._optimizer = optimizer - - @property - def setting(self): - """Prepare the setting of VQD as a string.""" - ret = f"Algorithm: {self.__class__.__name__}\n" - params = "" - for key, value in self.__dict__.items(): - if key[0] == "_": - if "initial_point" in key and value is None: - params += "-- {}: {}\n".format(key[1:], "Random seed") - else: - params += f"-- {key[1:]}: {value}\n" - ret += f"{params}" - return ret - - def print_settings(self): - """Preparing the setting of VQD into a string. - - Returns: - str: the formatted setting of VQD. - """ - ret = "\n" - ret += "==================== Setting of {} ============================\n".format( - self.__class__.__name__ - ) - ret += f"{self.setting}" - ret += "===============================================================\n" - if self.ansatz is not None: - ret += "{}".format(self.ansatz.draw(output="text")) - else: - ret += "ansatz has not been set" - ret += "===============================================================\n" - ret += f"{self._optimizer.setting}" - ret += "===============================================================\n" - return ret - - def construct_expectation( - self, - parameter: list[float] | list[Parameter] | np.ndarray, - operator: OperatorBase, - return_expectation: bool = False, - ) -> OperatorBase | tuple[OperatorBase, ExpectationBase]: - r""" - Generate the ansatz circuit and expectation value measurement, and return their - runnable composition. - - Args: - parameter: Parameters for the ansatz circuit. - operator: Qubit operator of the Observable - return_expectation: If True, return the ``ExpectationBase`` expectation converter used - in the construction of the expectation value. Useful e.g. to compute the standard - deviation of the expectation value. - - Returns: - The Operator equalling the measurement of the ansatz :class:`StateFn` by the - Observable's expectation :class:`StateFn`, and, optionally, the expectation converter. - - Raises: - AlgorithmError: If no operator has been provided. - AlgorithmError: If no expectation is passed and None could be inferred via the - ExpectationFactory. - """ - if operator is None: - raise AlgorithmError("The operator was never provided.") - - self._check_operator_ansatz(operator) - - # if expectation was never created, try to create one - if self.expectation is None: - expectation = ExpectationFactory.build( - operator=operator, - backend=self.quantum_instance, - include_custom=self._include_custom, - ) - else: - expectation = self.expectation - - wave_function = self.ansatz.assign_parameters(parameter) - - observable_meas = expectation.convert(StateFn(operator, is_measurement=True)) - ansatz_circuit_op = CircuitStateFn(wave_function) - expect_op = observable_meas.compose(ansatz_circuit_op).reduce() - - if return_expectation: - return expect_op, expectation - - return expect_op - - def construct_circuit( - self, - parameter: list[float] | list[Parameter] | np.ndarray, - operator: OperatorBase, - ) -> list[QuantumCircuit]: - """Return the circuits used to compute the expectation value. - - Args: - parameter: Parameters for the ansatz circuit. - operator: Qubit operator of the Observable - - Returns: - A list of the circuits used to compute the expectation value. - """ - expect_op = self.construct_expectation(parameter, operator).to_circuit_op() - - circuits = [] - - # recursively extract circuits - def extract_circuits(op): - if isinstance(op, CircuitStateFn): - circuits.append(op.primitive) - elif isinstance(op, ListOp): - for op_i in op.oplist: - extract_circuits(op_i) - - extract_circuits(expect_op) - - return circuits - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - def _eval_aux_ops( - self, - parameters: np.ndarray, - aux_operators: ListOrDict[OperatorBase], - expectation: ExpectationBase, - threshold: float = 1e-12, - ) -> ListOrDict[tuple[complex, complex]]: - # Create new CircuitSampler to avoid breaking existing one's caches. - sampler = CircuitSampler(self.quantum_instance) - - if isinstance(aux_operators, dict): - list_op = ListOp(list(aux_operators.values())) - else: - list_op = ListOp(aux_operators) - - aux_op_meas = expectation.convert(StateFn(list_op, is_measurement=True)) - aux_op_expect = aux_op_meas.compose( - CircuitStateFn(self.ansatz.assign_parameters(parameters)) - ) - aux_op_expect_sampled = sampler.convert(aux_op_expect) - - # compute means - values = np.real(aux_op_expect_sampled.eval()) - - # compute standard deviations - variances = np.real(expectation.compute_variance(aux_op_expect_sampled)) - if not isinstance(variances, np.ndarray) and variances == 0.0: - # when `variances` is a single value equal to 0., our expectation value is exact and we - # manually ensure the variances to be a list of the correct length - variances = np.zeros(len(aux_operators), dtype=float) - std_devs = np.sqrt(variances / self.quantum_instance.run_config.shots) - - # Discard values below threshold - aux_op_means = values * (np.abs(values) > threshold) - # zip means and standard deviations into tuples - aux_op_results = zip(aux_op_means, std_devs) - - # Return None eigenvalues for None operators if aux_operators is a list. - # None operators are already dropped in compute_minimum_eigenvalue if aux_operators is a - # dict. - if isinstance(aux_operators, list): - aux_operator_eigenvalues: ListOrDict[tuple[complex, complex]] = [None] * len( - aux_operators - ) - key_value_iterator = enumerate(aux_op_results) - else: - aux_operator_eigenvalues = {} - key_value_iterator = zip(aux_operators.keys(), aux_op_results) - - for key, value in key_value_iterator: - if aux_operators[key] is not None: - aux_operator_eigenvalues[key] = value - - return aux_operator_eigenvalues - - def compute_eigenvalues( - self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None - ) -> EigensolverResult: - super().compute_eigenvalues(operator, aux_operators) - - if self.quantum_instance is None: - raise AlgorithmError( - "A QuantumInstance or Backend must be supplied to run the quantum algorithm." - ) - self.quantum_instance.circuit_summary = True - - # this sets the size of the ansatz, so it must be called before the initial point - # validation - self._check_operator_ansatz(operator) - - # set an expectation for this algorithm run (will be reset to None at the end) - initial_point = _validate_initial_point(self.initial_point, self.ansatz) - - bounds = _validate_bounds(self.ansatz) - # We need to handle the array entries being zero or Optional i.e. having value None - if aux_operators: - zero_op = PauliSumOp.from_list([("I" * self.ansatz.num_qubits, 0)]) - - # Convert the None and zero values when aux_operators is a list. - # Drop None and convert zero values when aux_operators is a dict. - if isinstance(aux_operators, list): - key_op_iterator = enumerate(aux_operators) - converted: ListOrDict[OperatorBase] = [zero_op] * len(aux_operators) - else: - key_op_iterator = aux_operators.items() - converted = {} - for key, op in key_op_iterator: - if op is not None: - converted[key] = zero_op if op == 0 else op - - aux_operators = converted - - else: - aux_operators = None - - if self.betas is None: - upper_bound = ( - abs(operator.coeff) - if isinstance(operator, PauliOp) - else abs(operator.coeff) * sum(abs(operation.coeff) for operation in operator) - ) - self.betas = [upper_bound * 10] * (self.k) - logger.info("beta autoevaluated to %s", self.betas[0]) - - result = VQDResult() - result.optimal_point = [] - result.optimal_parameters = [] - result.optimal_value = [] - result.cost_function_evals = [] - result.optimizer_time = [] - result.eigenvalues = [] - result.eigenstates = [] - - if aux_operators is not None: - aux_values = [] - - for step in range(1, self.k + 1): - - self._eval_count = 0 - energy_evaluation, expectation = self.get_energy_evaluation( - step, operator, return_expectation=True, prev_states=result.optimal_parameters - ) - - # Convert the gradient operator into a callable function that is compatible with the - # optimization routine. Only used for the ground state currently as Gradient() doesnt - # support SumOps yet - if isinstance(self._gradient, GradientBase): - gradient = self._gradient.gradient_wrapper( - StateFn(operator, is_measurement=True) @ StateFn(self.ansatz), - bind_params=list(self.ansatz.parameters), - backend=self._quantum_instance, - ) - else: - gradient = self._gradient - - start_time = time() - - if callable(self.optimizer): - opt_result = self.optimizer( # pylint: disable=not-callable - fun=energy_evaluation, x0=initial_point, jac=gradient, bounds=bounds - ) - else: - opt_result = self.optimizer.minimize( - fun=energy_evaluation, x0=initial_point, jac=gradient, bounds=bounds - ) - - eval_time = time() - start_time - - result.optimal_point.append(opt_result.x) - result.optimal_parameters.append(dict(zip(self.ansatz.parameters, opt_result.x))) - result.optimal_value.append(opt_result.fun) - result.cost_function_evals.append(opt_result.nfev) - result.optimizer_time.append(eval_time) - - eigenvalue = ( - StateFn(operator, is_measurement=True) - .compose( - CircuitStateFn(self.ansatz.assign_parameters(result.optimal_parameters[-1])) - ) - .reduce() - .eval() - ) - - result.eigenvalues.append(eigenvalue) - result.eigenstates.append(self._get_eigenstate(result.optimal_parameters[-1])) - - if aux_operators is not None: - bound_ansatz = self.ansatz.assign_parameters(result.optimal_point[-1]) - aux_value = eval_observables( - self.quantum_instance, bound_ansatz, aux_operators, expectation=expectation - ) - aux_values.append(aux_value) - - if step == 1: - - logger.info( - "Ground state optimization complete in %s seconds.\n" - "Found opt_params %s in %s evals", - eval_time, - result.optimal_point, - self._eval_count, - ) - else: - logger.info( - ( - "%s excited state optimization complete in %s s.\n" - "Found opt_params %s in %s evals" - ), - str(step - 1), - eval_time, - result.optimal_point, - self._eval_count, - ) - - # To match the signature of NumpyEigenSolver Result - result.eigenstates = ListOp([StateFn(vec) for vec in result.eigenstates]) - result.eigenvalues = np.array(result.eigenvalues) - result.optimal_point = np.array(result.optimal_point) - result.optimal_value = np.array(result.optimal_value) - result.cost_function_evals = np.array(result.cost_function_evals) - result.optimizer_time = np.array(result.optimizer_time) - - if aux_operators is not None: - result.aux_operator_eigenvalues = aux_values - - return result - - def get_energy_evaluation( - self, - step: int, - operator: OperatorBase, - return_expectation: bool = False, - prev_states: list[np.ndarray] | None = None, - ) -> Callable[[np.ndarray], float | list[float]] | tuple[ - Callable[[np.ndarray], float | list[float]], ExpectationBase - ]: - """Returns a function handle to evaluates the energy at given parameters for the ansatz. - - This return value is the objective function to be passed to the optimizer for evaluation. - - Args: - step: level of energy being calculated. 0 for ground, 1 for first excited state... - operator: The operator whose energy to evaluate. - return_expectation: If True, return the ``ExpectationBase`` expectation converter used - in the construction of the expectation value. Useful e.g. to evaluate other - operators with the same expectation value converter. - prev_states: List of parameters from previous rounds of optimization. - - - Returns: - A callable that computes and returns the energy of the hamiltonian - of each parameter, and, optionally, the expectation - - Raises: - RuntimeError: If the circuit is not parameterized (i.e. has 0 free parameters). - AlgorithmError: If operator was not provided. - - """ - - num_parameters = self.ansatz.num_parameters - if num_parameters == 0: - raise RuntimeError("The ansatz must be parameterized, but has 0 free parameters.") - - if operator is None: - raise AlgorithmError("The operator was never provided.") - - if step > 1 and (len(prev_states) + 1) != step: - raise RuntimeError( - f"Passed previous states of the wrong size." - f"Passed array has length {str(len(prev_states))}" - ) - - self._check_operator_ansatz(operator) - overlap_op = [] - - ansatz_params = self.ansatz.parameters - expect_op, expectation = self.construct_expectation( - ansatz_params, operator, return_expectation=True - ) - - for state in range(step - 1): - - prev_circ = self.ansatz.assign_parameters(prev_states[state]) - overlap_op.append(~CircuitStateFn(prev_circ) @ CircuitStateFn(self.ansatz)) - - def energy_evaluation(parameters): - parameter_sets = np.reshape(parameters, (-1, num_parameters)) - # Dict associating each parameter with the lists of parameterization values for it - param_bindings = dict(zip(ansatz_params, parameter_sets.transpose().tolist())) - - sampled_expect_op = self._circuit_sampler.convert(expect_op, params=param_bindings) - means = np.real(sampled_expect_op.eval()) - - for state in range(step - 1): - sampled_final_op = self._circuit_sampler.convert( - overlap_op[state], params=param_bindings - ) - cost = sampled_final_op.eval() - means += np.real(self.betas[state] * np.conj(cost) * cost) - - if self._callback is not None: - variance = np.real(expectation.compute_variance(sampled_expect_op)) - estimator_error = np.sqrt(variance / self.quantum_instance.run_config.shots) - for i, param_set in enumerate(parameter_sets): - self._eval_count += 1 - self._callback(self._eval_count, param_set, means[i], estimator_error[i], step) - else: - self._eval_count += len(means) - - return means if len(means) > 1 else means[0] - - if return_expectation: - return energy_evaluation, expectation - - return energy_evaluation - - def _get_eigenstate(self, optimal_parameters) -> list[float] | dict[str, int]: - """Get the simulation outcome of the ansatz, provided with parameters.""" - optimal_circuit = self.ansatz.assign_parameters(optimal_parameters) - state_fn = self._circuit_sampler.convert(StateFn(optimal_circuit)).eval() - if self.quantum_instance.is_statevector: - state = state_fn.primitive.data # VectorStateFn -> Statevector -> np.array - else: - state = state_fn.to_dict_fn().primitive # SparseVectorStateFn -> DictStateFn -> dict - - return state - - -class VQDResult(VariationalResult, EigensolverResult): - """Deprecated: VQD Result. - - The VQDResult class has been superseded by the - :class:`qiskit.algorithms.eigensolvers.VQDResult` class. - This class will be deprecated in a future release and subsequently - removed after that. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.eigensolvers.VQDResult``." - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__(self) -> None: - super().__init__() - self._cost_function_evals: int | None = None - - @property - def cost_function_evals(self) -> int | None: - """Returns number of cost optimizer evaluations""" - return self._cost_function_evals - - @cost_function_evals.setter - def cost_function_evals(self, value: int) -> None: - """Sets number of cost function evaluations""" - self._cost_function_evals = value - - @property - def eigenstates(self) -> np.ndarray | None: - """return eigen state""" - return self._eigenstates - - @eigenstates.setter - def eigenstates(self, value: np.ndarray) -> None: - """set eigen state""" - self._eigenstates = value diff --git a/qiskit/algorithms/eigensolvers/__init__.py b/qiskit/algorithms/eigensolvers/__init__.py deleted file mode 100644 index 2934d6c2a340..000000000000 --- a/qiskit/algorithms/eigensolvers/__init__.py +++ /dev/null @@ -1,53 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -===================================================================== -Eigensolvers Package (:mod:`qiskit.algorithms.eigensolvers`) -===================================================================== - -.. currentmodule:: qiskit.algorithms.eigensolvers - -Eigensolvers -================ - -.. autosummary:: - :toctree: ../stubs/ - - Eigensolver - NumPyEigensolver - VQD - -Results -======= - - .. autosummary:: - :toctree: ../stubs/ - - EigensolverResult - NumPyEigensolverResult - VQDResult - -""" - -from .numpy_eigensolver import NumPyEigensolver, NumPyEigensolverResult -from .eigensolver import Eigensolver, EigensolverResult -from .vqd import VQD, VQDResult - -__all__ = [ - "NumPyEigensolver", - "NumPyEigensolverResult", - "Eigensolver", - "EigensolverResult", - "VQD", - "VQDResult", -] diff --git a/qiskit/algorithms/eigensolvers/eigensolver.py b/qiskit/algorithms/eigensolvers/eigensolver.py deleted file mode 100644 index 30fb7c488844..000000000000 --- a/qiskit/algorithms/eigensolvers/eigensolver.py +++ /dev/null @@ -1,106 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The eigensolver interface and result.""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Any -import numpy as np - -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..algorithm_result import AlgorithmResult -from ..list_or_dict import ListOrDict - - -class Eigensolver(ABC): - """The eigensolver interface. - - Algorithms that can compute eigenvalues for an operator - may implement this interface to allow different algorithms to be - used interchangeably. - """ - - @abstractmethod - def compute_eigenvalues( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> "EigensolverResult": - """ - Computes the minimum eigenvalue. The ``operator`` and ``aux_operators`` are supplied here. - While an ``operator`` is required by algorithms, ``aux_operators`` are optional. - - Args: - operator: Qubit operator of the observable. - aux_operators: Optional list of auxiliary operators to be evaluated with the - eigenstate of the minimum eigenvalue main result and their expectation values - returned. For instance, in chemistry, these can be dipole operators and total particle - count operators, so we can get values for these at the ground state. - - Returns: - An eigensolver result. - """ - return EigensolverResult() - - @classmethod - def supports_aux_operators(cls) -> bool: - """Whether computing the expectation value of auxiliary operators is supported. - - If the eigensolver computes the eigenvalues of the main operator, then it can compute - the expectation value of the ``aux_operators`` for that state. Otherwise they will be ignored. - - Returns: - ``True`` if ``aux_operator`` expectations can be evaluated, ``False`` otherwise. - """ - return False - - -class EigensolverResult(AlgorithmResult): - """Eigensolver result.""" - - def __init__(self) -> None: - super().__init__() - self._eigenvalues: np.ndarray | None = None - self._aux_operators_evaluated: list[ - ListOrDict[tuple[complex, dict[str, Any]]] - ] | None = None - - @property - def eigenvalues(self) -> np.ndarray | None: - """Return the eigenvalues.""" - return self._eigenvalues - - @eigenvalues.setter - def eigenvalues(self, value: np.ndarray) -> None: - """Set the eigenvalues.""" - self._eigenvalues = value - - @property - def aux_operators_evaluated( - self, - ) -> list[ListOrDict[tuple[complex, dict[str, Any]]]] | None: - """Return the aux operator expectation values. - - These values are in fact tuples formatted as (mean, metadata). - """ - return self._aux_operators_evaluated - - @aux_operators_evaluated.setter - def aux_operators_evaluated( - self, value: list[ListOrDict[tuple[complex, dict[str, Any]]]] - ) -> None: - """Set the aux operator eigenvalues.""" - self._aux_operators_evaluated = value diff --git a/qiskit/algorithms/eigensolvers/numpy_eigensolver.py b/qiskit/algorithms/eigensolvers/numpy_eigensolver.py deleted file mode 100644 index c0af8382ccd4..000000000000 --- a/qiskit/algorithms/eigensolvers/numpy_eigensolver.py +++ /dev/null @@ -1,327 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The NumPy eigensolver algorithm.""" - -from __future__ import annotations - -import warnings -from typing import Callable, Union, List, Optional -import logging -import numpy as np -from scipy import sparse as scisparse - -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info import SparsePauliOp, Statevector -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.utils.validation import validate_min - -from .eigensolver import Eigensolver, EigensolverResult -from ..exceptions import AlgorithmError -from ..list_or_dict import ListOrDict - -logger = logging.getLogger(__name__) - -FilterType = Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool] - - -class NumPyEigensolver(Eigensolver): - r""" - The NumPy eigensolver algorithm. - - The NumPy Eigensolver computes up to the first :math:`k` eigenvalues of a complex-valued square - matrix of dimension :math:`n \times n`, with :math:`k \leq n`. - - Note: - Operators are automatically converted to SciPy's ``spmatrix`` - as needed and this conversion can be costly in terms of memory and performance as the - operator size, mostly in terms of number of qubits it represents, gets larger. - """ - - def __init__( - self, - k: int = 1, - filter_criterion: FilterType | None = None, - ) -> None: - """ - Args: - k: Number of eigenvalues are to be computed, with a minimum value of 1. - filter_criterion: Callable that allows to filter eigenvalues/eigenstates. Only feasible - eigenstates are returned in the results. The callable has the signature - ``filter(eigenstate, eigenvalue, aux_values)`` and must return a boolean to indicate - whether to keep this value in the final returned result or not. If the number of - elements that satisfies the criterion is smaller than ``k``, then the returned list will - have fewer elements and can even be empty. - """ - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - validate_min("k", k, 1) - - super().__init__() - - self._in_k = k - self._k = k - - self._filter_criterion = filter_criterion - - @property - def k(self) -> int: - """Return k (number of eigenvalues requested).""" - return self._in_k - - @k.setter - def k(self, k: int) -> None: - """Set k (number of eigenvalues requested).""" - validate_min("k", k, 1) - self._in_k = k - self._k = k - - @property - def filter_criterion( - self, - ) -> FilterType | None: - """Return the filter criterion if set.""" - return self._filter_criterion - - @filter_criterion.setter - def filter_criterion(self, filter_criterion: FilterType | None) -> None: - """Set the filter criterion.""" - self._filter_criterion = filter_criterion - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - def _check_set_k(self, operator: BaseOperator | PauliSumOp) -> None: - if operator is not None: - if self._in_k > 2**operator.num_qubits: - self._k = 2**operator.num_qubits - logger.debug( - "WARNING: Asked for %s eigenvalues but max possible is %s.", self._in_k, self._k - ) - else: - self._k = self._in_k - - def _solve(self, operator: BaseOperator | PauliSumOp) -> tuple[np.ndarray, np.ndarray]: - if isinstance(operator, PauliSumOp): - op_matrix = operator.to_spmatrix() - else: - try: - op_matrix = operator.to_matrix(sparse=True) - except TypeError: - logger.debug( - "WARNING: operator of type `%s` does not support sparse matrices. " - "Trying dense computation", - type(operator), - ) - try: - op_matrix = operator.to_matrix() - except AttributeError as ex: - raise AlgorithmError(f"Unsupported operator type `{type(operator)}`.") from ex - - if isinstance(op_matrix, scisparse.csr_matrix): - # If matrix is diagonal, the elements on the diagonal are the eigenvalues. Solve by sorting. - if scisparse.csr_matrix(op_matrix.diagonal()).nnz == op_matrix.nnz: - diag = op_matrix.diagonal() - indices = np.argsort(diag)[: self._k] - eigval = diag[indices] - eigvec = np.zeros((op_matrix.shape[0], self._k)) - for i, idx in enumerate(indices): - eigvec[idx, i] = 1.0 - else: - if self._k >= 2**operator.num_qubits - 1: - logger.debug( - "SciPy doesn't support to get all eigenvalues, using NumPy instead." - ) - eigval, eigvec = self._solve_dense(operator.to_matrix()) - else: - eigval, eigvec = self._solve_sparse(op_matrix, self._k) - else: - # Sparse SciPy matrix not supported, use dense NumPy computation. - eigval, eigvec = self._solve_dense(operator.to_matrix()) - - indices = np.argsort(eigval)[: self._k] - eigval = eigval[indices] - eigvec = eigvec[:, indices] - return eigval, eigvec.T - - @staticmethod - def _solve_sparse(op_matrix: scisparse.csr_matrix, k: int) -> tuple[np.ndarray, np.ndarray]: - if (op_matrix != op_matrix.H).nnz == 0: - # Operator is Hermitian - return scisparse.linalg.eigsh(op_matrix, k=k, which="SA") - else: - return scisparse.linalg.eigs(op_matrix, k=k, which="SR") - - @staticmethod - def _solve_dense(op_matrix: np.ndarray) -> tuple[np.ndarray, np.ndarray]: - if op_matrix.all() == op_matrix.conj().T.all(): - # Operator is Hermitian - return np.linalg.eigh(op_matrix) - else: - return np.linalg.eig(op_matrix) - - @staticmethod - def _eval_aux_operators( - aux_operators: ListOrDict[BaseOperator | PauliSumOp], - wavefn: np.ndarray, - threshold: float = 1e-12, - ) -> ListOrDict[tuple[complex, complex]]: - - values: ListOrDict[tuple[complex, complex]] - - # As a list, aux_operators can contain None operators for which None values are returned. - # As a dict, the None operators in aux_operators have been dropped in compute_eigenvalues. - if isinstance(aux_operators, list): - values = [None] * len(aux_operators) - key_op_iterator = enumerate(aux_operators) - else: - values = {} - key_op_iterator = aux_operators.items() - - for key, operator in key_op_iterator: - if operator is None: - continue - - if operator.num_qubits is None or operator.num_qubits < 1: - logger.info( - "The number of qubits of the %s operator must be greater than zero.", key - ) - continue - - op_matrix = None - if isinstance(operator, PauliSumOp): - if operator.coeff != 0: - op_matrix = operator.to_spmatrix() - else: - try: - op_matrix = operator.to_matrix(sparse=True) - except TypeError: - logger.debug( - "WARNING: operator of type `%s` does not support sparse matrices. " - "Trying dense computation", - type(operator), - ) - try: - op_matrix = operator.to_matrix() - except AttributeError as ex: - raise AlgorithmError(f"Unsupported operator type {type(operator)}.") from ex - - if isinstance(op_matrix, scisparse.csr_matrix): - value = op_matrix.dot(wavefn).dot(np.conj(wavefn)) - elif isinstance(op_matrix, np.ndarray): - value = Statevector(wavefn).expectation_value(operator) - else: - value = 0.0 - - value = value if np.abs(value) > threshold else 0.0 - # The value gets wrapped into a tuple: (mean, metadata). - # The metadata includes variance (and, for other eigensolvers, shots). - # Since this is an exact computation, there are no shots - # and the variance is known to be zero. - values[key] = (value, {"variance": 0.0}) - return values - - def compute_eigenvalues( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> NumPyEigensolverResult: - - super().compute_eigenvalues(operator, aux_operators) - - if operator.num_qubits is None or operator.num_qubits < 1: - raise AlgorithmError("The number of qubits of the operator must be greater than zero.") - - self._check_set_k(operator) - - zero_op = SparsePauliOp(["I" * operator.num_qubits], coeffs=[0.0]) - if isinstance(aux_operators, list) and len(aux_operators) > 0: - # For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. - aux_operators = [zero_op if op == 0 else op for op in aux_operators] - elif isinstance(aux_operators, dict) and len(aux_operators) > 0: - aux_operators = { - key: zero_op if op == 0 else op # Convert zero values to zero operators - for key, op in aux_operators.items() - if op is not None # Discard None values - } - else: - aux_operators = None - - k_orig = self._k - if self._filter_criterion: - # need to consider all elements if a filter is set - self._k = 2**operator.num_qubits - - eigvals, eigvecs = self._solve(operator) - - # compute energies before filtering, as this also evaluates the aux operators - if aux_operators is not None: - aux_op_vals = [ - self._eval_aux_operators(aux_operators, eigvecs[i]) for i in range(self._k) - ] - else: - aux_op_vals = None - - # if a filter is set, loop over the given values and only keep - if self._filter_criterion: - filt_eigvals = [] - filt_eigvecs = [] - filt_aux_op_vals = [] - count = 0 - for i, (eigval, eigvec) in enumerate(zip(eigvals, eigvecs)): - if aux_op_vals is not None: - aux_op_val = aux_op_vals[i] - else: - aux_op_val = None - - if self._filter_criterion(eigvec, eigval, aux_op_val): - count += 1 - filt_eigvecs.append(eigvec) - filt_eigvals.append(eigval) - if aux_op_vals is not None: - filt_aux_op_vals.append(aux_op_val) - - if count == k_orig: - break - - eigvals = np.array(filt_eigvals) - eigvecs = np.array(filt_eigvecs) - aux_op_vals = filt_aux_op_vals - - self._k = k_orig - - result = NumPyEigensolverResult() - result.eigenvalues = eigvals - result.eigenstates = [Statevector(vec) for vec in eigvecs] - result.aux_operators_evaluated = aux_op_vals - - logger.debug("NumpyEigensolverResult:\n%s", result) - return result - - -class NumPyEigensolverResult(EigensolverResult): - """NumPy eigensolver result.""" - - def __init__(self) -> None: - super().__init__() - self._eigenstates: list[Statevector] | None = None - - @property - def eigenstates(self) -> list[Statevector] | None: - """Return eigenstates.""" - return self._eigenstates - - @eigenstates.setter - def eigenstates(self, value: list[Statevector]) -> None: - """Set eigenstates.""" - self._eigenstates = value diff --git a/qiskit/algorithms/eigensolvers/vqd.py b/qiskit/algorithms/eigensolvers/vqd.py deleted file mode 100644 index 59f07d8a918b..000000000000 --- a/qiskit/algorithms/eigensolvers/vqd.py +++ /dev/null @@ -1,542 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Variational Quantum Deflation Algorithm for computing higher energy states. - -See https://arxiv.org/abs/1805.08138. -""" - -from __future__ import annotations - -from collections.abc import Callable, Sequence -from typing import Any -import logging -from time import time - -import numpy as np - -from qiskit.algorithms.state_fidelities import BaseStateFidelity -from qiskit.circuit import QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.quantum_info import SparsePauliOp - -from ..list_or_dict import ListOrDict -from ..optimizers import Optimizer, Minimizer, OptimizerResult -from ..variational_algorithm import VariationalAlgorithm -from .eigensolver import Eigensolver, EigensolverResult -from ..utils import validate_bounds, validate_initial_point -from ..exceptions import AlgorithmError -from ..observables_evaluator import estimate_observables - -# private function as we expect this to be updated in the next release -from ..utils.set_batching import _set_default_batchsize - -logger = logging.getLogger(__name__) - - -class VQD(VariationalAlgorithm, Eigensolver): - r"""The Variational Quantum Deflation algorithm. Implementation using primitives. - - `VQD `__ is a quantum algorithm that uses a - variational technique to find - the k eigenvalues of the Hamiltonian :math:`H` of a given system. - - The algorithm computes excited state energies of generalised hamiltonians - by optimising over a modified cost function where each succesive eigenvalue - is calculated iteratively by introducing an overlap term with all - the previously computed eigenstates that must be minimised, thus ensuring - higher energy eigenstates are found. - - An instance of VQD requires defining three algorithmic sub-components: - an integer k denoting the number of eigenstates to calculate, a trial - state (a.k.a. ansatz) which is a :class:`QuantumCircuit`, - and one instance (or list of) classical :mod:`~qiskit.algorithms.optimizers`. - The optimizer varies the circuit parameters - The trial state :math:`|\psi(\vec\theta)\rangle` is varied by the optimizer, - which modifies the set of ansatz parameters :math:`\vec\theta` - such that the expectation value of the operator on the corresponding - state approaches a minimum. The algorithm does this by iteratively refining - each excited state to be orthogonal to all the previous excited states. - - An optional array of parameter values, via the *initial_point*, may be provided - as the starting point for the search of the minimum eigenvalue. This feature is - particularly useful when there are reasons to believe that the solution point - is close to a particular point. - - The length of the *initial_point* list value must match the number of the parameters - expected by the ansatz. If the *initial_point* is left at the default - of ``None``, then VQD will look to the ansatz for a preferred value, based on its - given initial state. If the ansatz returns ``None``, - then a random point will be generated within the parameter bounds set, as per above. - It is also possible to give a list of initial points, one for every kth eigenvalue. - If the ansatz provides ``None`` as the lower bound, then VQD - will default it to :math:`-2\pi`; similarly, if the ansatz returns ``None`` - as the upper bound, the default value will be :math:`2\pi`. - - The following attributes can be set via the initializer but can also be read and - updated once the VQD object has been constructed. - - Attributes: - estimator (BaseEstimator): The primitive instance used to perform the expectation - estimation as indicated in the VQD paper. - fidelity (BaseStateFidelity): The fidelity class instance used to compute the - overlap estimation as indicated in the VQD paper. - ansatz (QuantumCircuit): A parameterized circuit used as ansatz for the wave function. - optimizer(Optimizer | Sequence[Optimizer]): A classical optimizer or a list of optimizers, - one for every k-th eigenvalue. Can either be a Qiskit optimizer or a callable - that takes an array as input and returns a Qiskit or SciPy optimization result. - k (int): the number of eigenvalues to return. Returns the lowest k eigenvalues. - betas (list[float]): Beta parameters in the VQD paper. - Should have length k - 1, with k the number of excited states. - These hyper-parameters balance the contribution of each overlap term to the cost - function and have a default value computed as the mean square sum of the - coefficients of the observable. - initial point (Sequence[float] | Sequence[Sequence[float]] | None): An optional initial - point (i.e. initial parameter values) or a list of initial points - (one for every k-th eigenvalue) for the optimizer. - If ``None`` then VQD will look to the ansatz for a - preferred point and if not will simply compute a random one. - callback (Callable[[int, np.ndarray, float, dict[str, Any]], None] | None): - A callback that can access the intermediate data - during the optimization. Four parameter values are passed to the callback as - follows during each evaluation by the optimizer: the evaluation count, - the optimizer parameters for the ansatz, the estimated value, the estimation - metadata, and the current step. - """ - - def __init__( - self, - estimator: BaseEstimator, - fidelity: BaseStateFidelity, - ansatz: QuantumCircuit, - optimizer: Optimizer | Minimizer | Sequence[Optimizer | Minimizer], - *, - k: int = 2, - betas: Sequence[float] | None = None, - initial_point: Sequence[float] | Sequence[Sequence[float]] | None = None, - callback: Callable[[int, np.ndarray, float, dict[str, Any]], None] | None = None, - ) -> None: - """ - - Args: - estimator: The estimator primitive. - fidelity: The fidelity class using primitives. - ansatz: A parameterized circuit used as ansatz for the wave function. - optimizer: A classical optimizer or a list of optimizers, one for every k-th eigenvalue. - Can either be a Qiskit optimizer or a callable - that takes an array as input and returns a Qiskit or SciPy optimization result. - k: The number of eigenvalues to return. Returns the lowest k eigenvalues. - betas: Beta parameters in the VQD paper. - Should have length k - 1, with k the number of excited states. - These hyperparameters balance the contribution of each overlap term to the cost - function and have a default value computed as the mean square sum of the - coefficients of the observable. - initial_point: An optional initial point (i.e. initial parameter values) - or a list of initial points (one for every k-th eigenvalue) - for the optimizer. - If ``None`` then VQD will look to the ansatz for a preferred - point and if not will simply compute a random one. - callback: A callback that can access the intermediate data - during the optimization. Four parameter values are passed to the callback as - follows during each evaluation by the optimizer: the evaluation count, - the optimizer parameters for the ansatz, the estimated value, - the estimation metadata, and the current step. - """ - super().__init__() - - self.estimator = estimator - self.fidelity = fidelity - self.ansatz = ansatz - self.optimizer = optimizer - self.k = k - self.betas = betas - # this has to go via getters and setters due to the VariationalAlgorithm interface - self.initial_point = initial_point - self.callback = callback - - self._eval_count = 0 - - @property - def initial_point(self) -> Sequence[float] | Sequence[Sequence[float]] | None: - """Returns initial point.""" - return self._initial_point - - @initial_point.setter - def initial_point(self, initial_point: Sequence[float] | Sequence[Sequence[float]] | None): - """Sets initial point""" - self._initial_point = initial_point - - def _check_operator_ansatz(self, operator: BaseOperator | PauliSumOp): - """Check that the number of qubits of operator and ansatz match.""" - if operator is not None and self.ansatz is not None: - if operator.num_qubits != self.ansatz.num_qubits: - # try to set the number of qubits on the ansatz, if possible - try: - self.ansatz.num_qubits = operator.num_qubits - except AttributeError as exc: - raise AlgorithmError( - "The number of qubits of the ansatz does not match the " - "operator, and the ansatz does not allow setting the " - "number of qubits using `num_qubits`." - ) from exc - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - def compute_eigenvalues( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> VQDResult: - super().compute_eigenvalues(operator, aux_operators) - - # this sets the size of the ansatz, so it must be called before the initial point - # validation - self._check_operator_ansatz(operator) - - bounds = validate_bounds(self.ansatz) - - # We need to handle the array entries being zero or Optional i.e. having value None - if aux_operators: - zero_op = SparsePauliOp.from_list([("I" * self.ansatz.num_qubits, 0)]) - - # Convert the None and zero values when aux_operators is a list. - # Drop None and convert zero values when aux_operators is a dict. - if isinstance(aux_operators, list): - key_op_iterator = enumerate(aux_operators) - converted: ListOrDict[BaseOperator | PauliSumOp] = [zero_op] * len(aux_operators) - else: - key_op_iterator = aux_operators.items() - converted = {} - for key, op in key_op_iterator: - if op is not None: - converted[key] = zero_op if op == 0 else op - - aux_operators = converted - - else: - aux_operators = None - - if self.betas is None: - if isinstance(operator, PauliSumOp): - operator = operator.coeff * operator.primitive - - try: - upper_bound = sum(np.abs(operator.coeffs)) - - except Exception as exc: - raise NotImplementedError( - r"Beta autoevaluation is not supported for operators" - f"of type {type(operator)}." - ) from exc - - betas = [upper_bound * 10] * (self.k) - logger.info("beta autoevaluated to %s", betas[0]) - else: - betas = self.betas - - result = self._build_vqd_result() - - if aux_operators is not None: - aux_values = [] - - # We keep a list of the bound circuits with optimal parameters, to avoid re-binding - # the same parameters to the ansatz if we do multiple steps - prev_states = [] - - num_initial_points = 0 - if self.initial_point is not None: - initial_points = np.reshape(self.initial_point, (-1, self.ansatz.num_parameters)) - num_initial_points = len(initial_points) - - # 0 just means the initial point is ``None`` and ``validate_initial_point`` - # will select a random point - if num_initial_points <= 1: - initial_point = validate_initial_point(self.initial_point, self.ansatz) - - for step in range(1, self.k + 1): - if num_initial_points > 1: - initial_point = validate_initial_point(initial_points[step - 1], self.ansatz) - - if step > 1: - prev_states.append(self.ansatz.assign_parameters(result.optimal_points[-1])) - - self._eval_count = 0 - energy_evaluation = self._get_evaluate_energy( - step, operator, betas, prev_states=prev_states - ) - - start_time = time() - - # TODO: add gradient support after FidelityGradients are implemented - if isinstance(self.optimizer, Sequence): - optimizer = self.optimizer[step - 1] - else: - optimizer = self.optimizer # fall back to single optimizer if not list - - if callable(optimizer): - opt_result = optimizer( # pylint: disable=not-callable - fun=energy_evaluation, x0=initial_point, bounds=bounds - ) - else: - # we always want to submit as many estimations per job as possible for minimal - # overhead on the hardware - was_updated = _set_default_batchsize(optimizer) - - opt_result = optimizer.minimize( - fun=energy_evaluation, x0=initial_point, bounds=bounds - ) - - # reset to original value - if was_updated: - optimizer.set_max_evals_grouped(None) - - eval_time = time() - start_time - - self._update_vqd_result(result, opt_result, eval_time, self.ansatz.copy()) - - if aux_operators is not None: - aux_value = estimate_observables( - self.estimator, self.ansatz, aux_operators, result.optimal_points[-1] - ) - aux_values.append(aux_value) - - if step == 1: - logger.info( - "Ground state optimization complete in %s seconds.\n" - "Found opt_params %s in %s evals", - eval_time, - result.optimal_points, - self._eval_count, - ) - else: - logger.info( - ( - "%s excited state optimization complete in %s s.\n" - "Found opt_params %s in %s evals" - ), - str(step - 1), - eval_time, - result.optimal_points, - self._eval_count, - ) - - # To match the signature of EigensolverResult - result.eigenvalues = np.array(result.eigenvalues) - - if aux_operators is not None: - result.aux_operators_evaluated = aux_values - - return result - - def _get_evaluate_energy( - self, - step: int, - operator: BaseOperator | PauliSumOp, - betas: Sequence[float], - prev_states: list[QuantumCircuit] | None = None, - ) -> Callable[[np.ndarray], float | np.ndarray]: - """Returns a function handle to evaluate the ansatz's energy for any given parameters. - This is the objective function to be passed to the optimizer that is used for evaluation. - - Args: - step: level of energy being calculated. 0 for ground, 1 for first excited state... - operator: The operator whose energy to evaluate. - betas: Beta parameters in the VQD paper. - prev_states: List of optimal circuits from previous rounds of optimization. - - Returns: - A callable that computes and returns the energy of the hamiltonian - of each parameter. - - Raises: - AlgorithmError: If the circuit is not parameterized (i.e. has 0 free parameters). - AlgorithmError: If operator was not provided. - RuntimeError: If the previous states array is of the wrong size. - """ - - num_parameters = self.ansatz.num_parameters - if num_parameters == 0: - raise AlgorithmError("The ansatz must be parameterized, but has no free parameters.") - - if step > 1 and (len(prev_states) + 1) != step: - raise RuntimeError( - f"Passed previous states of the wrong size." - f"Passed array has length {str(len(prev_states))}" - ) - - self._check_operator_ansatz(operator) - - def evaluate_energy(parameters: np.ndarray) -> float | np.ndarray: - # handle broadcasting: ensure parameters is of shape [array, array, ...] - if len(parameters.shape) == 1: - parameters = np.reshape(parameters, (-1, num_parameters)) - batch_size = len(parameters) - - estimator_job = self.estimator.run( - batch_size * [self.ansatz], batch_size * [operator], parameters - ) - - total_cost = np.zeros(batch_size) - - if step > 1: - # compute overlap cost - batched_prev_states = [state for state in prev_states for _ in range(batch_size)] - fidelity_job = self.fidelity.run( - batch_size * [self.ansatz] * (step - 1), - batched_prev_states, - np.tile(parameters, (step - 1, 1)), - ) - costs = fidelity_job.result().fidelities - - costs = np.reshape(costs, (step - 1, -1)) - for state, cost in enumerate(costs): - total_cost += np.real(betas[state] * cost) - - try: - estimator_result = estimator_job.result() - - except Exception as exc: - raise AlgorithmError("The primitive job to evaluate the energy failed!") from exc - - values = estimator_result.values + total_cost - - if self.callback is not None: - metadata = estimator_result.metadata - for params, value, meta in zip(parameters, values, metadata): - self._eval_count += 1 - self.callback(self._eval_count, params, value, meta, step) - else: - self._eval_count += len(values) - - return values if len(values) > 1 else values[0] - - return evaluate_energy - - @staticmethod - def _build_vqd_result() -> VQDResult: - result = VQDResult() - result.optimal_points = np.array([]) - result.optimal_parameters = [] - result.optimal_values = np.array([]) - result.cost_function_evals = np.array([], dtype=int) - result.optimizer_times = np.array([]) - result.eigenvalues = [] - result.optimizer_results = [] - result.optimal_circuits = [] - return result - - @staticmethod - def _update_vqd_result( - result: VQDResult, opt_result: OptimizerResult, eval_time, ansatz - ) -> VQDResult: - result.optimal_points = ( - np.concatenate([result.optimal_points, [opt_result.x]]) - if len(result.optimal_points) > 0 - else np.array([opt_result.x]) - ) - result.optimal_parameters.append(dict(zip(ansatz.parameters, opt_result.x))) - result.optimal_values = np.concatenate([result.optimal_values, [opt_result.fun]]) - result.cost_function_evals = np.concatenate([result.cost_function_evals, [opt_result.nfev]]) - result.optimizer_times = np.concatenate([result.optimizer_times, [eval_time]]) - result.eigenvalues.append(opt_result.fun + 0j) - result.optimizer_results.append(opt_result) - result.optimal_circuits.append(ansatz) - return result - - -class VQDResult(EigensolverResult): - """VQD Result.""" - - def __init__(self) -> None: - super().__init__() - - self._cost_function_evals: np.ndarray | None = None - self._optimizer_times: np.ndarray | None = None - self._optimal_values: np.ndarray | None = None - self._optimal_points: np.ndarray | None = None - self._optimal_parameters: list[dict] | None = None - self._optimizer_results: list[OptimizerResult] | None = None - self._optimal_circuits: list[QuantumCircuit] | None = None - - @property - def cost_function_evals(self) -> np.ndarray | None: - """Returns number of cost optimizer evaluations""" - return self._cost_function_evals - - @cost_function_evals.setter - def cost_function_evals(self, value: np.ndarray) -> None: - """Sets number of cost function evaluations""" - self._cost_function_evals = value - - @property - def optimizer_times(self) -> np.ndarray | None: - """Returns time taken for optimization for each step""" - return self._optimizer_times - - @optimizer_times.setter - def optimizer_times(self, value: np.ndarray) -> None: - """Sets time taken for optimization for each step""" - self._optimizer_times = value - - @property - def optimal_values(self) -> np.ndarray | None: - """Returns optimal value for each step""" - return self._optimal_values - - @optimal_values.setter - def optimal_values(self, value: np.ndarray) -> None: - """Sets optimal values""" - self._optimal_values = value - - @property - def optimal_points(self) -> np.ndarray | None: - """Returns optimal point for each step""" - return self._optimal_points - - @optimal_points.setter - def optimal_points(self, value: np.ndarray) -> None: - """Sets optimal points""" - self._optimal_points = value - - @property - def optimal_parameters(self) -> list[dict] | None: - """Returns the optimal parameters for each step""" - return self._optimal_parameters - - @optimal_parameters.setter - def optimal_parameters(self, value: list[dict]) -> None: - """Sets optimal parameters""" - self._optimal_parameters = value - - @property - def optimizer_results(self) -> list[OptimizerResult] | None: - """Returns the optimizer results for each step""" - return self._optimizer_results - - @optimizer_results.setter - def optimizer_results(self, value: list[OptimizerResult]) -> None: - """Sets optimizer results""" - self._optimizer_results = value - - @property - def optimal_circuits(self) -> list[QuantumCircuit] | None: - """The optimal circuits. Along with the optimal parameters, - these can be used to retrieve the different eigenstates.""" - return self._optimal_circuits - - @optimal_circuits.setter - def optimal_circuits(self, optimal_circuits: list[QuantumCircuit]) -> None: - self._optimal_circuits = optimal_circuits diff --git a/qiskit/algorithms/evolvers/__init__.py b/qiskit/algorithms/evolvers/__init__.py deleted file mode 100644 index 990c787b7c15..000000000000 --- a/qiskit/algorithms/evolvers/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Quantum Time Evolution package.""" - -from .evolution_result import EvolutionResult -from .evolution_problem import EvolutionProblem - -__all__ = [ - "EvolutionResult", - "EvolutionProblem", -] diff --git a/qiskit/algorithms/evolvers/evolution_problem.py b/qiskit/algorithms/evolvers/evolution_problem.py deleted file mode 100644 index 8d87dbd9ae6a..000000000000 --- a/qiskit/algorithms/evolvers/evolution_problem.py +++ /dev/null @@ -1,122 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Evolution problem class.""" - -from __future__ import annotations - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.opflow import OperatorBase, StateFn -from qiskit.utils.deprecation import deprecate_func -from ..list_or_dict import ListOrDict - - -class EvolutionProblem: - """Deprecated: Evolution problem class. - - The EvolutionProblem class has been superseded by the - :class:`qiskit.algorithms.time_evolvers.TimeEvolutionProblem` class. - This class will be deprecated in a future release and subsequently - removed after that. - - This class is the input to time evolution algorithms and must contain information on the total - evolution time, a quantum state to be evolved and under which Hamiltonian the state is evolved. - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.time_evolvers.TimeEvolutionProblem``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - hamiltonian: OperatorBase, - time: float, - initial_state: StateFn | QuantumCircuit | None = None, - aux_operators: ListOrDict[OperatorBase] | None = None, - truncation_threshold: float = 1e-12, - t_param: Parameter | None = None, - param_value_dict: dict[Parameter, complex] | None = None, - ): - """ - Args: - hamiltonian: The Hamiltonian under which to evolve the system. - time: Total time of evolution. - initial_state: The quantum state to be evolved for methods like Trotterization. - For variational time evolutions, where the evolution happens in an ansatz, - this argument is not required. - aux_operators: Optional list of auxiliary operators to be evaluated with the - evolved ``initial_state`` and their expectation values returned. - truncation_threshold: Defines a threshold under which values can be assumed to be 0. - Used when ``aux_operators`` is provided. - t_param: Time parameter in case of a time-dependent Hamiltonian. This - free parameter must be within the ``hamiltonian``. - param_value_dict: Maps free parameters in the problem to values. Depending on the - algorithm, it might refer to e.g. a Hamiltonian or an initial state. - - Raises: - ValueError: If non-positive time of evolution is provided. - """ - - self.t_param = t_param - self.param_value_dict = param_value_dict - self.hamiltonian = hamiltonian - self.time = time - self.initial_state = initial_state - self.aux_operators = aux_operators - self.truncation_threshold = truncation_threshold - - @property - def time(self) -> float: - """Returns time.""" - return self._time - - @time.setter - def time(self, time: float) -> None: - """ - Sets time and validates it. - - Raises: - ValueError: If time is not positive. - """ - if time <= 0: - raise ValueError(f"Evolution time must be > 0 but was {time}.") - self._time = time - - def validate_params(self) -> None: - """ - Checks if all parameters present in the Hamiltonian are also present in the dictionary - that maps them to values. - - Raises: - ValueError: If Hamiltonian parameters cannot be bound with data provided. - """ - if isinstance(self.hamiltonian, OperatorBase): - t_param_set = set() - if self.t_param is not None: - t_param_set.add(self.t_param) - hamiltonian_dict_param_set: set[Parameter] = set() - if self.param_value_dict is not None: - hamiltonian_dict_param_set = hamiltonian_dict_param_set.union( - set(self.param_value_dict.keys()) - ) - params_set = t_param_set.union(hamiltonian_dict_param_set) - hamiltonian_param_set = set(self.hamiltonian.parameters) - - if hamiltonian_param_set != params_set: - raise ValueError( - f"Provided parameters {params_set} do not match Hamiltonian parameters " - f"{hamiltonian_param_set}." - ) diff --git a/qiskit/algorithms/evolvers/evolution_result.py b/qiskit/algorithms/evolvers/evolution_result.py deleted file mode 100644 index 5dd9e103f669..000000000000 --- a/qiskit/algorithms/evolvers/evolution_result.py +++ /dev/null @@ -1,55 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for holding evolution result.""" - -from __future__ import annotations - -from qiskit import QuantumCircuit -from qiskit.algorithms.list_or_dict import ListOrDict -from qiskit.opflow import StateFn, OperatorBase -from qiskit.utils.deprecation import deprecate_func -from ..algorithm_result import AlgorithmResult - - -class EvolutionResult(AlgorithmResult): - """Deprecated: Class for holding evolution result. - - The EvolutionResult class has been superseded by the - :class:`qiskit.algorithms.time_evolvers.TimeEvolutionResult` class. - This class will be deprecated in a future release and subsequently - removed after that. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.time_evolvers.TimeEvolutionResult``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - evolved_state: StateFn | QuantumCircuit | OperatorBase, - aux_ops_evaluated: ListOrDict[tuple[complex, complex]] | None = None, - ): - """ - Args: - evolved_state: An evolved quantum state. - aux_ops_evaluated: Optional list of observables for which expected values on an evolved - state are calculated. These values are in fact tuples formatted as (mean, standard - deviation). - """ - - self.evolved_state = evolved_state - self.aux_ops_evaluated = aux_ops_evaluated diff --git a/qiskit/algorithms/evolvers/imaginary_evolver.py b/qiskit/algorithms/evolvers/imaginary_evolver.py deleted file mode 100644 index 74b301d3e539..000000000000 --- a/qiskit/algorithms/evolvers/imaginary_evolver.py +++ /dev/null @@ -1,55 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Interface for Quantum Imaginary Time Evolution.""" - -from abc import ABC, abstractmethod - -from qiskit.utils.deprecation import deprecate_func -from .evolution_problem import EvolutionProblem -from .evolution_result import EvolutionResult - - -class ImaginaryEvolver(ABC): - """Deprecated: Interface for Quantum Imaginary Time Evolution. - - The ImaginaryEvolver interface has been superseded by the - :class:`qiskit.algorithms.time_evolvers.ImaginaryTimeEvolver` interface. - This interface will be deprecated in a future release and subsequently - removed after that. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the interface ``qiskit.algorithms.time_evolvers.ImaginaryTimeEvolver``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__(self) -> None: - pass - - @abstractmethod - def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: - r"""Perform imaginary time evolution :math:`\exp(-\tau H)|\Psi\rangle`. - - Evolves an initial state :math:`|\Psi\rangle` for an imaginary time :math:`\tau` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - - Args: - evolution_problem: The definition of the evolution problem. - - Returns: - Evolution result which includes an evolved quantum state. - """ - raise NotImplementedError() diff --git a/qiskit/algorithms/evolvers/real_evolver.py b/qiskit/algorithms/evolvers/real_evolver.py deleted file mode 100644 index 5024143b59b6..000000000000 --- a/qiskit/algorithms/evolvers/real_evolver.py +++ /dev/null @@ -1,55 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Interface for Quantum Real Time Evolution.""" - -from abc import ABC, abstractmethod -from qiskit.utils.deprecation import deprecate_func - -from .evolution_problem import EvolutionProblem -from .evolution_result import EvolutionResult - - -class RealEvolver(ABC): - """Deprecated: Interface for Quantum Real Time Evolution. - - The RealEvolver interface has been superseded by the - :class:`qiskit.algorithms.time_evolvers.RealTimeEvolver` interface. - This interface will be deprecated in a future release and subsequently - removed after that. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the interface ``qiskit.algorithms.time_evolvers.RealTimeEvolver``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__(self) -> None: - pass - - @abstractmethod - def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: - r"""Perform real time evolution :math:`\exp(-i t H)|\Psi\rangle`. - - Evolves an initial state :math:`|\Psi\rangle` for a time :math:`t` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - - Args: - evolution_problem: The definition of the evolution problem. - - Returns: - Evolution result which includes an evolved quantum state. - """ - raise NotImplementedError() diff --git a/qiskit/algorithms/evolvers/trotterization/__init__.py b/qiskit/algorithms/evolvers/trotterization/__init__.py deleted file mode 100644 index fe1b8d8aedf2..000000000000 --- a/qiskit/algorithms/evolvers/trotterization/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""This package contains Trotterization-based Quantum Real Time Evolution algorithm. -It is compliant with the new Quantum Time Evolution Framework and makes use of -:class:`qiskit.synthesis.evolution.ProductFormula` and -:class:`~qiskit.circuit.library.PauliEvolutionGate` implementations. """ - -from qiskit.algorithms.evolvers.trotterization.trotter_qrte import ( - TrotterQRTE, -) - -__all__ = ["TrotterQRTE"] diff --git a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py deleted file mode 100644 index 538635c67f42..000000000000 --- a/qiskit/algorithms/evolvers/trotterization/trotter_qrte.py +++ /dev/null @@ -1,262 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""An algorithm to implement a Trotterization real time-evolution.""" - -from __future__ import annotations - -import warnings - -from qiskit import QuantumCircuit -from qiskit.algorithms.aux_ops_evaluator import eval_observables -from qiskit.algorithms.evolvers import EvolutionProblem, EvolutionResult -from qiskit.algorithms.evolvers.real_evolver import RealEvolver -from qiskit.opflow import ( - SummedOp, - PauliOp, - CircuitOp, - ExpectationBase, - CircuitSampler, - PauliSumOp, - StateFn, - OperatorBase, -) -from qiskit.circuit.library import PauliEvolutionGate -from qiskit.providers import Backend -from qiskit.synthesis import ProductFormula, LieTrotter -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_func - - -class TrotterQRTE(RealEvolver): - """Deprecated: Quantum Real Time Evolution using Trotterization. - - The TrotterQRTE class has been superseded by the - :class:`qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE` class. - This class will be deprecated in a future release and subsequently - removed after that. - - Type of Trotterization is defined by a ProductFormula provided. - - Examples:: - - from qiskit.opflow import X, Z, Zero - from qiskit.algorithms import EvolutionProblem, TrotterQRTE - from qiskit import BasicAer - from qiskit.utils import QuantumInstance - - operator = X + Z - initial_state = Zero - time = 1 - evolution_problem = EvolutionProblem(operator, 1, initial_state) - # LieTrotter with 1 rep - backend = BasicAer.get_backend("statevector_simulator") - quantum_instance = QuantumInstance(backend=backend) - trotter_qrte = TrotterQRTE(quantum_instance=quantum_instance) - evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE``." - " See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - product_formula: ProductFormula | None = None, - expectation: ExpectationBase | None = None, - quantum_instance: QuantumInstance | Backend | None = None, - ) -> None: - """ - Args: - product_formula: A Lie-Trotter-Suzuki product formula. The default is the Lie-Trotter - first order product formula with a single repetition. - expectation: An instance of ExpectationBase which defines a method for calculating - expectation values of EvolutionProblem.aux_operators. - quantum_instance: A quantum instance used for calculating expectation values of - EvolutionProblem.aux_operators. - """ - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__init__() - if product_formula is None: - product_formula = LieTrotter() - self._product_formula = product_formula - self._quantum_instance = None - self._circuit_sampler: CircuitSampler | None = None - if quantum_instance is not None: - self.quantum_instance = quantum_instance - self._expectation = expectation - - @property - def product_formula(self) -> ProductFormula: - """Returns a product formula used in the algorithm.""" - return self._product_formula - - @product_formula.setter - def product_formula(self, product_formula: ProductFormula) -> None: - """ - Sets a product formula. - Args: - product_formula: A formula that defines the Trotterization algorithm. - """ - self._product_formula = product_formula - - @property - def quantum_instance(self) -> QuantumInstance | None: - """Returns a quantum instance used in the algorithm.""" - return self._quantum_instance - - @quantum_instance.setter - def quantum_instance(self, quantum_instance: QuantumInstance | Backend | None) -> None: - """ - Sets a quantum instance and a circuit sampler. - Args: - quantum_instance: The quantum instance used to run this algorithm. - """ - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - - self._circuit_sampler = None - if quantum_instance is not None: - self._circuit_sampler = CircuitSampler(quantum_instance) - - self._quantum_instance = quantum_instance - - @property - def expectation(self) -> ExpectationBase | None: - """Returns an expectation used in the algorithm.""" - return self._expectation - - @expectation.setter - def expectation(self, expectation: ExpectationBase | None) -> None: - """ - Sets an expectation. - Args: - expectation: An instance of ExpectationBase which defines a method for calculating - expectation values of EvolutionProblem.aux_operators. - """ - self._expectation = expectation - - @classmethod - def supports_aux_operators(cls) -> bool: - """ - Whether computing the expectation value of auxiliary operators is supported. - - Returns: - True if ``aux_operators`` expectations in the EvolutionProblem can be evaluated, False - otherwise. - """ - return True - - def evolve(self, evolution_problem: EvolutionProblem) -> EvolutionResult: - """ - Evolves a quantum state for a given time using the Trotterization method - based on a product formula provided. The result is provided in the form of a quantum - circuit. If auxiliary operators are included in the ``evolution_problem``, they are - evaluated on an evolved state using a backend provided. - - .. note:: - Time-dependent Hamiltonians are not yet supported. - - Args: - evolution_problem: Instance defining evolution problem. For the included Hamiltonian, - ``PauliOp``, ``SummedOp`` or ``PauliSumOp`` are supported by TrotterQRTE. - - Returns: - Evolution result that includes an evolved state as a quantum circuit and, optionally, - auxiliary operators evaluated for a resulting state on a backend. - - Raises: - ValueError: If ``t_param`` is not set to None in the EvolutionProblem (feature not - currently supported). - ValueError: If the ``initial_state`` is not provided in the EvolutionProblem. - """ - evolution_problem.validate_params() - if evolution_problem.t_param is not None: - raise ValueError( - "TrotterQRTE does not accept a time dependent hamiltonian," - "``t_param`` from the EvolutionProblem should be set to None." - ) - - if evolution_problem.aux_operators is not None and ( - self._quantum_instance is None or self._expectation is None - ): - raise ValueError( - "aux_operators were provided for evaluations but no ``expectation`` or " - "``quantum_instance`` was provided." - ) - hamiltonian = evolution_problem.hamiltonian - if not isinstance(hamiltonian, (PauliOp, PauliSumOp, SummedOp)): - raise ValueError( - "TrotterQRTE only accepts PauliOp | " - f"PauliSumOp | SummedOp, {type(hamiltonian)} provided." - ) - if isinstance(hamiltonian, OperatorBase): - hamiltonian = hamiltonian.assign_parameters(evolution_problem.param_value_dict) - if isinstance(hamiltonian, SummedOp): - hamiltonian = self._summed_op_to_pauli_sum_op(hamiltonian) - # the evolution gate - evolution_gate = CircuitOp( - PauliEvolutionGate(hamiltonian, evolution_problem.time, synthesis=self._product_formula) - ) - - if evolution_problem.initial_state is not None: - initial_state = evolution_problem.initial_state - if isinstance(initial_state, QuantumCircuit): - initial_state = StateFn(initial_state) - evolved_state = evolution_gate @ initial_state - - else: - raise ValueError("``initial_state`` must be provided in the EvolutionProblem.") - - evaluated_aux_ops = None - if evolution_problem.aux_operators is not None: - evaluated_aux_ops = eval_observables( - self._quantum_instance, - evolved_state.primitive, - evolution_problem.aux_operators, - self._expectation, - evolution_problem.truncation_threshold, - ) - - return EvolutionResult(evolved_state, evaluated_aux_ops) - - @staticmethod - def _summed_op_to_pauli_sum_op( - hamiltonian: SummedOp, - ) -> PauliSumOp | PauliOp: - """ - Tries binding parameters in a Hamiltonian. - - Args: - hamiltonian: The Hamiltonian that defines an evolution. - - Returns: - Hamiltonian. - - Raises: - ValueError: If the ``SummedOp`` Hamiltonian contains operators of an invalid type. - """ - # PauliSumOp does not allow parametrized coefficients but after binding the parameters - # we need to convert it into a PauliSumOp for the PauliEvolutionGate. - op_list = [] - for op in hamiltonian.oplist: - if not isinstance(op, PauliOp): - raise ValueError( - "Content of the Hamiltonian not of type PauliOp. The " - f"following type detected: {type(op)}." - ) - op_list.append(op) - return sum(op_list) diff --git a/qiskit/algorithms/exceptions.py b/qiskit/algorithms/exceptions.py deleted file mode 100644 index 1f830a3cba95..000000000000 --- a/qiskit/algorithms/exceptions.py +++ /dev/null @@ -1,21 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Exception for errors raised by Algorithms module.""" - -from qiskit.exceptions import QiskitError - - -class AlgorithmError(QiskitError): - """For Algorithm specific errors.""" - - pass diff --git a/qiskit/algorithms/gradients/__init__.py b/qiskit/algorithms/gradients/__init__.py deleted file mode 100644 index ff3a2fceca5e..000000000000 --- a/qiskit/algorithms/gradients/__init__.py +++ /dev/null @@ -1,130 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -============================================== -Gradients (:mod:`qiskit.algorithms.gradients`) -============================================== - -.. currentmodule:: qiskit.algorithms.gradients - -Base Classes -============ - -.. autosummary:: - :toctree: ../stubs/ - - BaseEstimatorGradient - BaseQGT - BaseSamplerGradient - EstimatorGradientResult - SamplerGradientResult - QGTResult - -Finite Differences -================== - -.. autosummary:: - :toctree: ../stubs/ - - FiniteDiffEstimatorGradient - FiniteDiffSamplerGradient - -Linear Combination of Unitaries -=============================== - -.. autosummary:: - :toctree: ../stubs/ - - LinCombEstimatorGradient - LinCombSamplerGradient - LinCombQGT - -Parameter Shift Rules -===================== - -.. autosummary:: - :toctree: ../stubs/ - - ParamShiftEstimatorGradient - ParamShiftSamplerGradient - -Quantum Fisher Information -========================== - -.. autosummary:: - :toctree: ../stubs/ - - QFIResult - QFI - -Classical Methods -================= - -.. autosummary:: - :toctree: ../stubs/ - - ReverseEstimatorGradient - ReverseQGT - -Simultaneous Perturbation Stochastic Approximation -================================================== - -.. autosummary:: - :toctree: ../stubs/ - - SPSAEstimatorGradient - SPSASamplerGradient -""" - -from .base.base_estimator_gradient import BaseEstimatorGradient -from .base.base_qgt import BaseQGT -from .base.base_sampler_gradient import BaseSamplerGradient -from .base.estimator_gradient_result import EstimatorGradientResult -from .finite_diff.finite_diff_estimator_gradient import FiniteDiffEstimatorGradient -from .finite_diff.finite_diff_sampler_gradient import FiniteDiffSamplerGradient -from .lin_comb.lin_comb_estimator_gradient import DerivativeType, LinCombEstimatorGradient -from .lin_comb.lin_comb_qgt import LinCombQGT -from .lin_comb.lin_comb_sampler_gradient import LinCombSamplerGradient -from .param_shift.param_shift_estimator_gradient import ParamShiftEstimatorGradient -from .param_shift.param_shift_sampler_gradient import ParamShiftSamplerGradient -from .qfi import QFI -from .qfi_result import QFIResult -from .base.qgt_result import QGTResult -from .base.sampler_gradient_result import SamplerGradientResult -from .spsa.spsa_estimator_gradient import SPSAEstimatorGradient -from .spsa.spsa_sampler_gradient import SPSASamplerGradient -from .reverse.reverse_gradient import ReverseEstimatorGradient -from .reverse.reverse_qgt import ReverseQGT - -__all__ = [ - "BaseEstimatorGradient", - "BaseQGT", - "BaseSamplerGradient", - "DerivativeType", - "EstimatorGradientResult", - "FiniteDiffEstimatorGradient", - "FiniteDiffSamplerGradient", - "LinCombEstimatorGradient", - "LinCombQGT", - "LinCombSamplerGradient", - "ParamShiftEstimatorGradient", - "ParamShiftSamplerGradient", - "QFI", - "QFIResult", - "QGTResult", - "SamplerGradientResult", - "SPSAEstimatorGradient", - "SPSASamplerGradient", - "ReverseEstimatorGradient", - "ReverseQGT", -] diff --git a/qiskit/algorithms/gradients/base/__init__.py b/qiskit/algorithms/gradients/base/__init__.py deleted file mode 100644 index 8f5fd2a37f84..000000000000 --- a/qiskit/algorithms/gradients/base/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/qiskit/algorithms/gradients/base/base_estimator_gradient.py b/qiskit/algorithms/gradients/base/base_estimator_gradient.py deleted file mode 100644 index 0cbf478fa2ec..000000000000 --- a/qiskit/algorithms/gradients/base/base_estimator_gradient.py +++ /dev/null @@ -1,360 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Abstract base class of gradient for ``Estimator``. -""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from collections.abc import Sequence -from copy import copy - -import numpy as np - -from qiskit.circuit import Parameter, ParameterExpression, QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator -from qiskit.primitives.utils import _circuit_key -from qiskit.providers import Options -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.transpiler.passes import TranslateParameterizedGates - -from .estimator_gradient_result import EstimatorGradientResult -from ..utils import ( - DerivativeType, - GradientCircuit, - _assign_unique_parameters, - _make_gradient_parameters, - _make_gradient_parameter_values, -) - -from ...algorithm_job import AlgorithmJob - - -class BaseEstimatorGradient(ABC): - """Base class for an ``EstimatorGradient`` to compute the gradients of the expectation value.""" - - def __init__( - self, - estimator: BaseEstimator, - options: Options | None = None, - derivative_type: DerivativeType = DerivativeType.REAL, - ): - r""" - Args: - estimator: The estimator used to compute the gradients. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - derivative_type: The type of derivative. Can be either ``DerivativeType.REAL`` - ``DerivativeType.IMAG``, or ``DerivativeType.COMPLEX``. - - - ``DerivativeType.REAL`` computes :math:`2 \mathrm{Re}[⟨ψ(ω)|O(θ)|dω ψ(ω)〉]`. - - ``DerivativeType.IMAG`` computes :math:`2 \mathrm{Im}[⟨ψ(ω)|O(θ)|dω ψ(ω)〉]`. - - ``DerivativeType.COMPLEX`` computes :math:`2 ⟨ψ(ω)|O(θ)|dω ψ(ω)〉`. - - Defaults to ``DerivativeType.REAL``, as this yields e.g. the commonly-used energy - gradient and this type is the only supported type for function-level schemes like - finite difference. - """ - self._estimator: BaseEstimator = estimator - self._default_options = Options() - if options is not None: - self._default_options.update_options(**options) - self._derivative_type = derivative_type - - self._gradient_circuit_cache: dict[ - tuple, - GradientCircuit, - ] = {} - - @property - def derivative_type(self) -> DerivativeType: - """Return the derivative type (real, imaginary or complex). - - Returns: - The derivative type. - """ - return self._derivative_type - - def run( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter] | None] | None = None, - **options, - ) -> AlgorithmJob: - """Run the job of the estimator gradient on the given circuits. - - Args: - circuits: The list of quantum circuits to compute the gradients. - observables: The list of observables. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the gradients of - the specified parameters. Each sequence of parameters corresponds to a circuit in - ``circuits``. Defaults to None, which means that the gradients of all parameters in - each circuit are calculated. None in the sequence means that the gradients of all - parameters in the corresponding circuit are calculated. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - - Returns: - The job object of the gradients of the expectation values. The i-th result corresponds to - ``circuits[i]`` evaluated with parameters bound as ``parameter_values[i]``. The j-th - element of the i-th result corresponds to the gradient of the i-th circuit with respect - to the j-th parameter. - - Raises: - ValueError: Invalid arguments are given. - """ - if isinstance(circuits, QuantumCircuit): - # Allow a single circuit to be passed in. - circuits = (circuits,) - if isinstance(observables, (BaseOperator, PauliSumOp)): - # Allow a single observable to be passed in. - observables = (observables,) - - if parameters is None: - # If parameters is None, we calculate the gradients of all parameters in each circuit. - parameters = [circuit.parameters for circuit in circuits] - else: - # If parameters is not None, we calculate the gradients of the specified parameters. - # None in parameters means that the gradients of all parameters in the corresponding - # circuit are calculated. - parameters = [ - params if params is not None else circuits[i].parameters - for i, params in enumerate(parameters) - ] - # Validate the arguments. - self._validate_arguments(circuits, observables, parameter_values, parameters) - # The priority of run option is as follows: - # options in ``run`` method > gradient's default options > primitive's default setting. - opts = copy(self._default_options) - opts.update_options(**options) - # Run the job. - job = AlgorithmJob( - self._run, circuits, observables, parameter_values, parameters, **opts.__dict__ - ) - job.submit() - return job - - @abstractmethod - def _run( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> EstimatorGradientResult: - """Compute the estimator gradients on the given circuits.""" - raise NotImplementedError() - - def _preprocess( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - supported_gates: Sequence[str], - ) -> tuple[Sequence[QuantumCircuit], Sequence[Sequence[float]], Sequence[Sequence[Parameter]]]: - """Preprocess the gradient. This makes a gradient circuit for each circuit. The gradient - circuit is a transpiled circuit by using the supported gates, and has unique parameters. - ``parameter_values`` and ``parameters`` are also updated to match the gradient circuit. - - Args: - circuits: The list of quantum circuits to compute the gradients. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the gradients of the specified - parameters. - supported_gates: The supported gates used to transpile the circuit. - - Returns: - The list of gradient circuits, the list of parameter values, and the list of parameters. - parameter_values and parameters are updated to match the gradient circuit. - """ - translator = TranslateParameterizedGates(supported_gates) - g_circuits, g_parameter_values, g_parameters = [], [], [] - for circuit, parameter_value_, parameters_ in zip(circuits, parameter_values, parameters): - circuit_key = _circuit_key(circuit) - if circuit_key not in self._gradient_circuit_cache: - unrolled = translator(circuit) - self._gradient_circuit_cache[circuit_key] = _assign_unique_parameters(unrolled) - gradient_circuit = self._gradient_circuit_cache[circuit_key] - g_circuits.append(gradient_circuit.gradient_circuit) - g_parameter_values.append( - _make_gradient_parameter_values(circuit, gradient_circuit, parameter_value_) - ) - g_parameters.append(_make_gradient_parameters(gradient_circuit, parameters_)) - return g_circuits, g_parameter_values, g_parameters - - def _postprocess( - self, - results: EstimatorGradientResult, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - ) -> EstimatorGradientResult: - """Postprocess the gradients. This method computes the gradient of the original circuits - by applying the chain rule to the gradient of the circuits with unique parameters. - - Args: - results: The computed gradients for the circuits with unique parameters. - circuits: The list of original circuits submitted for gradient computation. - parameter_values: The list of parameter values to be bound to the circuits. - parameters: The sequence of parameters to calculate only the gradients of the specified - parameters. - - Returns: - The gradients of the original circuits. - """ - gradients, metadata = [], [] - for idx, (circuit, parameter_values_, parameters_) in enumerate( - zip(circuits, parameter_values, parameters) - ): - gradient = np.zeros(len(parameters_)) - if ( - "derivative_type" in results.metadata[idx] - and results.metadata[idx]["derivative_type"] == DerivativeType.COMPLEX - ): - # If the derivative type is complex, cast the gradient to complex. - gradient = gradient.astype("complex") - gradient_circuit = self._gradient_circuit_cache[_circuit_key(circuit)] - g_parameters = _make_gradient_parameters(gradient_circuit, parameters_) - # Make a map from the gradient parameter to the respective index in the gradient. - g_parameter_indices = {param: i for i, param in enumerate(g_parameters)} - # Compute the original gradient from the gradient of the gradient circuit - # by using the chain rule. - for i, parameter in enumerate(parameters_): - for g_parameter, coeff in gradient_circuit.parameter_map[parameter]: - # Compute the coefficient - if isinstance(coeff, ParameterExpression): - local_map = { - p: parameter_values_[circuit.parameters.data.index(p)] - for p in coeff.parameters - } - bound_coeff = coeff.bind(local_map) - else: - bound_coeff = coeff - # The original gradient is a sum of the gradients of the parameters in the - # gradient circuit multiplied by the coefficients. - gradient[i] += ( - float(bound_coeff) - * results.gradients[idx][g_parameter_indices[g_parameter]] - ) - gradients.append(gradient) - metadata.append({"parameters": parameters_}) - return EstimatorGradientResult( - gradients=gradients, metadata=metadata, options=results.options - ) - - @staticmethod - def _validate_arguments( - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - ) -> None: - """Validate the arguments of the ``run`` method. - - Args: - circuits: The list of quantum circuits to compute the gradients. - observables: The list of observables. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the gradients of the specified - parameters. - - Raises: - ValueError: Invalid arguments are given. - """ - if len(circuits) != len(parameter_values): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of parameter value sets ({len(parameter_values)})." - ) - - for i, (circuit, parameter_value) in enumerate(zip(circuits, parameter_values)): - if not circuit.num_parameters: - raise ValueError(f"The {i}-th circuit is not parameterised.") - if len(parameter_value) != circuit.num_parameters: - raise ValueError( - f"The number of values ({len(parameter_value)}) does not match " - f"the number of parameters ({circuit.num_parameters}) for the {i}-th circuit." - ) - - if len(circuits) != len(observables): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of observables ({len(observables)})." - ) - - for i, (circuit, observable) in enumerate(zip(circuits, observables)): - if circuit.num_qubits != observable.num_qubits: - raise ValueError( - f"The number of qubits of the {i}-th circuit ({circuit.num_qubits}) does " - f"not match the number of qubits of the {i}-th observable " - f"({observable.num_qubits})." - ) - - if len(circuits) != len(parameters): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of the list of specified parameters ({len(parameters)})." - ) - - for i, (circuit, parameters_) in enumerate(zip(circuits, parameters)): - if not set(parameters_).issubset(circuit.parameters): - raise ValueError( - f"The {i}-th parameters contains parameters not present in the " - f"{i}-th circuit." - ) - - @property - def options(self) -> Options: - """Return the union of estimator options setting and gradient default options, - where, if the same field is set in both, the gradient's default options override - the primitive's default setting. - - Returns: - The gradient default + estimator options. - """ - return self._get_local_options(self._default_options.__dict__) - - def update_default_options(self, **options): - """Update the gradient's default options setting. - - Args: - **options: The fields to update the default options. - """ - - self._default_options.update_options(**options) - - def _get_local_options(self, options: Options) -> Options: - """Return the union of the primitive's default setting, - the gradient default options, and the options in the ``run`` method. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - - Args: - options: The fields to update the options - - Returns: - The gradient default + estimator + run options. - """ - opts = copy(self._estimator.options) - opts.update_options(**options) - return opts diff --git a/qiskit/algorithms/gradients/base/base_qgt.py b/qiskit/algorithms/gradients/base/base_qgt.py deleted file mode 100644 index f2999a8f2bf0..000000000000 --- a/qiskit/algorithms/gradients/base/base_qgt.py +++ /dev/null @@ -1,383 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Abstract base class of the Quantum Geometric Tensor (QGT). -""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from collections.abc import Sequence -from copy import copy - -import numpy as np - -from qiskit.circuit import Parameter, ParameterExpression, QuantumCircuit -from qiskit.primitives import BaseEstimator -from qiskit.primitives.utils import _circuit_key -from qiskit.providers import Options -from qiskit.transpiler.passes import TranslateParameterizedGates - -from .qgt_result import QGTResult -from ..utils import ( - DerivativeType, - GradientCircuit, - _assign_unique_parameters, - _make_gradient_parameters, - _make_gradient_parameter_values, -) - -from ...algorithm_job import AlgorithmJob - - -class BaseQGT(ABC): - r"""Base class to computes the Quantum Geometric Tensor (QGT) given a pure, - parameterized quantum state. QGT is defined as: - - .. math:: - - \mathrm{QGT}_{ij}= \langle \partial_i \psi | \partial_j \psi \rangle - - \langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle. - """ - - def __init__( - self, - estimator: BaseEstimator, - phase_fix: bool = True, - derivative_type: DerivativeType = DerivativeType.COMPLEX, - options: Options | None = None, - ): - r""" - Args: - estimator: The estimator used to compute the QGT. - phase_fix: Whether to calculate the second term (phase fix) of the QGT, which is - :math:`\langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle`. - Defaults to ``True``. - derivative_type: The type of derivative. Can be either ``DerivativeType.REAL`` - ``DerivativeType.IMAG``, or ``DerivativeType.COMPLEX``. Defaults to - ``DerivativeType.REAL``. - - - ``DerivativeType.REAL`` computes - - .. math:: - - \mathrm{Re(QGT)}_{ij}= \mathrm{Re}[\langle \partial_i \psi | \partial_j \psi \rangle - - \langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle]. - - - ``DerivativeType.IMAG`` computes - - .. math:: - - \mathrm{Im(QGT)}_{ij}= \mathrm{Im}[\langle \partial_i \psi | \partial_j \psi \rangle - - \langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle]. - - - ``DerivativeType.COMPLEX`` computes - - .. math:: - - \mathrm{QGT}_{ij}= [\langle \partial_i \psi | \partial_j \psi \rangle - - \langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle]. - - options: Backend runtime options used for circuit execution. The order of priority is: - options in ``run`` method > QGT's default options > primitive's default - setting. Higher priority setting overrides lower priority setting. - """ - self._estimator: BaseEstimator = estimator - self._phase_fix: bool = phase_fix - self._derivative_type: DerivativeType = derivative_type - self._default_options = Options() - if options is not None: - self._default_options.update_options(**options) - self._qgt_circuit_cache: dict[tuple, GradientCircuit] = {} - self._gradient_circuit_cache: dict[tuple, GradientCircuit] = {} - - @property - def derivative_type(self) -> DerivativeType: - """The derivative type.""" - return self._derivative_type - - @derivative_type.setter - def derivative_type(self, derivative_type: DerivativeType) -> None: - """Set the derivative type.""" - self._derivative_type = derivative_type - - def run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter] | None] | None = None, - **options, - ) -> AlgorithmJob: - """Run the job of the QGTs on the given circuits. - - Args: - circuits: The list of quantum circuits to compute the QGTs. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the QGTs of - the specified parameters. Each sequence of parameters corresponds to a circuit in - ``circuits``. Defaults to None, which means that the QGTs of all parameters in - each circuit are calculated. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > QGT's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting. - - Returns: - The job object of the QGTs of the expectation values. The i-th result corresponds to - ``circuits[i]`` evaluated with parameters bound as ``parameter_values[i]``. - - Raises: - ValueError: Invalid arguments are given. - """ - if isinstance(circuits, QuantumCircuit): - # Allow a single circuit to be passed in. - circuits = (circuits,) - - if parameters is None: - # If parameters is None, we calculate the gradients of all parameters in each circuit. - parameters = [circuit.parameters for circuit in circuits] - else: - # If parameters is not None, we calculate the gradients of the specified parameters. - # None in parameters means that the gradients of all parameters in the corresponding - # circuit are calculated. - parameters = [ - params if params is not None else circuits[i].parameters - for i, params in enumerate(parameters) - ] - # Validate the arguments. - self._validate_arguments(circuits, parameter_values, parameters) - # The priority of run option is as follows: - # options in ``run`` method > QGT's default options > primitive's default setting. - opts = copy(self._default_options) - opts.update_options(**options) - job = AlgorithmJob(self._run, circuits, parameter_values, parameters, **opts.__dict__) - job.submit() - return job - - @abstractmethod - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> QGTResult: - """Compute the QGTs on the given circuits.""" - raise NotImplementedError() - - def _preprocess( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - supported_gates: Sequence[str], - ) -> tuple[Sequence[QuantumCircuit], Sequence[Sequence[float]], Sequence[Sequence[Parameter]]]: - """Preprocess the gradient. This makes a gradient circuit for each circuit. The gradient - circuit is a transpiled circuit by using the supported gates, and has unique parameters. - ``parameter_values`` and ``parameters`` are also updated to match the gradient circuit. - - Args: - circuits: The list of quantum circuits to compute the gradients. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the gradients of the specified - parameters. - supported_gates: The supported gates used to transpile the circuit. - - Returns: - The list of gradient circuits, the list of parameter values, and the list of parameters. - parameter_values and parameters are updated to match the gradient circuit. - """ - translator = TranslateParameterizedGates(supported_gates) - g_circuits, g_parameter_values, g_parameters = [], [], [] - for circuit, parameter_value_, parameters_ in zip(circuits, parameter_values, parameters): - circuit_key = _circuit_key(circuit) - if circuit_key not in self._gradient_circuit_cache: - unrolled = translator(circuit) - self._gradient_circuit_cache[circuit_key] = _assign_unique_parameters(unrolled) - gradient_circuit = self._gradient_circuit_cache[circuit_key] - g_circuits.append(gradient_circuit.gradient_circuit) - g_parameter_values.append( - _make_gradient_parameter_values(circuit, gradient_circuit, parameter_value_) - ) - g_parameters_ = [ - g_param - for g_param in gradient_circuit.gradient_circuit.parameters - if g_param in _make_gradient_parameters(gradient_circuit, parameters_) - ] - g_parameters.append(g_parameters_) - return g_circuits, g_parameter_values, g_parameters - - def _postprocess( - self, - results: QGTResult, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - ) -> QGTResult: - """Postprocess the QGTs. This method computes the QGTs of the original circuits - by applying the chain rule to the QGTs of the circuits with unique parameters. - - Args: - results: The computed QGT for the circuits with unique parameters. - circuits: The list of original circuits submitted for gradient computation. - parameter_values: The list of parameter values to be bound to the circuits. - parameters: The sequence of parameters to calculate only the gradients of the specified - parameters. - - Returns: - The QGTs of the original circuits. - """ - qgts, metadata = [], [] - for idx, (circuit, parameter_values_, parameters_) in enumerate( - zip(circuits, parameter_values, parameters) - ): - dtype = complex if self.derivative_type == DerivativeType.COMPLEX else float - qgt: np.ndarray = np.zeros((len(parameters_), len(parameters_)), dtype=dtype) - - gradient_circuit = self._gradient_circuit_cache[_circuit_key(circuit)] - g_parameters = _make_gradient_parameters(gradient_circuit, parameters_) - # Make a map from the gradient parameter to the respective index in the gradient. - # parameters_ = [param for param in circuit.parameters if param in parameters_] - g_parameter_indices = [ - param - for param in gradient_circuit.gradient_circuit.parameters - if param in g_parameters - ] - g_parameter_indices = {param: i for i, param in enumerate(g_parameter_indices)} - rows, cols = np.triu_indices(len(parameters_)) - for row, col in zip(rows, cols): - for g_parameter1, coeff1 in gradient_circuit.parameter_map[parameters_[row]]: - for g_parameter2, coeff2 in gradient_circuit.parameter_map[parameters_[col]]: - if isinstance(coeff1, ParameterExpression): - local_map = { - p: parameter_values_[circuit.parameters.data.index(p)] - for p in coeff1.parameters - } - bound_coeff1 = coeff1.bind(local_map) - else: - bound_coeff1 = coeff1 - if isinstance(coeff2, ParameterExpression): - local_map = { - p: parameter_values_[circuit.parameters.data.index(p)] - for p in coeff2.parameters - } - bound_coeff2 = coeff2.bind(local_map) - else: - bound_coeff2 = coeff2 - qgt[row, col] += ( - float(bound_coeff1) - * float(bound_coeff2) - * results.qgts[idx][ - g_parameter_indices[g_parameter1], g_parameter_indices[g_parameter2] - ] - ) - - if self.derivative_type == DerivativeType.IMAG: - qgt += -1 * np.triu(qgt, k=1).T - else: - qgt += np.triu(qgt, k=1).conjugate().T - qgts.append(qgt) - metadata.append([{"parameters": parameters_}]) - return QGTResult( - qgts=qgts, - derivative_type=self.derivative_type, - metadata=metadata, - options=results.options, - ) - - @staticmethod - def _validate_arguments( - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - ) -> None: - """Validate the arguments of the ``run`` method. - - Args: - circuits: The list of quantum circuits to compute the QGTs. - parameter_values: The list of parameter values to be bound to the circuits. - parameters: The sequence of parameters with respect to which the QGTs should be - computed. - - Raises: - ValueError: Invalid arguments are given. - """ - if len(circuits) != len(parameter_values): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of parameter values ({len(parameter_values)})." - ) - - if len(circuits) != len(parameters): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of the specified parameter sets ({len(parameters)})." - ) - - for i, (circuit, parameter_value) in enumerate(zip(circuits, parameter_values)): - if not circuit.num_parameters: - raise ValueError(f"The {i}-th circuit is not parameterised.") - if len(parameter_value) != circuit.num_parameters: - raise ValueError( - f"The number of values ({len(parameter_value)}) does not match " - f"the number of parameters ({circuit.num_parameters}) for the {i}-th circuit." - ) - - if len(circuits) != len(parameters): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of the list of specified parameters ({len(parameters)})." - ) - - for i, (circuit, parameters_) in enumerate(zip(circuits, parameters)): - if not set(parameters_).issubset(circuit.parameters): - raise ValueError( - f"The {i}-th parameters contains parameters not present in the " - f"{i}-th circuit." - ) - - @property - def options(self) -> Options: - """Return the union of estimator options setting and QGT default options, - where, if the same field is set in both, the QGT's default options override - the primitive's default setting. - - Returns: - The QGT default + estimator options. - """ - return self._get_local_options(self._default_options.__dict__) - - def update_default_options(self, **options): - """Update the gradient's default options setting. - - Args: - **options: The fields to update the default options. - """ - - self._default_options.update_options(**options) - - def _get_local_options(self, options: Options) -> Options: - """Return the union of the primitive's default setting, - the QGT default options, and the options in the ``run`` method. - The order of priority is: options in ``run`` method > QGT's default options > primitive's - default setting. - - Args: - options: The fields to update the options - - Returns: - The QGT default + estimator + run options. - """ - opts = copy(self._estimator.options) - opts.update_options(**options) - return opts diff --git a/qiskit/algorithms/gradients/base/base_sampler_gradient.py b/qiskit/algorithms/gradients/base/base_sampler_gradient.py deleted file mode 100644 index b4947365fd5d..000000000000 --- a/qiskit/algorithms/gradients/base/base_sampler_gradient.py +++ /dev/null @@ -1,296 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Abstract base class of gradient for ``Sampler``. -""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from collections import defaultdict -from collections.abc import Sequence -from copy import copy - -from qiskit.circuit import Parameter, ParameterExpression, QuantumCircuit -from qiskit.primitives import BaseSampler -from qiskit.primitives.utils import _circuit_key -from qiskit.providers import Options -from qiskit.transpiler.passes import TranslateParameterizedGates - -from .sampler_gradient_result import SamplerGradientResult -from ..utils import ( - GradientCircuit, - _assign_unique_parameters, - _make_gradient_parameters, - _make_gradient_parameter_values, -) - -from ...algorithm_job import AlgorithmJob - - -class BaseSamplerGradient(ABC): - """Base class for a ``SamplerGradient`` to compute the gradients of the sampling probability.""" - - def __init__(self, sampler: BaseSampler, options: Options | None = None): - """ - Args: - sampler: The sampler used to compute the gradients. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - """ - self._sampler: BaseSampler = sampler - self._default_options = Options() - if options is not None: - self._default_options.update_options(**options) - self._gradient_circuit_cache: dict[tuple, GradientCircuit] = {} - - def run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter] | None] | None = None, - **options, - ) -> AlgorithmJob: - """Run the job of the sampler gradient on the given circuits. - - Args: - circuits: The list of quantum circuits to compute the gradients. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the gradients of - the specified parameters. Each sequence of parameters corresponds to a circuit in - ``circuits``. Defaults to None, which means that the gradients of all parameters in - each circuit are calculated. None in the sequence means that the gradients of all - parameters in the corresponding circuit are calculated. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - Returns: - The job object of the gradients of the sampling probability. The i-th result - corresponds to ``circuits[i]`` evaluated with parameters bound as ``parameter_values[i]``. - The j-th quasi-probability distribution in the i-th result corresponds to the gradients of - the sampling probability for the j-th parameter in ``circuits[i]``. - - Raises: - ValueError: Invalid arguments are given. - """ - if isinstance(circuits, QuantumCircuit): - # Allow a single circuit to be passed in. - circuits = (circuits,) - if parameters is None: - # If parameters is None, we calculate the gradients of all parameters in each circuit. - parameters = [circuit.parameters for circuit in circuits] - else: - # If parameters is not None, we calculate the gradients of the specified parameters. - # None in parameters means that the gradients of all parameters in the corresponding - # circuit are calculated. - parameters = [ - params if params is not None else circuits[i].parameters - for i, params in enumerate(parameters) - ] - # Validate the arguments. - self._validate_arguments(circuits, parameter_values, parameters) - # The priority of run option is as follows: - # options in `run` method > gradient's default options > primitive's default options. - opts = copy(self._default_options) - opts.update_options(**options) - job = AlgorithmJob(self._run, circuits, parameter_values, parameters, **opts.__dict__) - job.submit() - return job - - @abstractmethod - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> SamplerGradientResult: - """Compute the sampler gradients on the given circuits.""" - raise NotImplementedError() - - def _preprocess( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - supported_gates: Sequence[str], - ) -> tuple[Sequence[QuantumCircuit], Sequence[Sequence[float]], Sequence[set[Parameter]]]: - """Preprocess the gradient. This makes a gradient circuit for each circuit. The gradient - circuit is a transpiled circuit by using the supported gates, and has unique parameters. - ``parameter_values`` and ``parameters`` are also updated to match the gradient circuit. - - Args: - circuits: The list of quantum circuits to compute the gradients. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the gradients of the specified - parameters. - supported_gates: The supported gates used to transpile the circuit. - - Returns: - The list of gradient circuits, the list of parameter values, and the list of parameters. - parameter_values and parameters are updated to match the gradient circuit. - """ - translator = TranslateParameterizedGates(supported_gates) - g_circuits, g_parameter_values, g_parameters = [], [], [] - for circuit, parameter_value_, parameters_ in zip(circuits, parameter_values, parameters): - circuit_key = _circuit_key(circuit) - if circuit_key not in self._gradient_circuit_cache: - unrolled = translator(circuit) - self._gradient_circuit_cache[circuit_key] = _assign_unique_parameters(unrolled) - gradient_circuit = self._gradient_circuit_cache[circuit_key] - g_circuits.append(gradient_circuit.gradient_circuit) - g_parameter_values.append( - _make_gradient_parameter_values(circuit, gradient_circuit, parameter_value_) - ) - g_parameters.append(_make_gradient_parameters(gradient_circuit, parameters_)) - return g_circuits, g_parameter_values, g_parameters - - def _postprocess( - self, - results: SamplerGradientResult, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter] | None], - ) -> SamplerGradientResult: - """Postprocess the gradient. This computes the gradient of the original circuit from the - gradient of the gradient circuit by using the chain rule. - - Args: - results: The results of the gradient of the gradient circuits. - circuits: The list of quantum circuits to compute the gradients. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the gradients of the specified - parameters. - - Returns: - The results of the gradient of the original circuits. - """ - gradients, metadata = [], [] - for idx, (circuit, parameter_values_, parameters_) in enumerate( - zip(circuits, parameter_values, parameters) - ): - gradient_circuit = self._gradient_circuit_cache[_circuit_key(circuit)] - g_parameters = _make_gradient_parameters(gradient_circuit, parameters_) - # Make a map from the gradient parameter to the respective index in the gradient. - g_parameter_indices = {param: i for i, param in enumerate(g_parameters)} - # Compute the original gradient from the gradient of the gradient circuit - # by using the chain rule. - gradient = [] - for parameter in parameters_: - grad_dist: dict[int, float] = defaultdict(float) - for g_parameter, coeff in gradient_circuit.parameter_map[parameter]: - # Compute the coefficient - if isinstance(coeff, ParameterExpression): - local_map = { - p: parameter_values_[circuit.parameters.data.index(p)] - for p in coeff.parameters - } - bound_coeff = coeff.bind(local_map) - else: - bound_coeff = coeff - # The original gradient is a sum of the gradients of the parameters in the - # gradient circuit multiplied by the coefficients. - unique_gradient = results.gradients[idx][g_parameter_indices[g_parameter]] - for key, value in unique_gradient.items(): - grad_dist[key] += float(bound_coeff) * value - gradient.append(dict(grad_dist)) - gradients.append(gradient) - metadata.append([{"parameters": parameters_}]) - return SamplerGradientResult( - gradients=gradients, metadata=metadata, options=results.options - ) - - @staticmethod - def _validate_arguments( - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - ) -> None: - """Validate the arguments of the ``run`` method. - - Args: - circuits: The list of quantum circuits to compute the gradients. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the gradients of the specified - parameters. - - Raises: - ValueError: Invalid arguments are given. - """ - if len(circuits) != len(parameter_values): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of parameter value sets ({len(parameter_values)})." - ) - - for i, (circuit, parameter_value) in enumerate(zip(circuits, parameter_values)): - if not circuit.num_parameters: - raise ValueError(f"The {i}-th circuit is not parameterised.") - - if len(parameter_value) != circuit.num_parameters: - raise ValueError( - f"The number of values ({len(parameter_value)}) does not match " - f"the number of parameters ({circuit.num_parameters}) for the {i}-th circuit." - ) - - if len(circuits) != len(parameters): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of the specified parameter sets ({len(parameters)})." - ) - - for i, (circuit, parameters_) in enumerate(zip(circuits, parameters)): - if not set(parameters_).issubset(circuit.parameters): - raise ValueError( - f"The {i}-th parameter set contains parameters not present in the " - f"{i}-th circuit." - ) - - @property - def options(self) -> Options: - """Return the union of sampler options setting and gradient default options, - where, if the same field is set in both, the gradient's default options override - the primitive's default setting. - - Returns: - The gradient default + sampler options. - """ - return self._get_local_options(self._default_options.__dict__) - - def update_default_options(self, **options): - """Update the gradient's default options setting. - - Args: - **options: The fields to update the default options. - """ - - self._default_options.update_options(**options) - - def _get_local_options(self, options: Options) -> Options: - """Return the union of the primitive's default setting, - the gradient default options, and the options in the ``run`` method. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - - Args: - options: The fields to update the options - - Returns: - The gradient default + sampler + run options. - """ - opts = copy(self._sampler.options) - opts.update_options(**options) - return opts diff --git a/qiskit/algorithms/gradients/base/estimator_gradient_result.py b/qiskit/algorithms/gradients/base/estimator_gradient_result.py deleted file mode 100644 index ada3bdb2b7bf..000000000000 --- a/qiskit/algorithms/gradients/base/estimator_gradient_result.py +++ /dev/null @@ -1,35 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Estimator result class -""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any - -import numpy as np - -from qiskit.providers import Options - - -@dataclass(frozen=True) -class EstimatorGradientResult: - """Result of EstimatorGradient.""" - - gradients: list[np.ndarray] - """The gradients of the expectation values.""" - metadata: list[dict[str, Any]] - """Additional information about the job.""" - options: Options - """Primitive runtime options for the execution of the job.""" diff --git a/qiskit/algorithms/gradients/base/qgt_result.py b/qiskit/algorithms/gradients/base/qgt_result.py deleted file mode 100644 index f110e1c68381..000000000000 --- a/qiskit/algorithms/gradients/base/qgt_result.py +++ /dev/null @@ -1,39 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -QGT result class -""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any - -import numpy as np - -from qiskit.providers import Options - -from ..utils import DerivativeType - - -@dataclass(frozen=True) -class QGTResult: - """Result of QGT.""" - - qgts: list[np.ndarray] - """The QGT.""" - derivative_type: DerivativeType - """The type of derivative.""" - metadata: list[dict[str, Any]] - """Additional information about the job.""" - options: Options - """Primitive runtime options for the execution of the job.""" diff --git a/qiskit/algorithms/gradients/base/sampler_gradient_result.py b/qiskit/algorithms/gradients/base/sampler_gradient_result.py deleted file mode 100644 index b78b468f1b9f..000000000000 --- a/qiskit/algorithms/gradients/base/sampler_gradient_result.py +++ /dev/null @@ -1,33 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Sampler result class -""" - -from __future__ import annotations - -from typing import Any -from dataclasses import dataclass - -from qiskit.providers import Options - - -@dataclass(frozen=True) -class SamplerGradientResult: - """Result of SamplerGradient.""" - - gradients: list[list[dict[int, float]]] - """The gradients of the sample probabilities.""" - metadata: list[dict[str, Any]] - """Additional information about the job.""" - options: Options - """Primitive runtime options for the execution of the job.""" diff --git a/qiskit/algorithms/gradients/finite_diff/__init__.py b/qiskit/algorithms/gradients/finite_diff/__init__.py deleted file mode 100644 index 8f5fd2a37f84..000000000000 --- a/qiskit/algorithms/gradients/finite_diff/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/qiskit/algorithms/gradients/finite_diff/finite_diff_estimator_gradient.py b/qiskit/algorithms/gradients/finite_diff/finite_diff_estimator_gradient.py deleted file mode 100644 index ea1b987c2ff2..000000000000 --- a/qiskit/algorithms/gradients/finite_diff/finite_diff_estimator_gradient.py +++ /dev/null @@ -1,148 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Gradient of Sampler with Finite difference method.""" - -from __future__ import annotations - -from collections.abc import Sequence -from typing import Literal - -import numpy as np - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator -from qiskit.providers import Options -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..base.base_estimator_gradient import BaseEstimatorGradient -from ..base.estimator_gradient_result import EstimatorGradientResult - -from ...exceptions import AlgorithmError - - -class FiniteDiffEstimatorGradient(BaseEstimatorGradient): - """ - Compute the gradients of the expectation values by finite difference method [1]. - - **Reference:** - [1] `Finite difference method `_ - """ - - def __init__( - self, - estimator: BaseEstimator, - epsilon: float, - options: Options | None = None, - *, - method: Literal["central", "forward", "backward"] = "central", - ): - r""" - Args: - estimator: The estimator used to compute the gradients. - epsilon: The offset size for the finite difference gradients. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - method: The computation method of the gradients. - - - ``central`` computes :math:`\frac{f(x+e)-f(x-e)}{2e}`, - - ``forward`` computes :math:`\frac{f(x+e) - f(x)}{e}`, - - ``backward`` computes :math:`\frac{f(x)-f(x-e)}{e}` - - where :math:`e` is epsilon. - - Raises: - ValueError: If ``epsilon`` is not positive. - TypeError: If ``method`` is invalid. - """ - if epsilon <= 0: - raise ValueError(f"epsilon ({epsilon}) should be positive.") - self._epsilon = epsilon - if method not in ("central", "forward", "backward"): - raise TypeError( - f"The argument method should be central, forward, or backward: {method} is given." - ) - self._method = method - super().__init__(estimator, options) - - def _run( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> EstimatorGradientResult: - """Compute the estimator gradients on the given circuits.""" - job_circuits, job_observables, job_param_values, metadata = [], [], [], [] - all_n = [] - - for circuit, observable, parameter_values_, parameters_ in zip( - circuits, observables, parameter_values, parameters - ): - # Indices of parameters to be differentiated - indices = [circuit.parameters.data.index(p) for p in parameters_] - metadata.append({"parameters": parameters_}) - - # Combine inputs into a single job to reduce overhead. - offset = np.identity(circuit.num_parameters)[indices, :] - if self._method == "central": - plus = parameter_values_ + self._epsilon * offset - minus = parameter_values_ - self._epsilon * offset - n = 2 * len(indices) - job_circuits.extend([circuit] * n) - job_observables.extend([observable] * n) - job_param_values.extend(plus.tolist() + minus.tolist()) - all_n.append(n) - elif self._method == "forward": - plus = parameter_values_ + self._epsilon * offset - n = len(indices) + 1 - job_circuits.extend([circuit] * n) - job_observables.extend([observable] * n) - job_param_values.extend([parameter_values_] + plus.tolist()) - all_n.append(n) - elif self._method == "backward": - minus = parameter_values_ - self._epsilon * offset - n = len(indices) + 1 - job_circuits.extend([circuit] * n) - job_observables.extend([observable] * n) - job_param_values.extend([parameter_values_] + minus.tolist()) - all_n.append(n) - - # Run the single job with all circuits. - job = self._estimator.run(job_circuits, job_observables, job_param_values, **options) - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Estimator job failed.") from exc - - # Compute the gradients - gradients = [] - partial_sum_n = 0 - for n in all_n: - if self._method == "central": - result = results.values[partial_sum_n : partial_sum_n + n] - gradient = (result[: n // 2] - result[n // 2 :]) / (2 * self._epsilon) - elif self._method == "forward": - result = results.values[partial_sum_n : partial_sum_n + n] - gradient = (result[1:] - result[0]) / self._epsilon - elif self._method == "backward": - result = results.values[partial_sum_n : partial_sum_n + n] - gradient = (result[0] - result[1:]) / self._epsilon - partial_sum_n += n - gradients.append(gradient) - - opt = self._get_local_options(options) - return EstimatorGradientResult(gradients=gradients, metadata=metadata, options=opt) diff --git a/qiskit/algorithms/gradients/finite_diff/finite_diff_sampler_gradient.py b/qiskit/algorithms/gradients/finite_diff/finite_diff_sampler_gradient.py deleted file mode 100644 index bc250286c828..000000000000 --- a/qiskit/algorithms/gradients/finite_diff/finite_diff_sampler_gradient.py +++ /dev/null @@ -1,161 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Gradient of Sampler with Finite difference method.""" - -from __future__ import annotations - -from collections import defaultdict -from typing import Literal, Sequence - -import numpy as np - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.primitives import BaseSampler -from qiskit.providers import Options - -from ..base.base_sampler_gradient import BaseSamplerGradient -from ..base.sampler_gradient_result import SamplerGradientResult - -from ...exceptions import AlgorithmError - - -class FiniteDiffSamplerGradient(BaseSamplerGradient): - """ - Compute the gradients of the sampling probability by finite difference method [1]. - - **Reference:** - [1] `Finite difference method `_ - """ - - def __init__( - self, - sampler: BaseSampler, - epsilon: float, - options: Options | None = None, - *, - method: Literal["central", "forward", "backward"] = "central", - ): - r""" - Args: - sampler: The sampler used to compute the gradients. - epsilon: The offset size for the finite difference gradients. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - method: The computation method of the gradients. - - - ``central`` computes :math:`\frac{f(x+e)-f(x-e)}{2e}`, - - ``forward`` computes :math:`\frac{f(x+e) - f(x)}{e}`, - - ``backward`` computes :math:`\frac{f(x)-f(x-e)}{e}` - - where :math:`e` is epsilon. - - Raises: - ValueError: If ``epsilon`` is not positive. - TypeError: If ``method`` is invalid. - """ - if epsilon <= 0: - raise ValueError(f"epsilon ({epsilon}) should be positive.") - self._epsilon = epsilon - if method not in ("central", "forward", "backward"): - raise TypeError( - f"The argument method should be central, forward, or backward: {method} is given." - ) - self._method = method - super().__init__(sampler, options) - - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> SamplerGradientResult: - """Compute the sampler gradients on the given circuits.""" - job_circuits, job_param_values, metadata = [], [], [] - all_n = [] - for circuit, parameter_values_, parameters_ in zip(circuits, parameter_values, parameters): - # Indices of parameters to be differentiated - indices = [circuit.parameters.data.index(p) for p in parameters_] - metadata.append({"parameters": parameters_}) - # Combine inputs into a single job to reduce overhead. - offset = np.identity(circuit.num_parameters)[indices, :] - if self._method == "central": - plus = parameter_values_ + self._epsilon * offset - minus = parameter_values_ - self._epsilon * offset - n = 2 * len(indices) - job_circuits.extend([circuit] * n) - job_param_values.extend(plus.tolist() + minus.tolist()) - all_n.append(n) - elif self._method == "forward": - plus = parameter_values_ + self._epsilon * offset - n = len(indices) + 1 - job_circuits.extend([circuit] * n) - job_param_values.extend([parameter_values_] + plus.tolist()) - all_n.append(n) - elif self._method == "backward": - minus = parameter_values_ - self._epsilon * offset - n = len(indices) + 1 - job_circuits.extend([circuit] * n) - job_param_values.extend([parameter_values_] + minus.tolist()) - all_n.append(n) - - # Run the single job with all circuits. - job = self._sampler.run(job_circuits, job_param_values, **options) - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Sampler job failed.") from exc - - # Compute the gradients. - gradients = [] - partial_sum_n = 0 - for n in all_n: - gradient = [] - if self._method == "central": - result = results.quasi_dists[partial_sum_n : partial_sum_n + n] - for dist_plus, dist_minus in zip(result[: n // 2], result[n // 2 :]): - grad_dist: dict[int, float] = defaultdict(float) - for key, value in dist_plus.items(): - grad_dist[key] += value / (2 * self._epsilon) - for key, value in dist_minus.items(): - grad_dist[key] -= value / (2 * self._epsilon) - gradient.append(dict(grad_dist)) - elif self._method == "forward": - result = results.quasi_dists[partial_sum_n : partial_sum_n + n] - dist_zero = result[0] - for dist_plus in result[1:]: - grad_dist = defaultdict(float) - for key, value in dist_plus.items(): - grad_dist[key] += value / self._epsilon - for key, value in dist_zero.items(): - grad_dist[key] -= value / self._epsilon - gradient.append(dict(grad_dist)) - - elif self._method == "backward": - result = results.quasi_dists[partial_sum_n : partial_sum_n + n] - dist_zero = result[0] - for dist_minus in result[1:]: - grad_dist = defaultdict(float) - for key, value in dist_zero.items(): - grad_dist[key] += value / self._epsilon - for key, value in dist_minus.items(): - grad_dist[key] -= value / self._epsilon - gradient.append(dict(grad_dist)) - - partial_sum_n += n - gradients.append(gradient) - - opt = self._get_local_options(options) - return SamplerGradientResult(gradients=gradients, metadata=metadata, options=opt) diff --git a/qiskit/algorithms/gradients/lin_comb/__init__.py b/qiskit/algorithms/gradients/lin_comb/__init__.py deleted file mode 100644 index 8f5fd2a37f84..000000000000 --- a/qiskit/algorithms/gradients/lin_comb/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/qiskit/algorithms/gradients/lin_comb/lin_comb_estimator_gradient.py b/qiskit/algorithms/gradients/lin_comb/lin_comb_estimator_gradient.py deleted file mode 100644 index 93deb48b5820..000000000000 --- a/qiskit/algorithms/gradients/lin_comb/lin_comb_estimator_gradient.py +++ /dev/null @@ -1,195 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Gradient of probabilities with linear combination of unitaries (LCU) -""" - -from __future__ import annotations - -from collections.abc import Sequence - -import numpy as np - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator -from qiskit.primitives.utils import init_observable, _circuit_key -from qiskit.providers import Options -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..base.base_estimator_gradient import BaseEstimatorGradient -from ..base.estimator_gradient_result import EstimatorGradientResult -from ..utils import DerivativeType, _make_lin_comb_gradient_circuit, _make_lin_comb_observables - -from ...exceptions import AlgorithmError - - -class LinCombEstimatorGradient(BaseEstimatorGradient): - """Compute the gradients of the expectation values. - This method employs a linear combination of unitaries [1]. - - **Reference:** - [1] Schuld et al., Evaluating analytic gradients on quantum hardware, 2018 - `arXiv:1811.11184 `_ - """ - - SUPPORTED_GATES = [ - "rx", - "ry", - "rz", - "rzx", - "rzz", - "ryy", - "rxx", - "cx", - "cy", - "cz", - "ccx", - "swap", - "iswap", - "h", - "t", - "s", - "sdg", - "x", - "y", - "z", - ] - - def __init__( - self, - estimator: BaseEstimator, - derivative_type: DerivativeType = DerivativeType.REAL, - options: Options | None = None, - ): - r""" - Args: - estimator: The estimator used to compute the gradients. - derivative_type: The type of derivative. Can be either ``DerivativeType.REAL`` - ``DerivativeType.IMAG``, or ``DerivativeType.COMPLEX``. Defaults to - ``DerivativeType.REAL``. - - - ``DerivativeType.REAL`` computes :math:`2 \mathrm{Re}[⟨ψ(ω)|O(θ)|dω ψ(ω)〉]`. - - ``DerivativeType.IMAG`` computes :math:`2 \mathrm{Im}[⟨ψ(ω)|O(θ)|dω ψ(ω)〉]`. - - ``DerivativeType.COMPLEX`` computes :math:`2 ⟨ψ(ω)|O(θ)|dω ψ(ω)〉`. - - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting. - """ - self._lin_comb_cache: dict[tuple, dict[Parameter, QuantumCircuit]] = {} - super().__init__(estimator, options, derivative_type=derivative_type) - - @BaseEstimatorGradient.derivative_type.setter - def derivative_type(self, derivative_type: DerivativeType) -> None: - """Set the derivative type.""" - self._derivative_type = derivative_type - - def _run( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> EstimatorGradientResult: - """Compute the estimator gradients on the given circuits.""" - g_circuits, g_parameter_values, g_parameters = self._preprocess( - circuits, parameter_values, parameters, self.SUPPORTED_GATES - ) - results = self._run_unique( - g_circuits, observables, g_parameter_values, g_parameters, **options - ) - return self._postprocess(results, circuits, parameter_values, parameters) - - def _run_unique( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> EstimatorGradientResult: - """Compute the estimator gradients on the given circuits.""" - job_circuits, job_observables, job_param_values, metadata = [], [], [], [] - all_n = [] - for circuit, observable, parameter_values_, parameters_ in zip( - circuits, observables, parameter_values, parameters - ): - # Prepare circuits for the gradient of the specified parameters. - meta = {"parameters": parameters_} - circuit_key = _circuit_key(circuit) - if circuit_key not in self._lin_comb_cache: - # Cache the circuits for the linear combination of unitaries. - # We only cache the circuits for the specified parameters in the future. - self._lin_comb_cache[circuit_key] = _make_lin_comb_gradient_circuit( - circuit, add_measurement=False - ) - lin_comb_circuits = self._lin_comb_cache[circuit_key] - gradient_circuits = [] - for param in parameters_: - gradient_circuits.append(lin_comb_circuits[param]) - n = len(gradient_circuits) - # Make the observable as :class:`~qiskit.quantum_info.SparsePauliOp` and - # add an ancillary operator to compute the gradient. - observable = init_observable(observable) - observable_1, observable_2 = _make_lin_comb_observables( - observable, self._derivative_type - ) - # If its derivative type is `DerivativeType.COMPLEX`, calculate the gradient - # of the real and imaginary parts separately. - meta["derivative_type"] = self.derivative_type - metadata.append(meta) - # Combine inputs into a single job to reduce overhead. - if self._derivative_type == DerivativeType.COMPLEX: - job_circuits.extend(gradient_circuits * 2) - job_observables.extend([observable_1] * n + [observable_2] * n) - job_param_values.extend([parameter_values_] * 2 * n) - all_n.append(2 * n) - else: - job_circuits.extend(gradient_circuits) - job_observables.extend([observable_1] * n) - job_param_values.extend([parameter_values_] * n) - all_n.append(n) - - # Run the single job with all circuits. - job = self._estimator.run( - job_circuits, - job_observables, - job_param_values, - **options, - ) - try: - results = job.result() - except AlgorithmError as exc: - raise AlgorithmError("Estimator job failed.") from exc - - # Compute the gradients. - gradients = [] - partial_sum_n = 0 - for n in all_n: - # this disable is needed as Pylint does not understand derivative_type is a property if - # it is only defined in the base class and the getter is in the child - # pylint: disable=comparison-with-callable - if self.derivative_type == DerivativeType.COMPLEX: - gradient = np.zeros(n // 2, dtype="complex") - gradient.real = results.values[partial_sum_n : partial_sum_n + n // 2] - gradient.imag = results.values[partial_sum_n + n // 2 : partial_sum_n + n] - - else: - gradient = np.real(results.values[partial_sum_n : partial_sum_n + n]) - partial_sum_n += n - gradients.append(gradient) - - opt = self._get_local_options(options) - return EstimatorGradientResult(gradients=gradients, metadata=metadata, options=opt) diff --git a/qiskit/algorithms/gradients/lin_comb/lin_comb_qgt.py b/qiskit/algorithms/gradients/lin_comb/lin_comb_qgt.py deleted file mode 100644 index 0a9e05a9344a..000000000000 --- a/qiskit/algorithms/gradients/lin_comb/lin_comb_qgt.py +++ /dev/null @@ -1,257 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -A class for the Linear Combination Quantum Gradient Tensor. -""" - -from __future__ import annotations - -from collections.abc import Sequence - -import numpy as np - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.primitives import BaseEstimator -from qiskit.primitives.utils import _circuit_key -from qiskit.providers import Options -from qiskit.quantum_info import SparsePauliOp - -from ..base.base_qgt import BaseQGT -from .lin_comb_estimator_gradient import LinCombEstimatorGradient -from ..base.qgt_result import QGTResult -from ..utils import DerivativeType, _make_lin_comb_qgt_circuit, _make_lin_comb_observables - -from ...exceptions import AlgorithmError - - -class LinCombQGT(BaseQGT): - """Computes the Quantum Geometric Tensor (QGT) given a pure, parameterized quantum state. - - This method employs a linear combination of unitaries [1]. - - **Reference:** - - [1]: Schuld et al., "Evaluating analytic gradients on quantum hardware" (2018). - `arXiv:1811.11184 `_ - """ - - SUPPORTED_GATES = [ - "rx", - "ry", - "rz", - "rzx", - "rzz", - "ryy", - "rxx", - "cx", - "cy", - "cz", - "ccx", - "swap", - "iswap", - "h", - "t", - "s", - "sdg", - "x", - "y", - "z", - ] - - def __init__( - self, - estimator: BaseEstimator, - phase_fix: bool = True, - derivative_type: DerivativeType = DerivativeType.COMPLEX, - options: Options | None = None, - ): - r""" - Args: - estimator: The estimator used to compute the QGT. - phase_fix: Whether to calculate the second term (phase fix) of the QGT, which is - :math:`\langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle`. - Default to ``True``. - derivative_type: The type of derivative. Can be either ``DerivativeType.REAL`` - ``DerivativeType.IMAG``, or ``DerivativeType.COMPLEX``. Defaults to - ``DerivativeType.REAL``. - - - ``DerivativeType.REAL`` computes - - .. math:: - - \mathrm{Re(QGT)}_{ij}= \mathrm{Re}[\langle \partial_i \psi | \partial_j \psi \rangle - - \langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle]. - - - ``DerivativeType.IMAG`` computes - - .. math:: - - \mathrm{Re(QGT)}_{ij}= \mathrm{Im}[\langle \partial_i \psi | \partial_j \psi \rangle - - \langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle]. - - - ``DerivativeType.COMPLEX`` computes - - .. math:: - - \mathrm{QGT}_{ij}= [\langle \partial_i \psi | \partial_j \psi \rangle - - \langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle]. - - options: Backend runtime options used for circuit execution. The order of priority is: - options in ``run`` method > QGT's default options > primitive's default - setting. Higher priority setting overrides lower priority setting. - """ - super().__init__(estimator, phase_fix, derivative_type, options=options) - self._gradient = LinCombEstimatorGradient( - estimator, derivative_type=DerivativeType.COMPLEX, options=options - ) - self._lin_comb_qgt_circuit_cache: dict[ - tuple, dict[tuple[Parameter, Parameter], QuantumCircuit] - ] = {} - - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> QGTResult: - """Compute the QGT on the given circuits.""" - g_circuits, g_parameter_values, g_parameters = self._preprocess( - circuits, parameter_values, parameters, self.SUPPORTED_GATES - ) - results = self._run_unique(g_circuits, g_parameter_values, g_parameters, **options) - return self._postprocess(results, circuits, parameter_values, parameters) - - def _run_unique( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> QGTResult: - """Compute the QGTs on the given circuits.""" - job_circuits, job_observables, job_param_values, metadata = [], [], [], [] - all_n, all_m, phase_fixes = [], [], [] - - for circuit, parameter_values_, parameters_ in zip(circuits, parameter_values, parameters): - # Prepare circuits for the gradient of the specified parameters. - parameters_ = [p for p in circuit.parameters if p in parameters_] - meta = {"parameters": parameters_} - metadata.append(meta) - - # Compute the first term in the QGT - circuit_key = _circuit_key(circuit) - if circuit_key not in self._lin_comb_qgt_circuit_cache: - # generate the all of the circuits for the first term in the QGT and cache them. - # Only the circuit related to specified parameters will be executed. - # In the future, we can generate the specified circuits on demand. - self._lin_comb_qgt_circuit_cache[circuit_key] = _make_lin_comb_qgt_circuit(circuit) - lin_comb_qgt_circuits = self._lin_comb_qgt_circuit_cache[circuit_key] - - qgt_circuits = [] - rows, cols = np.triu_indices(len(parameters_)) - for row, col in zip(rows, cols): - param_i = parameters_[row] - param_j = parameters_[col] - qgt_circuits.append(lin_comb_qgt_circuits[(param_i, param_j)]) - - observable = SparsePauliOp.from_list([("I" * circuit.num_qubits, 1)]) - observable_1, observable_2 = _make_lin_comb_observables( - observable, self._derivative_type - ) - - n = len(qgt_circuits) - if self._derivative_type == DerivativeType.COMPLEX: - job_circuits.extend(qgt_circuits * 2) - job_observables.extend([observable_1] * n + [observable_2] * n) - job_param_values.extend([parameter_values_] * 2 * n) - all_m.append(len(parameters_)) - all_n.append(2 * n) - else: - job_circuits.extend(qgt_circuits) - job_observables.extend([observable_1] * n) - job_param_values.extend([parameter_values_] * n) - all_m.append(len(parameters_)) - all_n.append(n) - - # Run the single job with all circuits. - job = self._estimator.run( - job_circuits, - job_observables, - job_param_values, - **options, - ) - - if self._phase_fix: - # Compute the second term in the QGT if phase fix is enabled. - phase_fix_obs = [ - SparsePauliOp.from_list([("I" * circuit.num_qubits, 1)]) for circuit in circuits - ] - phase_fix_job = self._gradient.run( - circuits=circuits, - observables=phase_fix_obs, - parameter_values=parameter_values, - parameters=parameters, - **options, - ) - - try: - results = job.result() - if self._phase_fix: - gradient_results = phase_fix_job.result() - except AlgorithmError as exc: - raise AlgorithmError("Estimator job or gradient job failed.") from exc - - # Compute the phase fix - if self._phase_fix: - for gradient in gradient_results.gradients: - phase_fix = np.outer(np.conjugate(gradient), gradient) - # Select the real or imaginary part of the phase fix if needed - if self.derivative_type == DerivativeType.REAL: - phase_fix = np.real(phase_fix) - elif self.derivative_type == DerivativeType.IMAG: - phase_fix = np.imag(phase_fix) - phase_fixes.append(phase_fix) - else: - phase_fixes = [0 for i in range(len(circuits))] - # Compute the QGT - qgts = [] - partial_sum_n = 0 - for i, (n, m) in enumerate(zip(all_n, all_m)): - qgt = np.zeros((m, m), dtype="complex") - # Compute the first term in the QGT - if self.derivative_type == DerivativeType.COMPLEX: - qgt[np.triu_indices(m)] = results.values[partial_sum_n : partial_sum_n + n // 2] - qgt[np.triu_indices(m)] += ( - 1j * results.values[partial_sum_n + n // 2 : partial_sum_n + n] - ) - elif self.derivative_type == DerivativeType.REAL: - qgt[np.triu_indices(m)] = results.values[partial_sum_n : partial_sum_n + n] - elif self.derivative_type == DerivativeType.IMAG: - qgt[np.triu_indices(m)] = 1j * results.values[partial_sum_n : partial_sum_n + n] - - # Add the conjugate of the upper triangle to the lower triangle - qgt += np.triu(qgt, k=1).conjugate().T - if self.derivative_type == DerivativeType.REAL: - qgt = np.real(qgt) - elif self.derivative_type == DerivativeType.IMAG: - qgt = np.imag(qgt) - - # Subtract the phase fix from the QGT - qgt = qgt - phase_fixes[i] - partial_sum_n += n - qgts.append(qgt / 4) - - opt = self._get_local_options(options) - return QGTResult( - qgts=qgts, derivative_type=self.derivative_type, metadata=metadata, options=opt - ) diff --git a/qiskit/algorithms/gradients/lin_comb/lin_comb_sampler_gradient.py b/qiskit/algorithms/gradients/lin_comb/lin_comb_sampler_gradient.py deleted file mode 100644 index 759e77d460bb..000000000000 --- a/qiskit/algorithms/gradients/lin_comb/lin_comb_sampler_gradient.py +++ /dev/null @@ -1,147 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Gradient of probabilities with linear combination of unitaries (LCU) -""" - -from __future__ import annotations - -from collections import defaultdict -from collections.abc import Sequence - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.primitives import BaseSampler -from qiskit.primitives.utils import _circuit_key -from qiskit.providers import Options - -from ..base.base_sampler_gradient import BaseSamplerGradient -from ..base.sampler_gradient_result import SamplerGradientResult -from ..utils import _make_lin_comb_gradient_circuit - -from ...exceptions import AlgorithmError - - -class LinCombSamplerGradient(BaseSamplerGradient): - """Compute the gradients of the sampling probability. - This method employs a linear combination of unitaries [1]. - - **Reference:** - [1] Schuld et al., Evaluating analytic gradients on quantum hardware, 2018 - `arXiv:1811.11184 `_ - """ - - SUPPORTED_GATES = [ - "rx", - "ry", - "rz", - "rzx", - "rzz", - "ryy", - "rxx", - "cx", - "cy", - "cz", - "ccx", - "swap", - "iswap", - "h", - "t", - "s", - "sdg", - "x", - "y", - "z", - ] - - def __init__(self, sampler: BaseSampler, options: Options | None = None): - """ - Args: - sampler: The sampler used to compute the gradients. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - """ - self._lin_comb_cache: dict[tuple, dict[Parameter, QuantumCircuit]] = {} - super().__init__(sampler, options) - - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> SamplerGradientResult: - """Compute the estimator gradients on the given circuits.""" - g_circuits, g_parameter_values, g_parameters = self._preprocess( - circuits, parameter_values, parameters, self.SUPPORTED_GATES - ) - results = self._run_unique(g_circuits, g_parameter_values, g_parameters, **options) - return self._postprocess(results, circuits, parameter_values, parameters) - - def _run_unique( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> SamplerGradientResult: - """Compute the sampler gradients on the given circuits.""" - job_circuits, job_param_values, metadata = [], [], [] - all_n = [] - for circuit, parameter_values_, parameters_ in zip(circuits, parameter_values, parameters): - # Prepare circuits for the gradient of the specified parameters. - metadata.append({"parameters": parameters_}) - circuit_key = _circuit_key(circuit) - if circuit_key not in self._lin_comb_cache: - # Cache the circuits for the linear combination of unitaries. - # We only cache the circuits for the specified parameters in the future. - self._lin_comb_cache[circuit_key] = _make_lin_comb_gradient_circuit( - circuit, add_measurement=True - ) - lin_comb_circuits = self._lin_comb_cache[circuit_key] - gradient_circuits = [] - for param in parameters_: - gradient_circuits.append(lin_comb_circuits[param]) - # Combine inputs into a single job to reduce overhead. - n = len(gradient_circuits) - job_circuits.extend(gradient_circuits) - job_param_values.extend([parameter_values_] * n) - all_n.append(n) - - # Run the single job with all circuits. - job = self._sampler.run(job_circuits, job_param_values, **options) - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Sampler job failed.") from exc - - # Compute the gradients. - gradients = [] - partial_sum_n = 0 - for i, n in enumerate(all_n): - gradient = [] - result = results.quasi_dists[partial_sum_n : partial_sum_n + n] - m = 2 ** circuits[i].num_qubits - for dist in result: - grad_dist: dict[int, float] = defaultdict(float) - for key, value in dist.items(): - if key < m: - grad_dist[key] += value - else: - grad_dist[key - m] -= value - gradient.append(dict(grad_dist)) - gradients.append(gradient) - partial_sum_n += n - - opt = self._get_local_options(options) - return SamplerGradientResult(gradients=gradients, metadata=metadata, options=opt) diff --git a/qiskit/algorithms/gradients/param_shift/__init__.py b/qiskit/algorithms/gradients/param_shift/__init__.py deleted file mode 100644 index 8f5fd2a37f84..000000000000 --- a/qiskit/algorithms/gradients/param_shift/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/qiskit/algorithms/gradients/param_shift/param_shift_estimator_gradient.py b/qiskit/algorithms/gradients/param_shift/param_shift_estimator_gradient.py deleted file mode 100644 index ef334a291e6e..000000000000 --- a/qiskit/algorithms/gradients/param_shift/param_shift_estimator_gradient.py +++ /dev/null @@ -1,123 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Gradient of probabilities with parameter shift -""" - -from __future__ import annotations - -from collections.abc import Sequence - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..base.base_estimator_gradient import BaseEstimatorGradient -from ..base.estimator_gradient_result import EstimatorGradientResult -from ..utils import _make_param_shift_parameter_values - -from ...exceptions import AlgorithmError - - -class ParamShiftEstimatorGradient(BaseEstimatorGradient): - """ - Compute the gradients of the expectation values by the parameter shift rule [1]. - - **Reference:** - [1] Schuld, M., Bergholm, V., Gogolin, C., Izaac, J., and Killoran, N. Evaluating analytic - gradients on quantum hardware, `DOI `_ - """ - - SUPPORTED_GATES = [ - "x", - "y", - "z", - "h", - "rx", - "ry", - "rz", - "p", - "cx", - "cy", - "cz", - "ryy", - "rxx", - "rzz", - "rzx", - ] - - def _run( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> EstimatorGradientResult: - """Compute the gradients of the expectation values by the parameter shift rule.""" - g_circuits, g_parameter_values, g_parameters = self._preprocess( - circuits, parameter_values, parameters, self.SUPPORTED_GATES - ) - results = self._run_unique( - g_circuits, observables, g_parameter_values, g_parameters, **options - ) - return self._postprocess(results, circuits, parameter_values, parameters) - - def _run_unique( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> EstimatorGradientResult: - """Compute the estimator gradients on the given circuits.""" - job_circuits, job_observables, job_param_values, metadata = [], [], [], [] - all_n = [] - for circuit, observable, parameter_values_, parameters_ in zip( - circuits, observables, parameter_values, parameters - ): - metadata.append({"parameters": parameters_}) - # Make parameter values for the parameter shift rule. - param_shift_parameter_values = _make_param_shift_parameter_values( - circuit, parameter_values_, parameters_ - ) - # Combine inputs into a single job to reduce overhead. - n = len(param_shift_parameter_values) - job_circuits.extend([circuit] * n) - job_observables.extend([observable] * n) - job_param_values.extend(param_shift_parameter_values) - all_n.append(n) - - # Run the single job with all circuits. - job = self._estimator.run( - job_circuits, - job_observables, - job_param_values, - **options, - ) - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Estimator job failed.") from exc - - # Compute the gradients. - gradients = [] - partial_sum_n = 0 - for n in all_n: - result = results.values[partial_sum_n : partial_sum_n + n] - gradient_ = (result[: n // 2] - result[n // 2 :]) / 2 - gradients.append(gradient_) - partial_sum_n += n - - opt = self._get_local_options(options) - return EstimatorGradientResult(gradients=gradients, metadata=metadata, options=opt) diff --git a/qiskit/algorithms/gradients/param_shift/param_shift_sampler_gradient.py b/qiskit/algorithms/gradients/param_shift/param_shift_sampler_gradient.py deleted file mode 100644 index 642f4b002cd9..000000000000 --- a/qiskit/algorithms/gradients/param_shift/param_shift_sampler_gradient.py +++ /dev/null @@ -1,117 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Gradient of probabilities with parameter shift -""" - -from __future__ import annotations - -from collections import defaultdict -from collections.abc import Sequence - -from qiskit.circuit import Parameter, QuantumCircuit - -from ..base.base_sampler_gradient import BaseSamplerGradient -from ..base.sampler_gradient_result import SamplerGradientResult -from ..utils import _make_param_shift_parameter_values - -from ...exceptions import AlgorithmError - - -class ParamShiftSamplerGradient(BaseSamplerGradient): - """ - Compute the gradients of the sampling probability by the parameter shift rule [1]. - - **Reference:** - [1] Schuld, M., Bergholm, V., Gogolin, C., Izaac, J., and Killoran, N. Evaluating analytic - gradients on quantum hardware, `DOI `_ - """ - - SUPPORTED_GATES = [ - "x", - "y", - "z", - "h", - "rx", - "ry", - "rz", - "p", - "cx", - "cy", - "cz", - "ryy", - "rxx", - "rzz", - "rzx", - ] - - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> SamplerGradientResult: - """Compute the estimator gradients on the given circuits.""" - g_circuits, g_parameter_values, g_parameters = self._preprocess( - circuits, parameter_values, parameters, self.SUPPORTED_GATES - ) - results = self._run_unique(g_circuits, g_parameter_values, g_parameters, **options) - return self._postprocess(results, circuits, parameter_values, parameters) - - def _run_unique( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> SamplerGradientResult: - """Compute the sampler gradients on the given circuits.""" - job_circuits, job_param_values, metadata = [], [], [] - all_n = [] - for circuit, parameter_values_, parameters_ in zip(circuits, parameter_values, parameters): - metadata.append({"parameters": parameters_}) - # Make parameter values for the parameter shift rule. - param_shift_parameter_values = _make_param_shift_parameter_values( - circuit, parameter_values_, parameters_ - ) - # Combine inputs into a single job to reduce overhead. - n = len(param_shift_parameter_values) - job_circuits.extend([circuit] * n) - job_param_values.extend(param_shift_parameter_values) - all_n.append(n) - - # Run the single job with all circuits. - job = self._sampler.run(job_circuits, job_param_values, **options) - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Estimator job failed.") from exc - - # Compute the gradients. - gradients = [] - partial_sum_n = 0 - for n in all_n: - gradient = [] - result = results.quasi_dists[partial_sum_n : partial_sum_n + n] - for dist_plus, dist_minus in zip(result[: n // 2], result[n // 2 :]): - grad_dist: dict[int, float] = defaultdict(float) - for key, val in dist_plus.items(): - grad_dist[key] += val / 2 - for key, val in dist_minus.items(): - grad_dist[key] -= val / 2 - gradient.append(dict(grad_dist)) - gradients.append(gradient) - partial_sum_n += n - - opt = self._get_local_options(options) - return SamplerGradientResult(gradients=gradients, metadata=metadata, options=opt) diff --git a/qiskit/algorithms/gradients/qfi.py b/qiskit/algorithms/gradients/qfi.py deleted file mode 100644 index 94aa86fde56a..000000000000 --- a/qiskit/algorithms/gradients/qfi.py +++ /dev/null @@ -1,171 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -A class for the Quantum Fisher Information. -""" - -from __future__ import annotations - -from abc import ABC -from collections.abc import Sequence -from copy import copy - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.providers import Options - -from .base.base_qgt import BaseQGT -from .lin_comb.lin_comb_estimator_gradient import DerivativeType -from .qfi_result import QFIResult - -from ..algorithm_job import AlgorithmJob -from ..exceptions import AlgorithmError - - -class QFI(ABC): - r"""Computes the Quantum Fisher Information (QFI) given a pure, - parameterized quantum state. QFI is defined as: - - .. math:: - - \mathrm{QFI}_{ij}= 4 \mathrm{Re}[\langle \partial_i \psi | \partial_j \psi \rangle - - \langle\partial_i \psi | \psi \rangle \langle\psi | \partial_j \psi \rangle]. - """ - - def __init__( - self, - qgt: BaseQGT, - options: Options | None = None, - ): - r""" - Args: - qgt: The quantum geometric tensor used to compute the QFI. - options: Backend runtime options used for circuit execution. The order of priority is: - options in ``run`` method > QFI's default options > primitive's default - setting. Higher priority setting overrides lower priority setting. - """ - self._qgt: BaseQGT = qgt - self._default_options = Options() - if options is not None: - self._default_options.update_options(**options) - - def run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter] | None] | None = None, - **options, - ) -> AlgorithmJob: - """Run the job of the QFIs on the given circuits. - - Args: - circuits: The list of quantum circuits to compute the QFIs. - parameter_values: The list of parameter values to be bound to the circuit. - parameters: The sequence of parameters to calculate only the QFIs of - the specified parameters. Each sequence of parameters corresponds to a circuit in - ``circuits``. Defaults to None, which means that the QFIs of all parameters in - each circuit are calculated. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > QFI's - default options > QGT's default setting. - Higher priority setting overrides lower priority setting. - - Returns: - The job object of the QFIs of the expectation values. The i-th result corresponds to - ``circuits[i]`` evaluated with parameters bound as ``parameter_values[i]``. - """ - - if isinstance(circuits, QuantumCircuit): - # Allow a single circuit to be passed in. - circuits = (circuits,) - - if parameters is None: - # If parameters is None, we calculate the gradients of all parameters in each circuit. - parameters = [circuit.parameters for circuit in circuits] - else: - # If parameters is not None, we calculate the gradients of the specified parameters. - # None in parameters means that the gradients of all parameters in the corresponding - # circuit are calculated. - parameters = [ - params if params is not None else circuits[i].parameters - for i, params in enumerate(parameters) - ] - # The priority of run option is as follows: - # options in ``run`` method > QFI's default options > QGT's default setting. - opts = copy(self._default_options) - opts.update_options(**options) - job = AlgorithmJob(self._run, circuits, parameter_values, parameters, **opts.__dict__) - job.submit() - return job - - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> QFIResult: - """Compute the QFI on the given circuits.""" - # Set the derivative type to real - temp_derivative_type, self._qgt.derivative_type = ( - self._qgt.derivative_type, - DerivativeType.REAL, - ) - job = self._qgt.run(circuits, parameter_values, parameters, **options) - - try: - result = job.result() - except AlgorithmError as exc: - raise AlgorithmError("Estimator job or gradient job failed.") from exc - - self._qgt.derivative_type = temp_derivative_type - - return QFIResult( - qfis=[4 * qgt.real for qgt in result.qgts], - metadata=result.metadata, - options=result.options, - ) - - @property - def options(self) -> Options: - """Return the union of QGT's options setting and QFI's default options, - where, if the same field is set in both, the QFI's default options override - the QGT's default setting. - - Returns: - The QFI default + QGT options. - """ - return self._get_local_options(self._default_options.__dict__) - - def update_default_options(self, **options): - """Update the gradient's default options setting. - - Args: - **options: The fields to update the default options. - """ - - self._default_options.update_options(**options) - - def _get_local_options(self, options: Options) -> Options: - """Return the union of the QFI default setting, - the QGT default options, and the options in the ``run`` method. - The order of priority is: options in ``run`` method > QFI's default options > QGT's - default setting. - - Args: - options: The fields to update the options - - Returns: - The QFI default + QGT default + run options. - """ - opts = copy(self._qgt.options) - opts.update_options(**options) - return opts diff --git a/qiskit/algorithms/gradients/qfi_result.py b/qiskit/algorithms/gradients/qfi_result.py deleted file mode 100644 index 47a04021584d..000000000000 --- a/qiskit/algorithms/gradients/qfi_result.py +++ /dev/null @@ -1,35 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -QFI result class -""" - -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any - -import numpy as np - -from qiskit.providers import Options - - -@dataclass(frozen=True) -class QFIResult: - """Result of QFI.""" - - qfis: list[np.ndarray] - """The QFI.""" - metadata: list[dict[str, Any]] - """Additional information about the job.""" - options: Options - """Primitive runtime options for the execution of the job.""" diff --git a/qiskit/algorithms/gradients/reverse/__init__.py b/qiskit/algorithms/gradients/reverse/__init__.py deleted file mode 100644 index fdb172d367f0..000000000000 --- a/qiskit/algorithms/gradients/reverse/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/qiskit/algorithms/gradients/reverse/bind.py b/qiskit/algorithms/gradients/reverse/bind.py deleted file mode 100644 index 7660f7c836d0..000000000000 --- a/qiskit/algorithms/gradients/reverse/bind.py +++ /dev/null @@ -1,53 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Bind values to a parametrized circuit, accepting binds for non-existing parameters in the circuit.""" - -from __future__ import annotations -from collections.abc import Iterable - -from qiskit.circuit import QuantumCircuit, Parameter - -# pylint: disable=inconsistent-return-statements -def bind( - circuits: QuantumCircuit | Iterable[QuantumCircuit], - parameter_binds: dict[Parameter, float], - inplace: bool = False, -) -> QuantumCircuit | Iterable[QuantumCircuit] | None: - """Bind parameters in a circuit (or list of circuits). - - This method also allows passing parameter binds to parameters that are not in the circuit, - and thereby differs to :meth:`.QuantumCircuit.assign_parameters`. - - Args: - circuits: Input circuit(s). - parameter_binds: A dictionary with ``{Parameter: float}`` pairs determining the values to - which the free parameters in the circuit(s) are bound. - inplace: If ``True``, bind the values in place, otherwise return circuit copies. - - Returns: - The bound circuits, if ``inplace=False``, otherwise None. - - """ - if not isinstance(circuits, Iterable): - circuits = [circuits] - return_list = False - else: - return_list = True - - bound = [] - for circuit in circuits: - existing_parameter_binds = {p: parameter_binds[p] for p in circuit.parameters} - bound.append(circuit.assign_parameters(existing_parameter_binds, inplace=inplace)) - - if not inplace: - return bound if return_list else bound[0] diff --git a/qiskit/algorithms/gradients/reverse/derive_circuit.py b/qiskit/algorithms/gradients/reverse/derive_circuit.py deleted file mode 100644 index 96a2bead60cd..000000000000 --- a/qiskit/algorithms/gradients/reverse/derive_circuit.py +++ /dev/null @@ -1,157 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Split a circuit into subcircuits, each containing a single parameterized gate.""" - -from __future__ import annotations -import itertools -from collections.abc import Sequence - -from qiskit.circuit import QuantumCircuit, Parameter, Gate -from qiskit.circuit.library import RXGate, RYGate, RZGate, CRXGate, CRYGate, CRZGate - - -def gradient_lookup(gate: Gate) -> list[tuple[complex, QuantumCircuit]]: - """Returns a circuit implementing the gradient of the input gate. - - Args: - gate: The gate whose derivative is returned. - - Returns: - The derivative of the input gate as list of ``(coeff, circuit)`` pairs, - where the sum of all ``coeff * circuit`` elements describes the full derivative. - The circuit is the unitary part of the derivative with a potential separate ``coeff``. - The output is a list as derivatives of e.g. controlled gates can only be described - as a sum of ``coeff * circuit`` pairs. - - Raises: - NotImplementedError: If the derivative of ``gate`` is not implemented. - """ - - param = gate.params[0] - if isinstance(gate, RXGate): - derivative = QuantumCircuit(gate.num_qubits) - derivative.rx(param, 0) - derivative.x(0) - return [(-0.5j, derivative)] - if isinstance(gate, RYGate): - derivative = QuantumCircuit(gate.num_qubits) - derivative.ry(param, 0) - derivative.y(0) - return [(-0.5j, derivative)] - if isinstance(gate, RZGate): - derivative = QuantumCircuit(gate.num_qubits) - derivative.rz(param, 0) - derivative.z(0) - return [(-0.5j, derivative)] - if isinstance(gate, CRXGate): - proj1 = QuantumCircuit(gate.num_qubits) - proj1.rx(param, 1) - proj1.x(1) - - proj2 = QuantumCircuit(gate.num_qubits) - proj2.z(0) - proj2.rx(param, 1) - proj2.x(1) - - return [(-0.25j, proj1), (0.25j, proj2)] - if isinstance(gate, CRYGate): - proj1 = QuantumCircuit(gate.num_qubits) - proj1.ry(param, 1) - proj1.y(1) - - proj2 = QuantumCircuit(gate.num_qubits) - proj2.z(0) - proj2.ry(param, 1) - proj2.y(1) - - return [(-0.25j, proj1), (0.25j, proj2)] - if isinstance(gate, CRZGate): - proj1 = QuantumCircuit(gate.num_qubits) - proj1.rz(param, 1) - proj1.z(1) - - proj2 = QuantumCircuit(gate.num_qubits) - proj2.z(0) - proj2.rz(param, 1) - proj2.z(1) - - return [(-0.25j, proj1), (0.25j, proj2)] - raise NotImplementedError("Cannot implement gradient for", gate) - - -def derive_circuit( - circuit: QuantumCircuit, parameter: Parameter -) -> Sequence[tuple[complex, QuantumCircuit]]: - """Return the analytic gradient expression of the input circuit wrt. a single parameter. - - Returns a list of ``(coeff, gradient_circuit)`` tuples, where the derivative of the circuit is - given by the sum of the gradient circuits multiplied by their coefficient. - - For example, the circuit:: - - ┌───┐┌───────┐┌─────┐ - q: ┤ H ├┤ Rx(x) ├┤ Sdg ├ - └───┘└───────┘└─────┘ - - returns the coefficient `-0.5j` and the circuit equivalent to:: - - ┌───┐┌───────┐┌───┐┌─────┐ - q: ┤ H ├┤ Rx(x) ├┤ X ├┤ Sdg ├ - └───┘└───────┘└───┘└─────┘ - - as the derivative of `Rx(x)` is `-0.5j Rx(x) X`. - - Args: - circuit: The quantum circuit to derive. - parameter: The parameter with respect to which we derive. - - Returns: - A list of ``(coeff, gradient_circuit)`` tuples. - - Raises: - ValueError: If ``parameter`` is of the wrong type. - ValueError: If ``parameter`` is not in this circuit. - NotImplementedError: If a non-unique parameter is added, as the product rule is not yet - supported in this function. - """ - # this is added as useful user-warning, since sometimes ``ParameterExpression``s are - # passed around instead of ``Parameter``s - if not isinstance(parameter, Parameter): - raise ValueError(f"parameter must be of type Parameter, not {type(parameter)}.") - - if parameter not in circuit.parameters: - raise ValueError(f"The parameter {parameter} is not in this circuit.") - - if len(circuit._parameter_table[parameter]) > 1: - raise NotImplementedError("No product rule support yet, circuit parameters must be unique.") - - summands, op_context = [], [] - for i, op in enumerate(circuit.data): - gate = op.operation - op_context.append((op.qubits, op.clbits)) - if parameter in gate.params: - coeffs_and_grads = gradient_lookup(gate) - summands += [coeffs_and_grads] - else: - summands += [[(1, gate)]] - - gradient = [] - for product_rule_term in itertools.product(*summands): - summand_circuit = QuantumCircuit(*circuit.qregs) - c = 1 - for i, term in enumerate(product_rule_term): - c *= term[0] - summand_circuit.data.append([term[1], *op_context[i]]) - gradient += [(c, summand_circuit.copy())] - - return gradient diff --git a/qiskit/algorithms/gradients/reverse/reverse_gradient.py b/qiskit/algorithms/gradients/reverse/reverse_gradient.py deleted file mode 100644 index c3bf8005d377..000000000000 --- a/qiskit/algorithms/gradients/reverse/reverse_gradient.py +++ /dev/null @@ -1,200 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Estimator gradients with the classically efficient reverse mode.""" - -from __future__ import annotations -from collections.abc import Sequence -import logging - -import numpy as np - -from qiskit.circuit import QuantumCircuit, Parameter -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.quantum_info import Statevector -from qiskit.opflow import PauliSumOp -from qiskit.primitives import Estimator - -from .bind import bind -from .derive_circuit import derive_circuit -from .split_circuits import split - -from ..base.base_estimator_gradient import BaseEstimatorGradient -from ..base.estimator_gradient_result import EstimatorGradientResult -from ..utils import DerivativeType - -logger = logging.getLogger(__name__) - - -class ReverseEstimatorGradient(BaseEstimatorGradient): - """Estimator gradients with the classically efficient reverse mode. - - .. note:: - - This gradient implementation is based on statevector manipulations and scales - exponentially with the number of qubits. However, for small system sizes it can be very fast - compared to circuit-based gradients. - - This class implements the calculation of the expectation gradient as described in - [1]. By keeping track of two statevectors and iteratively sweeping through each parameterized - gate, this method scales only linearly with the number of parameters. - - **References:** - - [1]: Jones, T. and Gacon, J. "Efficient calculation of gradients in classical simulations - of variational quantum algorithms" (2020). - `arXiv:2009.02823 `_. - - """ - - SUPPORTED_GATES = ["rx", "ry", "rz", "cp", "crx", "cry", "crz"] - - def __init__(self, derivative_type: DerivativeType = DerivativeType.REAL): - """ - Args: - derivative_type: Defines whether the real, imaginary or real plus imaginary part - of the gradient is returned. - """ - dummy_estimator = Estimator() # this is required by the base class, but not used - super().__init__(dummy_estimator, derivative_type=derivative_type) - - @BaseEstimatorGradient.derivative_type.setter - def derivative_type(self, derivative_type: DerivativeType) -> None: - """Set the derivative type.""" - self._derivative_type = derivative_type - - def _run( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> EstimatorGradientResult: - """Compute the gradients of the expectation values by the parameter shift rule.""" - g_circuits, g_parameter_values, g_parameters = self._preprocess( - circuits, parameter_values, parameters, self.SUPPORTED_GATES - ) - results = self._run_unique( - g_circuits, observables, g_parameter_values, g_parameters, **options - ) - return self._postprocess(results, circuits, parameter_values, parameters) - - def _run_unique( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, # pylint: disable=unused-argument - ) -> EstimatorGradientResult: - num_gradients = len(circuits) - gradients = [] - metadata = [] - - for i in range(num_gradients): - # temporary variables for easier access - circuit = circuits[i] - parameters_ = parameters[i] - observable = observables[i] - values = parameter_values[i] - - # the metadata only contains the parameters as there are no run configs here - metadata.append( - { - "parameters": parameters_, - "derivative_type": self.derivative_type, - } - ) - - # keep track of the parameter order of the circuit, as the circuit splitting might - # produce a list of unitaries in a different order - # original_parameter_order = [p for p in circuit.parameters if p in parameters_] - - # split the circuit and generate lists of unitaries [U_1, U_2, ...] and - # parameters [p_1, p_2, ...] in these unitaries - unitaries, paramlist = split(circuit, parameters=parameters_) - - parameter_binds = dict(zip(circuit.parameters, values)) - bound_circuit = bind(circuit, parameter_binds) - - # initialize state variables -- we use the same naming as in the paper - phi = Statevector(bound_circuit) - lam = _evolve_by_operator(observable, phi) - - # store gradients in a dictionary to return them in the correct order - grads = {param: 0j for param in parameters_} - - num_parameters = len(unitaries) - for j in reversed(range(num_parameters)): - unitary_j = unitaries[j] - - # We currently only support gates with a single parameter -- which is reflected - # in self.SUPPORTED_GATES -- but generally we could also support gates with multiple - # parameters per gate - parameter_j = paramlist[j][0] - - # get the analytic gradient d U_j / d p_j and bind the gate - deriv = derive_circuit(unitary_j, parameter_j) - for _, gate in deriv: - bind(gate, parameter_binds, inplace=True) - - # iterate the state variable - unitary_j_dagger = bind(unitary_j, parameter_binds).inverse() - phi = phi.evolve(unitary_j_dagger) - - # compute current gradient - grad = sum( - coeff * lam.conjugate().data.dot(phi.evolve(gate).data) for coeff, gate in deriv - ) - - # Compute the full gradient (real and complex parts) as all information is available. - # Later, based on the derivative type, cast to real/imag/complex. - grads[parameter_j] += grad - - if j > 0: - lam = lam.evolve(unitary_j_dagger) - - gradient = np.array(list(grads.values())) - gradients.append(self._to_derivtype(gradient)) - - result = EstimatorGradientResult(gradients, metadata=metadata, options={}) - return result - - def _to_derivtype(self, gradient): - # this disable is needed as Pylint does not understand derivative_type is a property if - # it is only defined in the base class and the getter is in the child - # pylint: disable=comparison-with-callable - if self.derivative_type == DerivativeType.REAL: - return 2 * np.real(gradient) - if self.derivative_type == DerivativeType.IMAG: - return 2 * np.imag(gradient) - - return 2 * gradient - - -def _evolve_by_operator(operator, state): - """Evolve the Statevector state by operator.""" - - # try casting to sparse matrix and use sparse matrix-vector multiplication, which is - # a lot faster than using Statevector.evolve - if isinstance(operator, PauliSumOp): - operator = operator.primitive * operator.coeff - - try: - spmatrix = operator.to_matrix(sparse=True) - evolved = spmatrix @ state.data - return Statevector(evolved) - except (TypeError, AttributeError): - logger.info("Operator is not castable to a sparse matrix, using Statevector.evolve.") - - return state.evolve(operator) diff --git a/qiskit/algorithms/gradients/reverse/reverse_qgt.py b/qiskit/algorithms/gradients/reverse/reverse_qgt.py deleted file mode 100644 index 0b845ee96961..000000000000 --- a/qiskit/algorithms/gradients/reverse/reverse_qgt.py +++ /dev/null @@ -1,252 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""QGT with the classically efficient reverse mode.""" - -from __future__ import annotations -from collections.abc import Sequence -import logging - -import numpy as np - -from qiskit.circuit import QuantumCircuit, Parameter -from qiskit.quantum_info import Statevector -from qiskit.providers import Options -from qiskit.primitives import Estimator - -from ..base.base_qgt import BaseQGT -from ..base.qgt_result import QGTResult -from ..utils import DerivativeType - -from .split_circuits import split -from .bind import bind -from .derive_circuit import derive_circuit - -logger = logging.getLogger(__name__) - - -class ReverseQGT(BaseQGT): - """QGT calculation with the classically efficient reverse mode. - - .. note:: - - This QGT implementation is based on statevector manipulations and scales exponentially - with the number of qubits. However, for small system sizes it can be very fast - compared to circuit-based gradients. - - This class implements the calculation of the QGT as described in [1]. - By keeping track of three statevectors and iteratively sweeping through each parameterized - gate, this method scales only quadratically with the number of parameters. - - **References:** - - [1]: Jones, T. "Efficient classical calculation of the Quantum Natural Gradient" (2020). - `arXiv:2011.02991 `_. - - """ - - SUPPORTED_GATES = ["rx", "ry", "rz", "cp", "crx", "cry", "crz"] - - def __init__( - self, phase_fix: bool = True, derivative_type: DerivativeType = DerivativeType.COMPLEX - ): - """ - Args: - phase_fix: Whether or not to include the phase fix. - derivative_type: Determines whether the complex QGT or only the real or imaginary - parts are calculated. - """ - dummy_estimator = Estimator() # this method does not need an estimator - super().__init__(dummy_estimator, phase_fix, derivative_type) - - @property - def options(self) -> Options: - """There are no options for the reverse QGT, returns an empty options dict. - - Returns: - Empty options. - """ - return Options() - - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameter_sets: Sequence[set[Parameter]], - **options, - ) -> QGTResult: - """Compute the QGT on the given circuits.""" - g_circuits, g_parameter_values, g_parameter_sets = self._preprocess( - circuits, parameter_values, parameter_sets, self.SUPPORTED_GATES - ) - results = self._run_unique(g_circuits, g_parameter_values, g_parameter_sets, **options) - return self._postprocess(results, circuits, parameter_values, parameter_sets) - - def _run_unique( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameter_sets: Sequence[set[Parameter]], - **options, # pylint: disable=unused-argument - ) -> QGTResult: - num_qgts = len(circuits) - qgts = [] - metadata = [] - - for k in range(num_qgts): - values = np.asarray(parameter_values[k]) - circuit = circuits[k] - parameters = list(parameter_sets[k]) - - num_parameters = len(parameters) - original_parameter_order = [p for p in circuit.parameters if p in parameters] - metadata.append({"parameters": original_parameter_order}) - - unitaries, paramlist = split(circuit, parameters=parameters) - - # initialize the phase fix vector and the hessian part ``metric`` - num_parameters = len(unitaries) - phase_fixes = np.zeros(num_parameters, dtype=complex) - metric = np.zeros((num_parameters, num_parameters), dtype=complex) - - # initialize the state variables -- naming convention is the same as the paper - parameter_binds = dict(zip(circuit.parameters, values)) - bound_unitaries = bind(unitaries, parameter_binds) - - chi = Statevector(bound_unitaries[0]) - psi = chi.copy() - phi = Statevector.from_int(0, (2,) * circuit.num_qubits) - - # Get the analytic gradient of the first unitary - # Note: We currently only support gates with a single parameter -- which is reflected - # in self.SUPPORTED_GATES -- but generally we could also support gates with multiple - # parameters per gate. This is the reason for the second 0-index. - deriv = derive_circuit(unitaries[0], paramlist[0][0]) - for _, gate in deriv: - bind(gate, parameter_binds, inplace=True) - - grad_coeffs = [coeff for coeff, _ in deriv] - grad_states = [phi.evolve(gate) for _, gate in deriv] - - # compute phase fix (optional) and the hessian part - if self._phase_fix: - phase_fixes[0] = _phasefix_term(chi, grad_coeffs, grad_states) - - metric[0, 0] = _l_term(grad_coeffs, grad_states, grad_coeffs, grad_states) - - for j in range(1, num_parameters): - lam = psi.copy() - phi = psi.copy() - - # get the analytic gradient d U_j / d p_j and apply it - deriv = derive_circuit(unitaries[j], paramlist[j][0]) - - for _, gate in deriv: - bind(gate, parameter_binds, inplace=True) - - # compute |phi> (in general it's a sum of states and coeffs) - grad_coeffs = [coeff for coeff, _ in deriv] - grad_states = [phi.evolve(gate) for _, gate in deriv] - - # compute the digaonal element L_{j, j} - metric[j, j] += _l_term(grad_coeffs, grad_states, grad_coeffs, grad_states) - - # compute the off diagonal elements L_{i, j} - for i in reversed(range(j)): - # apply U_{i + 1}_dg - unitary_ip_inv = bound_unitaries[i + 1].inverse() - grad_states = [state.evolve(unitary_ip_inv) for state in grad_states] - - lam = lam.evolve(bound_unitaries[i].inverse()) - - # get the gradient d U_i / d p_i and apply it - deriv = derive_circuit(unitaries[i], paramlist[i][0]) - for _, gate in deriv: - bind(gate, parameter_binds, inplace=True) - - grad_coeffs_mu = [coeff for coeff, _ in deriv] - grad_states_mu = [lam.evolve(gate) for _, gate in deriv] - - metric[i, j] += _l_term( - grad_coeffs_mu, grad_states_mu, grad_coeffs, grad_states - ) - - if self._phase_fix: - phase_fixes[j] += _phasefix_term(chi, grad_coeffs, grad_states) - - psi = psi.evolve(bound_unitaries[j]) - - # The following code stacks the QGT together and maps the values into the - # correct original order of parameters - - # map circuit parameter to global index in the circuit - param_to_circuit = { - param: index for index, param in enumerate(original_parameter_order) - } - # map global index to the local index used in the calculation, the new index can - # now be accessed by remap[index] - remap = { - index: param_to_circuit[_extract_parameter(plist[0])] - for index, plist in enumerate(paramlist) - } - - qgt = np.zeros((num_parameters, num_parameters), dtype=complex) - for i in range(num_parameters): - iloc = remap[i] - for j in range(num_parameters): - jloc = remap[j] - if i <= j: - qgt[iloc, jloc] += metric[i, j] - else: - qgt[iloc, jloc] += np.conj(metric[j, i]) - - qgt[iloc, jloc] -= np.conj(phase_fixes[i]) * phase_fixes[j] - - # append and cast to real/imag if required - qgts.append(self._to_derivtype(qgt)) - - result = QGTResult(qgts, self.derivative_type, metadata, options=None) - return result - - def _to_derivtype(self, qgt): - if self.derivative_type == DerivativeType.REAL: - return np.real(qgt) - if self.derivative_type == DerivativeType.IMAG: - return np.imag(qgt) - - return qgt - - -def _l_term(coeffs_i, states_i, coeffs_j, states_j): - return sum( - sum( - np.conj(coeff_i) * coeff_j * np.conj(state_i.data).dot(state_j.data) - for coeff_i, state_i in zip(coeffs_i, states_i) - ) - for coeff_j, state_j in zip(coeffs_j, states_j) - ) - - -def _phasefix_term(chi, coeffs, states): - return sum( - coeff_i * np.conj(chi.data).dot(state_i.data) for coeff_i, state_i in zip(coeffs, states) - ) - - -def _extract_parameter(expression): - if isinstance(expression, Parameter): - return expression - - if len(expression.parameters) > 1: - raise ValueError("Expression has more than one parameter.") - - return list(expression.parameters)[0] diff --git a/qiskit/algorithms/gradients/reverse/split_circuits.py b/qiskit/algorithms/gradients/reverse/split_circuits.py deleted file mode 100644 index b2bdf2b5375b..000000000000 --- a/qiskit/algorithms/gradients/reverse/split_circuits.py +++ /dev/null @@ -1,68 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Split a circuit into subcircuits, each containing a single parameterized gate.""" - -from __future__ import annotations - -from collections.abc import Iterable -from qiskit.circuit import QuantumCircuit, ParameterExpression, Parameter - - -def split( - circuit: QuantumCircuit, - parameters: Iterable[Parameter] | None = None, -) -> tuple[list[QuantumCircuit], list[list[Parameter]]]: - """Split the circuit at ParameterExpressions. - - Args: - circuit: The circuit to split. - parameters: The parameters at which to split. If None, split at each parameter. - - Returns: - A list of the split circuits along with a list of which parameters are in the subcircuits. - """ - circuits = [] - corresponding_parameters = [] - - sub = QuantumCircuit(*circuit.qregs, *circuit.cregs) - for inst in circuit.data: - # check if new split must be created - if parameters is None: - params = [ - param - for param in inst.operation.params - if isinstance(param, ParameterExpression) and len(param.parameters) > 0 - ] - else: - if inst.operation.definition is not None: - free_inst_params = inst.operation.definition.parameters - else: - free_inst_params = {} - - params = [p for p in parameters if p in free_inst_params] - - new_split = bool(len(params) > 0) - - if new_split: - sub.append(inst) - circuits.append(sub) - corresponding_parameters.append(params) - sub = QuantumCircuit(*circuit.qregs, *circuit.cregs) - else: - sub.append(inst) - - # handle leftover gates - if len(sub.data) > 0: - circuits[-1].compose(sub, inplace=True) - - return circuits, corresponding_parameters diff --git a/qiskit/algorithms/gradients/spsa/__init__.py b/qiskit/algorithms/gradients/spsa/__init__.py deleted file mode 100644 index 8f5fd2a37f84..000000000000 --- a/qiskit/algorithms/gradients/spsa/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/qiskit/algorithms/gradients/spsa/spsa_estimator_gradient.py b/qiskit/algorithms/gradients/spsa/spsa_estimator_gradient.py deleted file mode 100644 index 021bcb5803f0..000000000000 --- a/qiskit/algorithms/gradients/spsa/spsa_estimator_gradient.py +++ /dev/null @@ -1,135 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Gradient of Sampler with Finite difference method.""" - -from __future__ import annotations - -from collections.abc import Sequence - -import numpy as np - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator -from qiskit.providers import Options -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..base.base_estimator_gradient import BaseEstimatorGradient -from ..base.estimator_gradient_result import EstimatorGradientResult - -from ...exceptions import AlgorithmError - - -class SPSAEstimatorGradient(BaseEstimatorGradient): - """ - Compute the gradients of the expectation value by the Simultaneous Perturbation Stochastic - Approximation (SPSA) [1]. - - **Reference:** - [1] J. C. Spall, Adaptive stochastic approximation by the simultaneous perturbation method in - IEEE Transactions on Automatic Control, vol. 45, no. 10, pp. 1839-1853, Oct 2020, - `doi: 10.1109/TAC.2000.880982 `_ - """ - - def __init__( - self, - estimator: BaseEstimator, - epsilon: float, - batch_size: int = 1, - seed: int | None = None, - options: Options | None = None, - ): - """ - Args: - estimator: The estimator used to compute the gradients. - epsilon: The offset size for the SPSA gradients. - batch_size: The number of gradients to average. - seed: The seed for a random perturbation vector. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - - Raises: - ValueError: If ``epsilon`` is not positive. - """ - if epsilon <= 0: - raise ValueError(f"epsilon ({epsilon}) should be positive.") - self._epsilon = epsilon - self._batch_size = batch_size - self._seed = np.random.default_rng(seed) - - super().__init__(estimator, options) - - def _run( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> EstimatorGradientResult: - """Compute the estimator gradients on the given circuits.""" - job_circuits, job_observables, job_param_values, metadata, offsets = [], [], [], [], [] - all_n = [] - for circuit, observable, parameter_values_, parameters_ in zip( - circuits, observables, parameter_values, parameters - ): - # Indices of parameters to be differentiated. - indices = [circuit.parameters.data.index(p) for p in parameters_] - metadata.append({"parameters": parameters_}) - # Make random perturbation vectors. - offset = [ - (-1) ** (self._seed.integers(0, 2, len(circuit.parameters))) - for _ in range(self._batch_size) - ] - plus = [parameter_values_ + self._epsilon * offset_ for offset_ in offset] - minus = [parameter_values_ - self._epsilon * offset_ for offset_ in offset] - offsets.append(offset) - - # Combine inputs into a single job to reduce overhead. - job_circuits.extend([circuit] * 2 * self._batch_size) - job_observables.extend([observable] * 2 * self._batch_size) - job_param_values.extend(plus + minus) - all_n.append(2 * self._batch_size) - - # Run the single job with all circuits. - job = self._estimator.run( - job_circuits, - job_observables, - job_param_values, - **options, - ) - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Estimator job failed.") from exc - - # Compute the gradients. - gradients = [] - partial_sum_n = 0 - for i, n in enumerate(all_n): - result = results.values[partial_sum_n : partial_sum_n + n] - partial_sum_n += n - n = len(result) // 2 - diffs = (result[:n] - result[n:]) / (2 * self._epsilon) - # Calculate the gradient for each batch. Note that (``diff`` / ``offset``) is the gradient - # since ``offset`` is a perturbation vector of 1s and -1s. - batch_gradients = np.array([diff / offset for diff, offset in zip(diffs, offsets[i])]) - # Take the average of the batch gradients. - gradient = np.mean(batch_gradients, axis=0) - indices = [circuits[i].parameters.data.index(p) for p in metadata[i]["parameters"]] - gradients.append(gradient[indices]) - - opt = self._get_local_options(options) - return EstimatorGradientResult(gradients=gradients, metadata=metadata, options=opt) diff --git a/qiskit/algorithms/gradients/spsa/spsa_sampler_gradient.py b/qiskit/algorithms/gradients/spsa/spsa_sampler_gradient.py deleted file mode 100644 index be23274d7ebc..000000000000 --- a/qiskit/algorithms/gradients/spsa/spsa_sampler_gradient.py +++ /dev/null @@ -1,136 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Gradient of Sampler with Finite difference method.""" - -from __future__ import annotations - -from collections import defaultdict -from collections.abc import Sequence - -import numpy as np - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.primitives import BaseSampler -from qiskit.providers import Options - -from ..base.base_sampler_gradient import BaseSamplerGradient -from ..base.sampler_gradient_result import SamplerGradientResult - -from ...exceptions import AlgorithmError - - -class SPSASamplerGradient(BaseSamplerGradient): - """ - Compute the gradients of the sampling probability by the Simultaneous Perturbation Stochastic - Approximation (SPSA) [1]. - - **Reference:** - [1] J. C. Spall, Adaptive stochastic approximation by the simultaneous perturbation method in - IEEE Transactions on Automatic Control, vol. 45, no. 10, pp. 1839-1853, Oct 2020, - `doi: 10.1109/TAC.2000.880982 `_. - """ - - def __init__( - self, - sampler: BaseSampler, - epsilon: float, - batch_size: int = 1, - seed: int | None = None, - options: Options | None = None, - ): - """ - Args: - sampler: The sampler used to compute the gradients. - epsilon: The offset size for the SPSA gradients. - batch_size: number of gradients to average. - seed: The seed for a random perturbation vector. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > gradient's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting - - Raises: - ValueError: If ``epsilon`` is not positive. - """ - if epsilon <= 0: - raise ValueError(f"epsilon ({epsilon}) should be positive.") - self._batch_size = batch_size - self._epsilon = epsilon - self._seed = np.random.default_rng(seed) - - super().__init__(sampler, options) - - def _run( - self, - circuits: Sequence[QuantumCircuit], - parameter_values: Sequence[Sequence[float]], - parameters: Sequence[Sequence[Parameter]], - **options, - ) -> SamplerGradientResult: - """Compute the sampler gradients on the given circuits.""" - job_circuits, job_param_values, metadata, offsets = [], [], [], [] - all_n = [] - for circuit, parameter_values_, parameters_ in zip(circuits, parameter_values, parameters): - # Indices of parameters to be differentiated. - indices = [circuit.parameters.data.index(p) for p in parameters_] - metadata.append({"parameters": parameters_}) - offset = np.array( - [ - (-1) ** (self._seed.integers(0, 2, len(circuit.parameters))) - for _ in range(self._batch_size) - ] - ) - plus = [parameter_values_ + self._epsilon * offset_ for offset_ in offset] - minus = [parameter_values_ - self._epsilon * offset_ for offset_ in offset] - offsets.append(offset) - - # Combine inputs into a single job to reduce overhead. - n = 2 * self._batch_size - job_circuits.extend([circuit] * n) - job_param_values.extend(plus + minus) - all_n.append(n) - - # Run the single job with all circuits. - job = self._sampler.run(job_circuits, job_param_values, **options) - try: - results = job.result() - except Exception as exc: - raise AlgorithmError("Sampler job failed.") from exc - - # Compute the gradients. - gradients = [] - partial_sum_n = 0 - for i, n in enumerate(all_n): - dist_diffs = {} - result = results.quasi_dists[partial_sum_n : partial_sum_n + n] - for j, (dist_plus, dist_minus) in enumerate(zip(result[: n // 2], result[n // 2 :])): - dist_diff: dict[int, float] = defaultdict(float) - for key, value in dist_plus.items(): - dist_diff[key] += value / (2 * self._epsilon) - for key, value in dist_minus.items(): - dist_diff[key] -= value / (2 * self._epsilon) - dist_diffs[j] = dist_diff - gradient = [] - indices = [circuits[i].parameters.data.index(p) for p in metadata[i]["parameters"]] - for j in indices: - gradient_j: dict[int, float] = defaultdict(float) - for k in range(self._batch_size): - for key, value in dist_diffs[k].items(): - gradient_j[key] += value * offsets[i][k][j] - gradient_j = {key: value / self._batch_size for key, value in gradient_j.items()} - gradient.append(gradient_j) - gradients.append(gradient) - partial_sum_n += n - - opt = self._get_local_options(options) - return SamplerGradientResult(gradients=gradients, metadata=metadata, options=opt) diff --git a/qiskit/algorithms/gradients/utils.py b/qiskit/algorithms/gradients/utils.py deleted file mode 100644 index f46911ee26ff..000000000000 --- a/qiskit/algorithms/gradients/utils.py +++ /dev/null @@ -1,375 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Utility functions for gradients -""" - -from __future__ import annotations - -from collections import defaultdict -from collections.abc import Sequence -from dataclasses import dataclass -from enum import Enum - -import numpy as np - -from qiskit.circuit import ( - ClassicalRegister, - Gate, - Instruction, - Parameter, - ParameterExpression, - QuantumCircuit, - QuantumRegister, -) -from qiskit.circuit.library.standard_gates import ( - CXGate, - CYGate, - CZGate, - RXGate, - RXXGate, - RYGate, - RYYGate, - RZGate, - RZXGate, - RZZGate, - XGate, -) -from qiskit.quantum_info import SparsePauliOp - - -################################################################################ -## Gradient circuits and Enum -################################################################################ -class DerivativeType(Enum): - """Types of derivative.""" - - REAL = "real" - IMAG = "imag" - COMPLEX = "complex" - - -@dataclass -class GradientCircuit: - """Gradient circuit with unique parameters and mapping information.""" - - gradient_circuit: QuantumCircuit - """An internal quantum circuit with unique parameters used to calculate the gradient""" - parameter_map: dict[Parameter, list[tuple[Parameter, float | ParameterExpression]]] - """A dictionary maps the parameters of ``circuit`` to the parameters of ``gradient_circuit`` with - coefficients""" - gradient_parameter_map: dict[Parameter, ParameterExpression] - """A dictionary maps the parameters of ``gradient_circuit`` to the parameter expressions of - ``circuit``""" - - -@dataclass -class LinearCombGradientCircuit: - """Gradient circuit for the linear combination of unitaries method.""" - - gradient_circuit: QuantumCircuit - """A gradient circuit for the linear combination of unitaries method.""" - coeff: float | ParameterExpression - """A coefficient corresponds to the gradient circuit.""" - - -################################################################################ -## Parameter shift gradient -################################################################################ -def _make_param_shift_parameter_values( - circuit: QuantumCircuit, - parameter_values: np.ndarray | list[float], - parameters: Sequence[Parameter], -) -> list[np.ndarray]: - """Returns a list of parameter values with offsets for parameter shift rule. - - Args: - circuit: The original quantum circuit - parameter_values: parameter values to be added to the base parameter values. - parameters: The parameters to be shifted. - - Returns: - A list of parameter values with offsets for parameter shift rule. - """ - indices = [circuit.parameters.data.index(p) for p in parameters] - offset = np.identity(circuit.num_parameters)[indices, :] - plus_offsets = parameter_values + offset * np.pi / 2 - minus_offsets = parameter_values - offset * np.pi / 2 - return plus_offsets.tolist() + minus_offsets.tolist() - - -################################################################################ -## Linear combination gradient and Linear combination QGT -################################################################################ -def _make_lin_comb_gradient_circuit( - circuit: QuantumCircuit, add_measurement: bool = False -) -> dict[Parameter, QuantumCircuit]: - """Makes a circuit that computes the linear combination of the gradient circuits.""" - circuit_temp = circuit.copy() - qr_aux = QuantumRegister(1, "qr_aux") - cr_aux = ClassicalRegister(1, "cr_aux") - circuit_temp.add_register(qr_aux) - circuit_temp.add_register(cr_aux) - circuit_temp.h(qr_aux) - circuit_temp.data.insert(0, circuit_temp.data.pop()) - circuit_temp.sdg(qr_aux) - circuit_temp.data.insert(1, circuit_temp.data.pop()) - - lin_comb_circuits = {} - for i, instruction in enumerate(circuit_temp.data): - if instruction.operation.is_parameterized(): - for p in instruction.operation.params[0].parameters: - gate = _gate_gradient(instruction.operation) - lin_comb_circuit = circuit_temp.copy() - # insert `gate` to i-th position - lin_comb_circuit.append(gate, [qr_aux[0]] + list(instruction.qubits), []) - lin_comb_circuit.data.insert(i, lin_comb_circuit.data.pop()) - lin_comb_circuit.h(qr_aux) - if add_measurement: - lin_comb_circuit.measure(qr_aux, cr_aux) - lin_comb_circuits[p] = lin_comb_circuit - - return lin_comb_circuits - - -def _gate_gradient(gate: Gate) -> Instruction: - """Returns the derivative of the gate""" - # pylint: disable=too-many-return-statements - if isinstance(gate, RXGate): - return CXGate() - if isinstance(gate, RYGate): - return CYGate() - if isinstance(gate, RZGate): - return CZGate() - if isinstance(gate, RXXGate): - cxx_circ = QuantumCircuit(3) - cxx_circ.cx(0, 1) - cxx_circ.cx(0, 2) - cxx = cxx_circ.to_instruction() - return cxx - if isinstance(gate, RYYGate): - cyy_circ = QuantumCircuit(3) - cyy_circ.cy(0, 1) - cyy_circ.cy(0, 2) - cyy = cyy_circ.to_instruction() - return cyy - if isinstance(gate, RZZGate): - czz_circ = QuantumCircuit(3) - czz_circ.cz(0, 1) - czz_circ.cz(0, 2) - czz = czz_circ.to_instruction() - return czz - if isinstance(gate, RZXGate): - czx_circ = QuantumCircuit(3) - czx_circ.cx(0, 2) - czx_circ.cz(0, 1) - czx = czx_circ.to_instruction() - return czx - raise TypeError(f"Unrecognized parameterized gate, {gate}") - - -def _make_lin_comb_qgt_circuit( - circuit: QuantumCircuit, add_measurement: bool = False -) -> dict[tuple[Parameter, Parameter], QuantumCircuit]: - """Makes a circuit that computes the linear combination of the QGT circuits.""" - circuit_temp = circuit.copy() - qr_aux = QuantumRegister(1, "aux") - circuit_temp.add_register(qr_aux) - if add_measurement: - cr_aux = ClassicalRegister(1, "aux") - circuit_temp.add_bits(cr_aux) - circuit_temp.h(qr_aux) - circuit_temp.data.insert(0, circuit_temp.data.pop()) - - lin_comb_qgt_circuits = {} - for i, instruction_i in enumerate(circuit_temp.data): - if not instruction_i.operation.is_parameterized(): - continue - for j, instruction_j in enumerate(circuit_temp.data): - if not instruction_j.operation.is_parameterized(): - continue - # Calculate the QGT of the i-th gate with respect to the j-th gate. - param_i = instruction_i.operation.params[0] - param_j = instruction_j.operation.params[0] - - for p_i in param_i.parameters: - for p_j in param_j.parameters: - if circuit_temp.parameters.data.index(p_i) > circuit_temp.parameters.data.index( - p_j - ): - continue - gate_i = _gate_gradient(instruction_i.operation) - gate_j = _gate_gradient(instruction_j.operation) - lin_comb_qgt_circuit = circuit_temp.copy() - if i < j: - # insert gate_j to j-th position - lin_comb_qgt_circuit.append( - gate_j, [qr_aux[0]] + list(instruction_j.qubits), [] - ) - lin_comb_qgt_circuit.data.insert(j, lin_comb_qgt_circuit.data.pop()) - # insert gate_i to i-th position with two X gates at its sides - lin_comb_qgt_circuit.append(XGate(), [qr_aux[0]], []) - lin_comb_qgt_circuit.data.insert(i, lin_comb_qgt_circuit.data.pop()) - lin_comb_qgt_circuit.append( - gate_i, [qr_aux[0]] + list(instruction_i.qubits), [] - ) - lin_comb_qgt_circuit.data.insert(i, lin_comb_qgt_circuit.data.pop()) - lin_comb_qgt_circuit.append(XGate(), [qr_aux[0]], []) - lin_comb_qgt_circuit.data.insert(i, lin_comb_qgt_circuit.data.pop()) - else: - # insert gate_i to i-th position - lin_comb_qgt_circuit.append( - gate_i, [qr_aux[0]] + list(instruction_i.qubits), [] - ) - lin_comb_qgt_circuit.data.insert(i, lin_comb_qgt_circuit.data.pop()) - # insert gate_j to j-th position with two X gates at its sides - lin_comb_qgt_circuit.append(XGate(), [qr_aux[0]], []) - lin_comb_qgt_circuit.data.insert(j, lin_comb_qgt_circuit.data.pop()) - lin_comb_qgt_circuit.append( - gate_j, [qr_aux[0]] + list(instruction_j.qubits), [] - ) - lin_comb_qgt_circuit.data.insert(j, lin_comb_qgt_circuit.data.pop()) - lin_comb_qgt_circuit.append(XGate(), [qr_aux[0]], []) - lin_comb_qgt_circuit.data.insert(j, lin_comb_qgt_circuit.data.pop()) - - lin_comb_qgt_circuit.h(qr_aux) - if add_measurement: - lin_comb_qgt_circuit.measure(qr_aux, cr_aux) - lin_comb_qgt_circuits[(p_i, p_j)] = lin_comb_qgt_circuit - - return lin_comb_qgt_circuits - - -def _make_lin_comb_observables( - observable: SparsePauliOp, - derivative_type: DerivativeType, -) -> tuple[SparsePauliOp, SparsePauliOp | None]: - """Make the observable with an ancillary operator for the linear combination gradient. - - Args: - observable: The observable. - derivative_type: The type of derivative. Can be either ``DerivativeType.REAL`` - ``DerivativeType.IMAG``, or ``DerivativeType.COMPLEX``. - - Returns: - The observable with an ancillary operator for the linear combination gradient. - - Raises: - ValueError: If the derivative type is not supported. - """ - if derivative_type == DerivativeType.REAL: - return observable.expand(SparsePauliOp.from_list([("Z", 1)])), None - elif derivative_type == DerivativeType.IMAG: - return observable.expand(SparsePauliOp.from_list([("Y", -1)])), None - elif derivative_type == DerivativeType.COMPLEX: - return observable.expand(SparsePauliOp.from_list([("Z", 1)])), observable.expand( - SparsePauliOp.from_list([("Y", -1)]) - ) - else: - raise ValueError(f"Derivative type {derivative_type} is not supported.") - - -################################################################################ -## Preprocess -################################################################################ -def _assign_unique_parameters( - circuit: QuantumCircuit, -) -> GradientCircuit: - """Assign unique parameters to the circuit. - - Args: - circuit: The circuit to assign unique parameters. - - Returns: - The circuit with unique parameters and the mapping from the original parameters to the - unique parameters. - """ - gradient_circuit = circuit.copy_empty_like(f"{circuit.name}_gradient") - parameter_map = defaultdict(list) - gradient_parameter_map = {} - num_gradient_parameters = 0 - for instruction in circuit.data: - if instruction.operation.is_parameterized(): - new_op_params = [] - for angle in instruction.operation.params: - new_parameter = Parameter(f"__gθ{num_gradient_parameters}") - new_op_params.append(new_parameter) - num_gradient_parameters += 1 - for parameter in angle.parameters: - parameter_map[parameter].append((new_parameter, angle.gradient(parameter))) - gradient_parameter_map[new_parameter] = angle - instruction.operation.params = new_op_params - gradient_circuit.append(instruction.operation, instruction.qubits, instruction.clbits) - # For the global phase - gradient_circuit.global_phase = circuit.global_phase - if isinstance(gradient_circuit.global_phase, ParameterExpression): - substitution_map = {} - for parameter in gradient_circuit.global_phase.parameters: - if parameter in parameter_map: - substitution_map[parameter] = parameter_map[parameter][0][0] - else: - new_parameter = Parameter(f"__gθ{num_gradient_parameters}") - substitution_map[parameter] = new_parameter - parameter_map[parameter].append((new_parameter, 1)) - num_gradient_parameters += 1 - gradient_circuit.global_phase = gradient_circuit.global_phase.subs(substitution_map) - return GradientCircuit(gradient_circuit, parameter_map, gradient_parameter_map) - - -def _make_gradient_parameter_values( - circuit: QuantumCircuit, - gradient_circuit: GradientCircuit, - parameter_values: np.ndarray, -) -> np.ndarray: - """Makes parameter values for the gradient circuit. - - Args: - circuit: The original quantum circuit - gradient_circuit: The gradient circuit - parameter_values: The parameter values for the original circuit - parameter_set: The parameter set to calculate gradients - - Returns: - The parameter values for the gradient circuit. - """ - g_circuit = gradient_circuit.gradient_circuit - g_parameter_values = np.empty(len(g_circuit.parameters)) - for i, g_parameter in enumerate(g_circuit.parameters): - expr = gradient_circuit.gradient_parameter_map[g_parameter] - bound_expr = expr.bind( - {p: parameter_values[circuit.parameters.data.index(p)] for p in expr.parameters} - ) - g_parameter_values[i] = float(bound_expr) - return g_parameter_values - - -def _make_gradient_parameters( - gradient_circuit: GradientCircuit, - parameters: Sequence[Parameter], -) -> Sequence[Parameter]: - """Makes parameter set for the gradient circuit. - - Args: - gradient_circuit: The gradient circuit - parameters: The parameters in the original circuit to calculate gradients - - Returns: - The parameters in the gradient circuit to calculate gradients. - """ - g_parameters = [ - g_parameter - for parameter in parameters - for g_parameter, _ in gradient_circuit.parameter_map[parameter] - ] - # make g_parameters unique and return it. - return list(dict.fromkeys(g_parameters)) diff --git a/qiskit/algorithms/list_or_dict.py b/qiskit/algorithms/list_or_dict.py deleted file mode 100644 index 95314dd79a3b..000000000000 --- a/qiskit/algorithms/list_or_dict.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Introduced new type to maintain readability.""" - -from typing import TypeVar, List, Union, Optional, Dict - -_T = TypeVar("_T") # Pylint does not allow single character class names. -ListOrDict = Union[List[Optional[_T]], Dict[str, _T]] diff --git a/qiskit/algorithms/minimum_eigen_solvers/__init__.py b/qiskit/algorithms/minimum_eigen_solvers/__init__.py deleted file mode 100644 index 3d7d18023b75..000000000000 --- a/qiskit/algorithms/minimum_eigen_solvers/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Minimum Eigen Solvers Package""" - -from .vqe import VQE, VQEResult -from .qaoa import QAOA -from .numpy_minimum_eigen_solver import NumPyMinimumEigensolver -from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult - -__all__ = [ - "VQE", - "VQEResult", - "QAOA", - "NumPyMinimumEigensolver", - "MinimumEigensolver", - "MinimumEigensolverResult", -] diff --git a/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py b/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py deleted file mode 100644 index 6625cf30eaeb..000000000000 --- a/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py +++ /dev/null @@ -1,141 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Minimum Eigensolver interface""" -from __future__ import annotations - -from abc import ABC, abstractmethod - -import numpy as np - -from qiskit.opflow import OperatorBase -from qiskit.utils.deprecation import deprecate_func -from ..algorithm_result import AlgorithmResult -from ..list_or_dict import ListOrDict - - -class MinimumEigensolver(ABC): - """Deprecated: Minimum Eigensolver Interface. - - The Minimum Eigensolver interface has been superseded by the - :class:`qiskit.algorithms.minimum_eigensolvers.MinimumEigensolver` interface. - This interface will be deprecated in a future release and subsequently - removed after that. - - Algorithms that can compute a minimum eigenvalue for an operator - may implement this interface to allow different algorithms to be - used interchangeably. - """ - - @deprecate_func( - additional_msg=( - "Instead, use the interface " - "``qiskit.algorithms.minimum_eigensolvers.MinimumEigensolver``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__(self) -> None: - pass - - @abstractmethod - def compute_minimum_eigenvalue( - self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None - ) -> "MinimumEigensolverResult": - """ - Computes minimum eigenvalue. Operator and aux_operators can be supplied here and - if not None will override any already set into algorithm so it can be reused with - different operators. While an operator is required by algorithms, aux_operators - are optional. To 'remove' a previous aux_operators array use an empty list here. - - Args: - operator: Qubit operator of the Observable - aux_operators: Optional list of auxiliary operators to be evaluated with the - eigenstate of the minimum eigenvalue main result and their expectation values - returned. For instance in chemistry these can be dipole operators, total particle - count operators so we can get values for these at the ground state. - - Returns: - MinimumEigensolverResult - """ - return MinimumEigensolverResult() - - @classmethod - def supports_aux_operators(cls) -> bool: - """Whether computing the expectation value of auxiliary operators is supported. - - If the minimum eigensolver computes an eigenstate of the main operator then it - can compute the expectation value of the aux_operators for that state. Otherwise - they will be ignored. - - Returns: - True if aux_operator expectations can be evaluated, False otherwise - """ - return False - - -class MinimumEigensolverResult(AlgorithmResult): - """Deprecated: Minimum Eigensolver Result. - - The MinimumEigensolverResult class has been superseded by the - :class:`qiskit.algorithms.minimum_eigensolvers.MinimumEigensolverResult` class. - This class will be deprecated in a future release and subsequently - removed after that. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class " - "``qiskit.algorithms.minimum_eigensolvers.MinimumEigensolverResult``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__(self) -> None: - super().__init__() - self._eigenvalue: complex | None = None - self._eigenstate: np.ndarray | None = None - self._aux_operator_eigenvalues: ListOrDict[tuple[complex, complex]] | None = None - - @property - def eigenvalue(self) -> complex | None: - """returns eigen value""" - return self._eigenvalue - - @eigenvalue.setter - def eigenvalue(self, value: complex) -> None: - """set eigen value""" - self._eigenvalue = value - - @property - def eigenstate(self) -> np.ndarray | None: - """return eigen state""" - return self._eigenstate - - @eigenstate.setter - def eigenstate(self, value: np.ndarray) -> None: - """set eigen state""" - self._eigenstate = value - - @property - def aux_operator_eigenvalues(self) -> ListOrDict[tuple[complex, complex]] | None: - """Return aux operator expectation values. - - These values are in fact tuples formatted as (mean, standard deviation). - """ - return self._aux_operator_eigenvalues - - @aux_operator_eigenvalues.setter - def aux_operator_eigenvalues(self, value: ListOrDict[tuple[complex, complex]]) -> None: - """set aux operator eigen values""" - self._aux_operator_eigenvalues = value diff --git a/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py b/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py deleted file mode 100644 index 83623666b715..000000000000 --- a/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py +++ /dev/null @@ -1,105 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Numpy Minimum Eigensolver algorithm.""" -from __future__ import annotations - -import logging -import warnings -from collections.abc import Callable - -import numpy as np - -from qiskit.opflow import OperatorBase -from qiskit.utils.deprecation import deprecate_func -from ..eigen_solvers.numpy_eigen_solver import NumPyEigensolver -from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult -from ..list_or_dict import ListOrDict - -logger = logging.getLogger(__name__) - - -class NumPyMinimumEigensolver(MinimumEigensolver): - """ - Deprecated: Numpy Minimum Eigensolver algorithm. - - The NumPyMinimumEigensolver class has been superseded by the - :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` class. - This class will be deprecated in a future release and subsequently - removed after that. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class " - "``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - filter_criterion: Callable[ - [list | np.ndarray, float, ListOrDict[float] | None], bool - ] = None, - ) -> None: - """ - Args: - filter_criterion: callable that allows to filter eigenvalues/eigenstates. The minimum - eigensolver is only searching over feasible states and returns an eigenstate that - has the smallest eigenvalue among feasible states. The callable has the signature - `filter(eigenstate, eigenvalue, aux_values)` and must return a boolean to indicate - whether to consider this value or not. If there is no - feasible element, the result can even be empty. - """ - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__init__() - self._ces = NumPyEigensolver(filter_criterion=filter_criterion) - self._ret = MinimumEigensolverResult() - - @property - def filter_criterion( - self, - ) -> Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] | None: - """returns the filter criterion if set""" - return self._ces.filter_criterion - - @filter_criterion.setter - def filter_criterion( - self, - filter_criterion: Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] - | None, - ) -> None: - """set the filter criterion""" - self._ces.filter_criterion = filter_criterion - - @classmethod - def supports_aux_operators(cls) -> bool: - return NumPyEigensolver.supports_aux_operators() - - def compute_minimum_eigenvalue( - self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None - ) -> MinimumEigensolverResult: - super().compute_minimum_eigenvalue(operator, aux_operators) - result_ces = self._ces.compute_eigenvalues(operator, aux_operators) - self._ret = MinimumEigensolverResult() - if result_ces.eigenvalues is not None and len(result_ces.eigenvalues) > 0: - self._ret.eigenvalue = result_ces.eigenvalues[0] - self._ret.eigenstate = result_ces.eigenstates[0] - if result_ces.aux_operator_eigenvalues: - self._ret.aux_operator_eigenvalues = result_ces.aux_operator_eigenvalues[0] - - logger.debug("MinimumEigensolver:\n%s", self._ret) - - return self._ret diff --git a/qiskit/algorithms/minimum_eigen_solvers/qaoa.py b/qiskit/algorithms/minimum_eigen_solvers/qaoa.py deleted file mode 100644 index fc18be860218..000000000000 --- a/qiskit/algorithms/minimum_eigen_solvers/qaoa.py +++ /dev/null @@ -1,185 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Quantum Approximate Optimization Algorithm.""" -from __future__ import annotations - -import warnings -from collections.abc import Callable - -import numpy as np - -from qiskit.algorithms.optimizers import Minimizer, Optimizer -from qiskit.circuit import QuantumCircuit -from qiskit.opflow import OperatorBase, ExpectationBase -from qiskit.opflow.gradients import GradientBase -from qiskit.providers import Backend -from qiskit.utils.quantum_instance import QuantumInstance -from qiskit.utils.validation import validate_min -from qiskit.utils.deprecation import deprecate_func -from qiskit.circuit.library.n_local.qaoa_ansatz import QAOAAnsatz -from qiskit.algorithms.minimum_eigen_solvers.vqe import VQE - - -class QAOA(VQE): - """ - Deprecated: Quantum Approximate Optimization Algorithm. - - The QAOA class has been superseded by the - :class:`qiskit.algorithms.minimum_eigensolvers.QAOA` class. - This class will be deprecated in a future release and subsequently - removed after that. - - `QAOA `__ is a well-known algorithm for finding approximate - solutions to combinatorial-optimization problems. - - The QAOA implementation directly extends :class:`VQE` and inherits VQE's optimization structure. - However, unlike VQE, which can be configured with arbitrary ansatzes, - QAOA uses its own fine-tuned ansatz, which comprises :math:`p` parameterized global - :math:`x` rotations and :math:`p` different parameterizations of the problem hamiltonian. - QAOA is thus principally configured by the single integer parameter, *p*, - which dictates the depth of the ansatz, and thus affects the approximation quality. - - An optional array of :math:`2p` parameter values, as the *initial_point*, may be provided as the - starting **beta** and **gamma** parameters (as identically named in the - original `QAOA paper `__) for the QAOA ansatz. - - An operator or a parameterized quantum circuit may optionally also be provided as a custom - `mixer` Hamiltonian. This allows, as discussed in - `this paper `__ for quantum annealing, - and in `this paper `__ for QAOA, - to run constrained optimization problems where the mixer constrains - the evolution to a feasible subspace of the full Hilbert space. - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.minimum_eigensolvers.QAOA``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - optimizer: Optimizer | Minimizer | None = None, - reps: int = 1, - initial_state: QuantumCircuit | None = None, - mixer: QuantumCircuit | OperatorBase = None, - initial_point: np.ndarray | None = None, - gradient: GradientBase | Callable[[np.ndarray | list], list] | None = None, - expectation: ExpectationBase | None = None, - include_custom: bool = False, - max_evals_grouped: int = 1, - callback: Callable[[int, np.ndarray, float, float], None] | None = None, - quantum_instance: QuantumInstance | Backend | None = None, - ) -> None: - """ - Args: - optimizer: A classical optimizer, see also :class:`~qiskit.algorithms.VQE` for - more details on the possible types. - reps: the integer parameter :math:`p` as specified in https://arxiv.org/abs/1411.4028, - Has a minimum valid value of 1. - initial_state: An optional initial state to prepend the QAOA circuit with - mixer: the mixer Hamiltonian to evolve with or a custom quantum circuit. Allows support - of optimizations in constrained subspaces as per https://arxiv.org/abs/1709.03489 - as well as warm-starting the optimization as introduced - in http://arxiv.org/abs/2009.10095. - initial_point: An optional initial point (i.e. initial parameter values) - for the optimizer. If ``None`` then it will simply compute a random one. - gradient: An optional gradient operator respectively a gradient function used for - optimization. - expectation: The Expectation converter for taking the average value of the - Observable over the ansatz state function. When None (the default) an - :class:`~qiskit.opflow.expectations.ExpectationFactory` is used to select - an appropriate expectation based on the operator and backend. When using Aer - qasm_simulator backend, with paulis, it is however much faster to leverage custom - Aer function for the computation but, although VQE performs much faster - with it, the outcome is ideal, with no shot noise, like using a state vector - simulator. If you are just looking for the quickest performance when choosing Aer - qasm_simulator and the lack of shot noise is not an issue then set `include_custom` - parameter here to True (defaults to False). - include_custom: When `expectation` parameter here is None setting this to True will - allow the factory to include the custom Aer pauli expectation. - max_evals_grouped: Max number of evaluations performed simultaneously. Signals the - given optimizer that more than one set of parameters can be supplied so that - potentially the expectation values can be computed in parallel. Typically this is - possible when a finite difference gradient is used by the optimizer such that - multiple points to compute the gradient can be passed and if computed in parallel - improve overall execution time. Ignored if a gradient operator or function is - given. - callback: a callback that can access the intermediate data during the optimization. - Four parameter values are passed to the callback as follows during each evaluation - by the optimizer for its current set of parameters as it works towards the minimum. - These are: the evaluation count, the optimizer parameters for the - ansatz, the evaluated mean and the evaluated standard deviation. - quantum_instance: Quantum Instance or Backend - """ - validate_min("reps", reps, 1) - - self._reps = reps - self._mixer = mixer - self._initial_state = initial_state - self._cost_operator: OperatorBase | None = None - - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__init__( - ansatz=None, - optimizer=optimizer, - initial_point=initial_point, - gradient=gradient, - expectation=expectation, - include_custom=include_custom, - max_evals_grouped=max_evals_grouped, - callback=callback, - quantum_instance=quantum_instance, - ) - - def _check_operator_ansatz(self, operator: OperatorBase) -> None: - # Recreates a circuit based on operator parameter. - if operator != self._cost_operator: - self._cost_operator = operator - self.ansatz = QAOAAnsatz( - operator, self._reps, initial_state=self._initial_state, mixer_operator=self._mixer - ).decompose() # TODO remove decompose once #6674 is fixed - - @property - def initial_state(self) -> QuantumCircuit | None: - """ - Returns: - Returns the initial state. - """ - return self._initial_state - - @initial_state.setter - def initial_state(self, initial_state: QuantumCircuit | None) -> None: - """ - Args: - initial_state: Initial state to set. - """ - self._initial_state = initial_state - - @property - def mixer(self) -> QuantumCircuit | OperatorBase: - """ - Returns: - Returns the mixer. - """ - return self._mixer - - @mixer.setter - def mixer(self, mixer: QuantumCircuit | OperatorBase) -> None: - """ - Args: - mixer: Mixer to set. - """ - self._mixer = mixer diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py deleted file mode 100644 index 3ca342f9894d..000000000000 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ /dev/null @@ -1,749 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Variational Quantum Eigensolver algorithm. - -See https://arxiv.org/abs/1304.3061 -""" - -from __future__ import annotations - -import logging -import warnings -from collections.abc import Callable -from time import time - -import numpy as np - -from qiskit.circuit import Parameter, QuantumCircuit -from qiskit.circuit.library import RealAmplitudes -from qiskit.opflow import ( - CircuitSampler, - CircuitStateFn, - ExpectationBase, - ExpectationFactory, - ListOp, - OperatorBase, - PauliSumOp, - StateFn, -) -from qiskit.opflow.gradients import GradientBase -from qiskit.providers import Backend -from qiskit.utils import QuantumInstance, algorithm_globals -from qiskit.utils.backend_utils import is_aer_provider -from qiskit.utils.validation import validate_min -from qiskit.utils.deprecation import deprecate_func - -from ..aux_ops_evaluator import eval_observables -from ..exceptions import AlgorithmError -from ..list_or_dict import ListOrDict -from ..optimizers import SLSQP, Minimizer, Optimizer -from ..variational_algorithm import VariationalAlgorithm, VariationalResult -from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult - -logger = logging.getLogger(__name__) - - -class VQE(VariationalAlgorithm, MinimumEigensolver): - r"""Deprecated: Variational Quantum Eigensolver algorithm. - - The VQE class has been superseded by the - :class:`qiskit.algorithms.minimum_eigensolvers.VQE` class. - This class will be deprecated in a future release and subsequently - removed after that. - - `VQE `__ is a quantum algorithm that uses a - variational technique to find - the minimum eigenvalue of the Hamiltonian :math:`H` of a given system. - - An instance of VQE requires defining two algorithmic sub-components: - a trial state (a.k.a. ansatz) which is a :class:`QuantumCircuit`, and one of the classical - :mod:`~qiskit.algorithms.optimizers`. The ansatz is varied, via its set of parameters, by the - optimizer, such that it works towards a state, as determined by the parameters applied to the - ansatz, that will result in the minimum expectation value being measured of the input operator - (Hamiltonian). - - An optional array of parameter values, via the *initial_point*, may be provided as the - starting point for the search of the minimum eigenvalue. This feature is particularly useful - such as when there are reasons to believe that the solution point is close to a particular - point. As an example, when building the dissociation profile of a molecule, - it is likely that using the previous computed optimal solution as the starting - initial point for the next interatomic distance is going to reduce the number of iterations - necessary for the variational algorithm to converge. It provides an - `initial point tutorial `__ detailing this use case. - - The length of the *initial_point* list value must match the number of the parameters - expected by the ansatz being used. If the *initial_point* is left at the default - of ``None``, then VQE will look to the ansatz for a preferred value, based on its - given initial state. If the ansatz returns ``None``, - then a random point will be generated within the parameter bounds set, as per above. - If the ansatz provides ``None`` as the lower bound, then VQE - will default it to :math:`-2\pi`; similarly, if the ansatz returns ``None`` - as the upper bound, the default value will be :math:`2\pi`. - - The optimizer can either be one of Qiskit's optimizers, such as - :class:`~qiskit.algorithms.optimizers.SPSA` or a callable with the following signature: - - .. note:: - - The callable _must_ have the argument names ``fun, x0, jac, bounds`` as indicated - in the following code block. - - .. code-block:: python - - from qiskit.algorithms.optimizers import OptimizerResult - - def my_minimizer(fun, x0, jac=None, bounds=None) -> OptimizerResult: - # Note that the callable *must* have these argument names! - # Args: - # fun (callable): the function to minimize - # x0 (np.ndarray): the initial point for the optimization - # jac (callable, optional): the gradient of the objective function - # bounds (list, optional): a list of tuples specifying the parameter bounds - - result = OptimizerResult() - result.x = # optimal parameters - result.fun = # optimal function value - return result - - The above signature also allows to directly pass any SciPy minimizer, for instance as - - .. code-block:: python - - from functools import partial - from scipy.optimize import minimize - - optimizer = partial(minimize, method="L-BFGS-B") - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.minimum_eigensolvers.VQE``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - ansatz: QuantumCircuit | None = None, - optimizer: Optimizer | Minimizer | None = None, - initial_point: np.ndarray | None = None, - gradient: GradientBase | Callable | None = None, - expectation: ExpectationBase | None = None, - include_custom: bool = False, - max_evals_grouped: int = 1, - callback: Callable[[int, np.ndarray, float, float], None] | None = None, - quantum_instance: QuantumInstance | Backend | None = None, - ) -> None: - """ - - Args: - ansatz: A parameterized circuit used as Ansatz for the wave function. - optimizer: A classical optimizer. Can either be a Qiskit optimizer or a callable - that takes an array as input and returns a Qiskit or SciPy optimization result. - initial_point: An optional initial point (i.e. initial parameter values) - for the optimizer. If ``None`` then VQE will look to the ansatz for a preferred - point and if not will simply compute a random one. - gradient: An optional gradient function or operator for optimizer. - expectation: The Expectation converter for taking the average value of the - Observable over the ansatz state function. When ``None`` (the default) an - :class:`~qiskit.opflow.expectations.ExpectationFactory` is used to select - an appropriate expectation based on the operator and backend. When using Aer - qasm_simulator backend, with paulis, it is however much faster to leverage custom - Aer function for the computation but, although VQE performs much faster - with it, the outcome is ideal, with no shot noise, like using a state vector - simulator. If you are just looking for the quickest performance when choosing Aer - qasm_simulator and the lack of shot noise is not an issue then set `include_custom` - parameter here to ``True`` (defaults to ``False``). - include_custom: When `expectation` parameter here is None setting this to ``True`` will - allow the factory to include the custom Aer pauli expectation. - max_evals_grouped: Max number of evaluations performed simultaneously. Signals the - given optimizer that more than one set of parameters can be supplied so that - potentially the expectation values can be computed in parallel. Typically this is - possible when a finite difference gradient is used by the optimizer such that - multiple points to compute the gradient can be passed and if computed in parallel - improve overall execution time. Deprecated if a gradient operator or function is - given. - callback: a callback that can access the intermediate data during the optimization. - Four parameter values are passed to the callback as follows during each evaluation - by the optimizer for its current set of parameters as it works towards the minimum. - These are: the evaluation count, the optimizer parameters for the - ansatz, the evaluated mean and the evaluated standard deviation.` - quantum_instance: Quantum Instance or Backend - - """ - validate_min("max_evals_grouped", max_evals_grouped, 1) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__init__() - - self._max_evals_grouped = max_evals_grouped - self._circuit_sampler: CircuitSampler | None = None - self._expectation = None - self.expectation = expectation - self._include_custom = include_custom - - self._ansatz: QuantumCircuit | None = None - self.ansatz = ansatz - - self._optimizer: Optimizer | None = None - self.optimizer = optimizer - - self._initial_point: np.ndarray | None = None - self.initial_point = initial_point - self._gradient: GradientBase | Callable | None = None - self.gradient = gradient - self._quantum_instance: QuantumInstance | None = None - - if quantum_instance is not None: - self.quantum_instance = quantum_instance - - self._eval_time = None - self._eval_count = 0 - self._callback: Callable[[int, np.ndarray, float, float], None] | None = None - self.callback = callback - - logger.info(self.print_settings()) - - # TODO remove this once the stateful methods are deleted - self._ret: VQEResult | None = None - - @property - def ansatz(self) -> QuantumCircuit: - """Returns the ansatz.""" - return self._ansatz - - @ansatz.setter - def ansatz(self, ansatz: QuantumCircuit | None): - """Sets the ansatz. - - Args: - ansatz: The parameterized circuit used as an ansatz. - If None is passed, RealAmplitudes is used by default. - - """ - if ansatz is None: - ansatz = RealAmplitudes() - - self._ansatz = ansatz - - @property - def gradient(self) -> GradientBase | Callable | None: - """Returns the gradient.""" - return self._gradient - - @gradient.setter - def gradient(self, gradient: GradientBase | Callable | None): - """Sets the gradient.""" - self._gradient = gradient - - @property - def quantum_instance(self) -> QuantumInstance | None: - """Returns quantum instance.""" - return self._quantum_instance - - @quantum_instance.setter - def quantum_instance(self, quantum_instance: QuantumInstance | Backend) -> None: - """Sets quantum_instance""" - if not isinstance(quantum_instance, QuantumInstance): - quantum_instance = QuantumInstance(quantum_instance) - - self._quantum_instance = quantum_instance - self._circuit_sampler = CircuitSampler( - quantum_instance, param_qobj=is_aer_provider(quantum_instance.backend) - ) - - @property - def initial_point(self) -> np.ndarray | None: - """Returns initial point""" - return self._initial_point - - @initial_point.setter - def initial_point(self, initial_point: np.ndarray): - """Sets initial point""" - self._initial_point = initial_point - - @property - def max_evals_grouped(self) -> int: - """Returns max_evals_grouped""" - return self._max_evals_grouped - - @max_evals_grouped.setter - def max_evals_grouped(self, max_evals_grouped: int): - """Sets max_evals_grouped""" - self._max_evals_grouped = max_evals_grouped - self.optimizer.set_max_evals_grouped(max_evals_grouped) - - @property - def include_custom(self) -> bool: - """Returns include_custom""" - return self._include_custom - - @include_custom.setter - def include_custom(self, include_custom: bool): - """Sets include_custom. If set to another value than the one that was previsously set, - the expectation attribute is reset to None. - """ - if include_custom != self._include_custom: - self._include_custom = include_custom - self.expectation = None - - @property - def callback(self) -> Callable[[int, np.ndarray, float, float], None] | None: - """Returns callback""" - return self._callback - - @callback.setter - def callback(self, callback: Callable[[int, np.ndarray, float, float], None] | None): - """Sets callback""" - self._callback = callback - - @property - def expectation(self) -> ExpectationBase | None: - """The expectation value algorithm used to construct the expectation measurement from - the observable.""" - return self._expectation - - @expectation.setter - def expectation(self, exp: ExpectationBase | None) -> None: - self._expectation = exp - - def _check_operator_ansatz(self, operator: OperatorBase): - """Check that the number of qubits of operator and ansatz match.""" - if operator is not None and self.ansatz is not None: - if operator.num_qubits != self.ansatz.num_qubits: - # try to set the number of qubits on the ansatz, if possible - try: - self.ansatz.num_qubits = operator.num_qubits - except AttributeError as ex: - raise AlgorithmError( - "The number of qubits of the ansatz does not match the " - "operator, and the ansatz does not allow setting the " - "number of qubits using `num_qubits`." - ) from ex - - @property - def optimizer(self) -> Optimizer: - """Returns optimizer""" - return self._optimizer - - @optimizer.setter - def optimizer(self, optimizer: Optimizer | None): - """Sets the optimizer attribute. - - Args: - optimizer: The optimizer to be used. If None is passed, SLSQP is used by default. - - """ - if optimizer is None: - optimizer = SLSQP() - - if isinstance(optimizer, Optimizer): - optimizer.set_max_evals_grouped(self.max_evals_grouped) - - self._optimizer = optimizer - - @property - def setting(self): - """Prepare the setting of VQE as a string.""" - ret = f"Algorithm: {self.__class__.__name__}\n" - params = "" - for key, value in self.__dict__.items(): - if key[0] == "_": - if "initial_point" in key and value is None: - params += "-- {}: {}\n".format(key[1:], "Random seed") - else: - params += f"-- {key[1:]}: {value}\n" - ret += f"{params}" - return ret - - def print_settings(self): - """ - Preparing the setting of VQE into a string. - - Returns: - str: the formatted setting of VQE - """ - ret = "\n" - ret += "==================== Setting of {} ============================\n".format( - self.__class__.__name__ - ) - ret += f"{self.setting}" - ret += "===============================================================\n" - if self.ansatz is not None: - ret += "{}".format(self.ansatz.draw(output="text")) - else: - ret += "ansatz has not been set" - ret += "===============================================================\n" - if callable(self.optimizer): - ret += "Optimizer is custom callable\n" - else: - ret += f"{self._optimizer.setting}" - ret += "===============================================================\n" - return ret - - def construct_expectation( - self, - parameter: list[float] | list[Parameter] | np.ndarray, - operator: OperatorBase, - return_expectation: bool = False, - ) -> OperatorBase | tuple[OperatorBase, ExpectationBase]: - r""" - Generate the ansatz circuit and expectation value measurement, and return their - runnable composition. - - Args: - parameter: Parameters for the ansatz circuit. - operator: Qubit operator of the Observable - return_expectation: If True, return the ``ExpectationBase`` expectation converter used - in the construction of the expectation value. Useful e.g. to compute the standard - deviation of the expectation value. - - Returns: - The Operator equalling the measurement of the ansatz :class:`StateFn` by the - Observable's expectation :class:`StateFn`, and, optionally, the expectation converter. - - Raises: - AlgorithmError: If no operator has been provided. - AlgorithmError: If no expectation is passed and None could be inferred via the - ExpectationFactory. - """ - if operator is None: - raise AlgorithmError("The operator was never provided.") - - self._check_operator_ansatz(operator) - - # if expectation was never created, try to create one - if self.expectation is None: - expectation = ExpectationFactory.build( - operator=operator, - backend=self.quantum_instance, - include_custom=self._include_custom, - ) - else: - expectation = self.expectation - - wave_function = self.ansatz.assign_parameters(parameter) - - observable_meas = expectation.convert(StateFn(operator, is_measurement=True)) - ansatz_circuit_op = CircuitStateFn(wave_function) - expect_op = observable_meas.compose(ansatz_circuit_op).reduce() - - if return_expectation: - return expect_op, expectation - - return expect_op - - def construct_circuit( - self, - parameter: list[float] | list[Parameter] | np.ndarray, - operator: OperatorBase, - ) -> list[QuantumCircuit]: - """Return the circuits used to compute the expectation value. - - Args: - parameter: Parameters for the ansatz circuit. - operator: Qubit operator of the Observable - - Returns: - A list of the circuits used to compute the expectation value. - """ - expect_op = self.construct_expectation(parameter, operator).to_circuit_op() - - circuits = [] - - # recursively extract circuits - def extract_circuits(op): - if isinstance(op, CircuitStateFn): - circuits.append(op.primitive) - elif isinstance(op, ListOp): - for op_i in op.oplist: - extract_circuits(op_i) - - extract_circuits(expect_op) - - return circuits - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - def compute_minimum_eigenvalue( - self, operator: OperatorBase, aux_operators: ListOrDict[OperatorBase] | None = None - ) -> MinimumEigensolverResult: - super().compute_minimum_eigenvalue(operator, aux_operators) - - if self.quantum_instance is None: - raise AlgorithmError( - "A QuantumInstance or Backend must be supplied to run the quantum algorithm." - ) - self.quantum_instance.circuit_summary = True - - # this sets the size of the ansatz, so it must be called before the initial point - # validation - self._check_operator_ansatz(operator) - - # set an expectation for this algorithm run (will be reset to None at the end) - initial_point = _validate_initial_point(self.initial_point, self.ansatz) - - bounds = _validate_bounds(self.ansatz) - # We need to handle the array entries being zero or Optional i.e. having value None - if aux_operators: - zero_op = PauliSumOp.from_list([("I" * self.ansatz.num_qubits, 0)]) - - # Convert the None and zero values when aux_operators is a list. - # Drop None and convert zero values when aux_operators is a dict. - if isinstance(aux_operators, list): - key_op_iterator = enumerate(aux_operators) - converted: ListOrDict[OperatorBase] = [zero_op] * len(aux_operators) - else: - key_op_iterator = aux_operators.items() - converted = {} - for key, op in key_op_iterator: - if op is not None: - converted[key] = zero_op if op == 0 else op - - aux_operators = converted - - else: - aux_operators = None - - # Convert the gradient operator into a callable function that is compatible with the - # optimization routine. - if isinstance(self._gradient, GradientBase): - gradient = self._gradient.gradient_wrapper( - ~StateFn(operator) @ StateFn(self.ansatz), - bind_params=list(self.ansatz.parameters), - backend=self._quantum_instance, - ) - else: - gradient = self._gradient - - self._eval_count = 0 - energy_evaluation, expectation = self.get_energy_evaluation( - operator, return_expectation=True - ) - - start_time = time() - - if callable(self.optimizer): - opt_result = self.optimizer( # pylint: disable=not-callable - fun=energy_evaluation, x0=initial_point, jac=gradient, bounds=bounds - ) - else: - opt_result = self.optimizer.minimize( - fun=energy_evaluation, x0=initial_point, jac=gradient, bounds=bounds - ) - - eval_time = time() - start_time - - result = VQEResult() - result.optimal_point = opt_result.x - result.optimal_parameters = dict(zip(self.ansatz.parameters, opt_result.x)) - result.optimal_value = opt_result.fun - result.cost_function_evals = opt_result.nfev - result.optimizer_time = eval_time - result.eigenvalue = opt_result.fun + 0j - result.eigenstate = self._get_eigenstate(result.optimal_parameters) - - logger.info( - "Optimization complete in %s seconds.\nFound opt_params %s in %s evals", - eval_time, - result.optimal_point, - self._eval_count, - ) - - # TODO delete as soon as get_optimal_vector etc are removed - self._ret = result - - if aux_operators is not None: - bound_ansatz = self.ansatz.assign_parameters(result.optimal_point) - - aux_values = eval_observables( - self.quantum_instance, bound_ansatz, aux_operators, expectation=expectation - ) - result.aux_operator_eigenvalues = aux_values - - return result - - def get_energy_evaluation( - self, - operator: OperatorBase, - return_expectation: bool = False, - ) -> Callable[[np.ndarray], float | list[float]] | tuple[ - Callable[[np.ndarray], float | list[float]], ExpectationBase - ]: - """Returns a function handle to evaluates the energy at given parameters for the ansatz. - - This is the objective function to be passed to the optimizer that is used for evaluation. - - Args: - operator: The operator whose energy to evaluate. - return_expectation: If True, return the ``ExpectationBase`` expectation converter used - in the construction of the expectation value. Useful e.g. to evaluate other - operators with the same expectation value converter. - - - Returns: - Energy of the hamiltonian of each parameter, and, optionally, the expectation - converter. - - Raises: - RuntimeError: If the circuit is not parameterized (i.e. has 0 free parameters). - - """ - num_parameters = self.ansatz.num_parameters - if num_parameters == 0: - raise RuntimeError("The ansatz must be parameterized, but has 0 free parameters.") - - ansatz_params = self.ansatz.parameters - expect_op, expectation = self.construct_expectation( - ansatz_params, operator, return_expectation=True - ) - - def energy_evaluation(parameters): - parameter_sets = np.reshape(parameters, (-1, num_parameters)) - # Create dict associating each parameter with the lists of parameterization values for it - param_bindings = dict(zip(ansatz_params, parameter_sets.transpose().tolist())) - - start_time = time() - sampled_expect_op = self._circuit_sampler.convert(expect_op, params=param_bindings) - means = np.real(sampled_expect_op.eval()) - - if self._callback is not None: - variance = np.real(expectation.compute_variance(sampled_expect_op)) - estimator_error = np.sqrt(variance / self.quantum_instance.run_config.shots) - for i, param_set in enumerate(parameter_sets): - self._eval_count += 1 - self._callback(self._eval_count, param_set, means[i], estimator_error[i]) - else: - self._eval_count += len(means) - - end_time = time() - logger.info( - "Energy evaluation returned %s - %.5f (ms), eval count: %s", - means, - (end_time - start_time) * 1000, - self._eval_count, - ) - - return means if len(means) > 1 else means[0] - - if return_expectation: - return energy_evaluation, expectation - - return energy_evaluation - - def _get_eigenstate(self, optimal_parameters) -> list[float] | dict[str, int]: - """Get the simulation outcome of the ansatz, provided with parameters.""" - optimal_circuit = self.ansatz.assign_parameters(optimal_parameters) - state_fn = self._circuit_sampler.convert(StateFn(optimal_circuit)).eval() - if self.quantum_instance.is_statevector: - state = state_fn.primitive.data # VectorStateFn -> Statevector -> np.array - else: - state = state_fn.to_dict_fn().primitive # SparseVectorStateFn -> DictStateFn -> dict - - return state - - -class VQEResult(VariationalResult, MinimumEigensolverResult): - """Deprecated: VQE Result. - - The VQEResult class has been superseded by the - :class:`qiskit.algorithms.minimum_eigensolvers.VQEResult` class. - This class will be deprecated in a future release and subsequently - removed after that. - - """ - - @deprecate_func( - additional_msg=( - "Instead, use the class ``qiskit.algorithms.minimum_eigensolvers.VQEResult``. " - "See https://qisk.it/algo_migration for a migration guide." - ), - since="0.24.0", - ) - def __init__(self) -> None: - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__init__() - self._cost_function_evals: int | None = None - - @property - def cost_function_evals(self) -> int | None: - """Returns number of cost optimizer evaluations""" - return self._cost_function_evals - - @cost_function_evals.setter - def cost_function_evals(self, value: int) -> None: - """Sets number of cost function evaluations""" - self._cost_function_evals = value - - @property - def eigenstate(self) -> np.ndarray | None: - """return eigen state""" - return self._eigenstate - - @eigenstate.setter - def eigenstate(self, value: np.ndarray) -> None: - """set eigen state""" - self._eigenstate = value - - -def _validate_initial_point(point, ansatz): - expected_size = ansatz.num_parameters - - # try getting the initial point from the ansatz - if point is None and hasattr(ansatz, "preferred_init_points"): - point = ansatz.preferred_init_points - # if the point is None choose a random initial point - - if point is None: - # get bounds if ansatz has them set, otherwise use [-2pi, 2pi] for each parameter - bounds = getattr(ansatz, "parameter_bounds", None) - if bounds is None: - bounds = [(-2 * np.pi, 2 * np.pi)] * expected_size - - # replace all Nones by [-2pi, 2pi] - lower_bounds = [] - upper_bounds = [] - for lower, upper in bounds: - lower_bounds.append(lower if lower is not None else -2 * np.pi) - upper_bounds.append(upper if upper is not None else 2 * np.pi) - - # sample from within bounds - point = algorithm_globals.random.uniform(lower_bounds, upper_bounds) - - elif len(point) != expected_size: - raise ValueError( - f"The dimension of the initial point ({len(point)}) does not match the " - f"number of parameters in the circuit ({expected_size})." - ) - - return point - - -def _validate_bounds(ansatz): - if hasattr(ansatz, "parameter_bounds") and ansatz.parameter_bounds is not None: - bounds = ansatz.parameter_bounds - if len(bounds) != ansatz.num_parameters: - raise ValueError( - f"The number of bounds ({len(bounds)}) does not match the number of " - f"parameters in the circuit ({ansatz.num_parameters})." - ) - else: - bounds = [(None, None)] * ansatz.num_parameters - - return bounds diff --git a/qiskit/algorithms/minimum_eigensolvers/__init__.py b/qiskit/algorithms/minimum_eigensolvers/__init__.py deleted file mode 100644 index d7406c09860b..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/__init__.py +++ /dev/null @@ -1,66 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -============================================================================ -Minimum Eigensolvers Package (:mod:`qiskit.algorithms.minimum_eigensolvers`) -============================================================================ - -.. currentmodule:: qiskit.algorithms.minimum_eigensolvers - -Minimum Eigensolvers -==================== -.. autosummary:: - :toctree: ../stubs/ - - MinimumEigensolver - NumPyMinimumEigensolver - VQE - AdaptVQE - SamplingMinimumEigensolver - SamplingVQE - QAOA - -.. autosummary:: - :toctree: ../stubs/ - - MinimumEigensolverResult - NumPyMinimumEigensolverResult - VQEResult - AdaptVQEResult - SamplingMinimumEigensolverResult - SamplingVQEResult -""" - -from .adapt_vqe import AdaptVQE, AdaptVQEResult -from .minimum_eigensolver import MinimumEigensolver, MinimumEigensolverResult -from .numpy_minimum_eigensolver import NumPyMinimumEigensolver, NumPyMinimumEigensolverResult -from .vqe import VQE, VQEResult -from .sampling_mes import SamplingMinimumEigensolver, SamplingMinimumEigensolverResult -from .sampling_vqe import SamplingVQE, SamplingVQEResult -from .qaoa import QAOA - -__all__ = [ - "AdaptVQE", - "AdaptVQEResult", - "MinimumEigensolver", - "MinimumEigensolverResult", - "NumPyMinimumEigensolver", - "NumPyMinimumEigensolverResult", - "VQE", - "VQEResult", - "SamplingMinimumEigensolver", - "SamplingMinimumEigensolverResult", - "SamplingVQE", - "SamplingVQEResult", - "QAOA", -] diff --git a/qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py b/qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py deleted file mode 100644 index 4398d63e5107..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py +++ /dev/null @@ -1,419 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""An implementation of the AdaptVQE algorithm.""" -from __future__ import annotations - -from collections.abc import Sequence -from enum import Enum - -import re -import logging -import warnings -from typing import Any - -import numpy as np - -from qiskit import QiskitError -from qiskit.algorithms.list_or_dict import ListOrDict -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.opflow import OperatorBase, PauliSumOp -from qiskit.circuit.library import EvolvedOperatorAnsatz -from qiskit.utils.deprecation import deprecate_arg, deprecate_func -from qiskit.utils.validation import validate_min - -from .minimum_eigensolver import MinimumEigensolver -from .vqe import VQE, VQEResult -from ..observables_evaluator import estimate_observables -from ..variational_algorithm import VariationalAlgorithm - - -logger = logging.getLogger(__name__) - - -class TerminationCriterion(Enum): - """A class enumerating the various finishing criteria.""" - - CONVERGED = "Threshold converged" - CYCLICITY = "Aborted due to a cyclic selection of evolution operators" - MAXIMUM = "Maximum number of iterations reached" - - -class AdaptVQE(VariationalAlgorithm, MinimumEigensolver): - """The Adaptive Variational Quantum Eigensolver algorithm. - - `AdaptVQE `__ is a quantum algorithm which creates a compact - ansatz from a set of evolution operators. It iteratively extends the ansatz circuit, by - selecting the building block that leads to the largest gradient from a set of candidates. In - chemistry, this is usually a list of orbital excitations. Thus, a common choice of ansatz to be - used with this algorithm is the Unitary Coupled Cluster ansatz implemented in Qiskit Nature. - This results in a wavefunction ansatz which is uniquely adapted to the operator whose minimum - eigenvalue is being determined. This class relies on a supplied instance of :class:`~.VQE` to - find the minimum eigenvalue. The performance of AdaptVQE significantly depends on the - minimization routine. - - .. code-block:: python - - from qiskit.algorithms.minimum_eigensolvers import AdaptVQE, VQE - from qiskit.algorithms.optimizers import SLSQP - from qiskit.primitives import Estimator - from qiskit.circuit.library import EvolvedOperatorAnsatz - - # get your Hamiltonian - hamiltonian = ... - - # construct your ansatz - ansatz = EvolvedOperatorAnsatz(...) - - vqe = VQE(Estimator(), ansatz, SLSQP()) - - adapt_vqe = AdaptVQE(vqe) - - eigenvalue, _ = adapt_vqe.compute_minimum_eigenvalue(hamiltonian) - - The following attributes can be set via the initializer but can also be read and updated once - the AdaptVQE object has been constructed. - - Attributes: - solver: a :class:`~.VQE` instance used internally to compute the minimum eigenvalues. - It is a requirement that the :attr:`~.VQE.ansatz` of this solver is of type - :class:`~qiskit.circuit.library.EvolvedOperatorAnsatz`. - gradient_threshold: once all gradients have an absolute value smaller than this threshold, - the algorithm has converged and terminates. - eigenvalue_threshold: once the eigenvalue has changed by less than this threshold from one - iteration to the next, the algorithm has converged and terminates. When this case - occurs, the excitation included in the final iteration did not result in a significant - improvement of the eigenvalue and, thus, the results from this iteration are not - considered. - max_iterations: the maximum number of iterations for the adaptive loop. If ``None``, the - algorithm is not bound in its number of iterations. - """ - - @deprecate_arg( - "threshold", - since="0.24.0", - pending=True, - new_alias="gradient_threshold", - ) - def __init__( - self, - solver: VQE, - *, - gradient_threshold: float = 1e-5, - eigenvalue_threshold: float = 1e-5, - max_iterations: int | None = None, - threshold: float | None = None, # pylint: disable=unused-argument - ) -> None: - """ - Args: - solver: a :class:`~.VQE` instance used internally to compute the minimum eigenvalues. - It is a requirement that the :attr:`~.VQE.ansatz` of this solver is of type - :class:`~qiskit.circuit.library.EvolvedOperatorAnsatz`. - gradient_threshold: once all gradients have an absolute value smaller than this - threshold, the algorithm has converged and terminates. - eigenvalue_threshold: once the eigenvalue has changed by less than this threshold from - one iteration to the next, the algorithm has converged and terminates. When this - case occurs, the excitation included in the final iteration did not result in a - significant improvement of the eigenvalue and, thus, the results from this iteration - are not considered. - max_iterations: the maximum number of iterations for the adaptive loop. If ``None``, the - algorithm is not bound in its number of iterations. - threshold: once all gradients have an absolute value smaller than this threshold, the - algorithm has converged and terminates. Defaults to ``1e-5``. - """ - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - validate_min("gradient_threshold", gradient_threshold, 1e-15) - validate_min("eigenvalue_threshold", eigenvalue_threshold, 1e-15) - - self.solver = solver - self.gradient_threshold = gradient_threshold - self.eigenvalue_threshold = eigenvalue_threshold - self.max_iterations = max_iterations - self._tmp_ansatz: EvolvedOperatorAnsatz | None = None - self._excitation_pool: list[OperatorBase] = [] - self._excitation_list: list[OperatorBase] = [] - - @property - @deprecate_func( - since="0.24.0", - pending=True, - is_property=True, - additional_msg="Instead, use the gradient_threshold attribute.", - ) - def threshold(self) -> float: - """The threshold for the gradients. - - Once all gradients have an absolute value smaller than this threshold, the algorithm has - converged and terminates. - """ - return self.gradient_threshold - - @threshold.setter - @deprecate_func( - since="0.24.0", - pending=True, - is_property=True, - additional_msg="Instead, use the gradient_threshold attribute.", - ) - def threshold(self, threshold: float) -> None: - self.gradient_threshold = threshold - - @property - def initial_point(self) -> Sequence[float] | None: - """Returns the initial point of the internal :class:`~.VQE` solver.""" - return self.solver.initial_point - - @initial_point.setter - def initial_point(self, value: Sequence[float] | None) -> None: - """Sets the initial point of the internal :class:`~.VQE` solver.""" - self.solver.initial_point = value - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - def _compute_gradients( - self, - theta: list[float], - operator: BaseOperator | OperatorBase, - ) -> list[tuple[complex, dict[str, Any]]]: - """ - Computes the gradients for all available excitation operators. - - Args: - theta: List of (up to now) optimal parameters. - operator: operator whose gradient needs to be computed. - Returns: - List of pairs consisting of the computed gradient and excitation operator. - """ - # The excitations operators are applied later as exp(i*theta*excitation). - # For this commutator, we need to explicitly pull in the imaginary phase. - commutators = [1j * (operator @ exc - exc @ operator) for exc in self._excitation_pool] - res = estimate_observables(self.solver.estimator, self.solver.ansatz, commutators, theta) - return res - - @staticmethod - def _check_cyclicity(indices: list[int]) -> bool: - """ - Auxiliary function to check for cycles in the indices of the selected excitations. - - Args: - indices: The list of chosen gradient indices. - - Returns: - Whether repeating sequences of indices have been detected. - """ - cycle_regex = re.compile(r"(\b.+ .+\b)( \b\1\b)+") - # reg-ex explanation: - # 1. (\b.+ .+\b) will match at least two numbers and try to match as many as possible. The - # word boundaries in the beginning and end ensure that now numbers are split into digits. - # 2. the match of this part is placed into capture group 1 - # 3. ( \b\1\b)+ will match a space followed by the contents of capture group 1 (again - # delimited by word boundaries to avoid separation into digits). - # -> this results in any sequence of at least two numbers being detected - match = cycle_regex.search(" ".join(map(str, indices))) - logger.debug("Cycle detected: %s", match) - # Additionally we also need to check whether the last two numbers are identical, because the - # reg-ex above will only find cycles of at least two consecutive numbers. - # It is sufficient to assert that the last two numbers are different due to the iterative - # nature of the algorithm. - return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) - - def compute_minimum_eigenvalue( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> AdaptVQEResult: - """Computes the minimum eigenvalue. - - Args: - operator: Operator whose minimum eigenvalue we want to find. - aux_operators: Additional auxiliary operators to evaluate. - - Raises: - TypeError: If an ansatz other than :class:`~.EvolvedOperatorAnsatz` is provided. - QiskitError: If all evaluated gradients lie below the convergence threshold in the first - iteration of the algorithm. - - Returns: - An :class:`~.AdaptVQEResult` which is a :class:`~.VQEResult` but also but also - includes runtime information about the AdaptVQE algorithm like the number of iterations, - termination criterion, and the final maximum gradient. - """ - if not isinstance(self.solver.ansatz, EvolvedOperatorAnsatz): - raise TypeError("The AdaptVQE ansatz must be of the EvolvedOperatorAnsatz type.") - - # Overwrite the solver's ansatz with the initial state - self._tmp_ansatz = self.solver.ansatz - self._excitation_pool = self._tmp_ansatz.operators - self.solver.ansatz = self._tmp_ansatz.initial_state - - prev_op_indices: list[int] = [] - prev_raw_vqe_result: VQEResult | None = None - raw_vqe_result: VQEResult | None = None - theta: list[float] = [] - max_grad: tuple[complex, dict[str, Any] | None] = (0.0, None) - self._excitation_list = [] - history: list[complex] = [] - iteration = 0 - while self.max_iterations is None or iteration < self.max_iterations: - iteration += 1 - logger.info("--- Iteration #%s ---", str(iteration)) - # compute gradients - logger.debug("Computing gradients") - cur_grads = self._compute_gradients(theta, operator) - # pick maximum gradient - max_grad_index, max_grad = max( - enumerate(cur_grads), key=lambda item: np.abs(item[1][0]) - ) - logger.info( - "Found maximum gradient %s at index %s", - str(np.abs(max_grad[0])), - str(max_grad_index), - ) - # log gradients - if np.abs(max_grad[0]) < self.gradient_threshold: - if iteration == 1: - raise QiskitError( - "All gradients have been evaluated to lie below the convergence threshold " - "during the first iteration of the algorithm. Try to either tighten the " - "convergence threshold or pick a different ansatz." - ) - logger.info( - "AdaptVQE terminated successfully with a final maximum gradient: %s", - str(np.abs(max_grad[0])), - ) - termination_criterion = TerminationCriterion.CONVERGED - break - # store maximum gradient's index for cycle detection - prev_op_indices.append(max_grad_index) - # check indices of picked gradients for cycles - if self._check_cyclicity(prev_op_indices): - logger.info("Alternating sequence found. Finishing.") - logger.info("Final maximum gradient: %s", str(np.abs(max_grad[0]))) - termination_criterion = TerminationCriterion.CYCLICITY - break - # add new excitation to self._ansatz - logger.info( - "Adding new operator to the ansatz: %s", str(self._excitation_pool[max_grad_index]) - ) - self._excitation_list.append(self._excitation_pool[max_grad_index]) - theta.append(0.0) - # setting up the ansatz for the VQE iteration - self._tmp_ansatz.operators = self._excitation_list - self.solver.ansatz = self._tmp_ansatz - self.solver.initial_point = theta - # evaluating the eigenvalue with the internal VQE - prev_raw_vqe_result = raw_vqe_result - raw_vqe_result = self.solver.compute_minimum_eigenvalue(operator) - theta = raw_vqe_result.optimal_point.tolist() - # checking convergence based on the change in eigenvalue - if iteration > 1: - eigenvalue_diff = np.abs(raw_vqe_result.eigenvalue - history[-1]) - if eigenvalue_diff < self.eigenvalue_threshold: - logger.info( - "AdaptVQE terminated successfully with a final change in eigenvalue: %s", - str(eigenvalue_diff), - ) - termination_criterion = TerminationCriterion.CONVERGED - logger.debug( - "Reverting the addition of the last excitation to the ansatz since it " - "resulted in a change of the eigenvalue below the configured threshold." - ) - self._excitation_list.pop() - theta.pop() - self._tmp_ansatz.operators = self._excitation_list - self.solver.ansatz = self._tmp_ansatz - self.solver.initial_point = theta - raw_vqe_result = prev_raw_vqe_result - break - # appending the computed eigenvalue to the tracking history - history.append(raw_vqe_result.eigenvalue) - logger.info("Current eigenvalue: %s", str(raw_vqe_result.eigenvalue)) - else: - # reached maximum number of iterations - termination_criterion = TerminationCriterion.MAXIMUM - logger.info("Maximum number of iterations reached. Finishing.") - logger.info("Final maximum gradient: %s", str(np.abs(max_grad[0]))) - - result = AdaptVQEResult() - result.combine(raw_vqe_result) - result.num_iterations = iteration - result.final_max_gradient = max_grad[0] - result.termination_criterion = termination_criterion - result.eigenvalue_history = history - - # once finished evaluate auxiliary operators if any - if aux_operators is not None: - aux_values = estimate_observables( - self.solver.estimator, self.solver.ansatz, aux_operators, result.optimal_point - ) - result.aux_operators_evaluated = aux_values - - logger.info("The final eigenvalue is: %s", str(result.eigenvalue)) - self.solver.ansatz.operators = self._excitation_pool - return result - - -class AdaptVQEResult(VQEResult): - """AdaptVQE Result.""" - - def __init__(self) -> None: - super().__init__() - self._num_iterations: int | None = None - self._final_max_gradient: float | None = None - self._termination_criterion: str = "" - self._eigenvalue_history: list[float] | None = None - - @property - def num_iterations(self) -> int: - """Returns the number of iterations.""" - return self._num_iterations - - @num_iterations.setter - def num_iterations(self, value: int) -> None: - """Sets the number of iterations.""" - self._num_iterations = value - - @property - def final_max_gradient(self) -> float: - """Returns the final maximum gradient.""" - return self._final_max_gradient - - @final_max_gradient.setter - def final_max_gradient(self, value: float) -> None: - """Sets the final maximum gradient.""" - self._final_max_gradient = value - - @property - def termination_criterion(self) -> str: - """Returns the termination criterion.""" - return self._termination_criterion - - @termination_criterion.setter - def termination_criterion(self, value: str) -> None: - """Sets the termination criterion.""" - self._termination_criterion = value - - @property - def eigenvalue_history(self) -> list[float]: - """Returns the history of computed eigenvalues. - - The history's length matches the number of iterations and includes the final computed value. - """ - return self._eigenvalue_history - - @eigenvalue_history.setter - def eigenvalue_history(self, eigenvalue_history: list[float]) -> None: - """Sets the history of computed eigenvalues.""" - self._eigenvalue_history = eigenvalue_history diff --git a/qiskit/algorithms/minimum_eigensolvers/diagonal_estimator.py b/qiskit/algorithms/minimum_eigensolvers/diagonal_estimator.py deleted file mode 100644 index 40354c884d98..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/diagonal_estimator.py +++ /dev/null @@ -1,199 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Expectation value for a diagonal observable using a sampler primitive.""" - -from __future__ import annotations - -from collections.abc import Callable, Sequence, Mapping -from typing import Any - -from dataclasses import dataclass - -import numpy as np -from qiskit.algorithms.algorithm_job import AlgorithmJob -from qiskit.circuit import QuantumCircuit -from qiskit.primitives import BaseSampler, BaseEstimator, EstimatorResult -from qiskit.primitives.utils import init_observable, _circuit_key -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info import SparsePauliOp -from qiskit.quantum_info.operators.base_operator import BaseOperator - - -@dataclass(frozen=True) -class _DiagonalEstimatorResult(EstimatorResult): - """A result from an expectation of a diagonal observable.""" - - # TODO make each measurement a dataclass rather than a dict - best_measurements: Sequence[Mapping[str, Any]] | None = None - - -class _DiagonalEstimator(BaseEstimator): - """An estimator for diagonal observables.""" - - def __init__( - self, - sampler: BaseSampler, - aggregation: float | Callable[[Sequence[tuple[float, float]]], float] | None = None, - callback: Callable[[Sequence[Mapping[str, Any]]], None] | None = None, - **options, - ) -> None: - r"""Evaluate the expectation of quantum state with respect to a diagonal operator. - - Args: - sampler: The sampler used to evaluate the circuits. - aggregation: The aggregation function to aggregate the measurement outcomes. If a float - this specified the CVaR :math:`\alpha` parameter. - callback: A callback which is given the best measurements of all circuits in each - evaluation. - run_options: Options for the sampler. - - """ - super().__init__(options=options) - self.sampler = sampler - if not callable(aggregation): - aggregation = _get_cvar_aggregation(aggregation) - - self.aggregation = aggregation - self.callback = callback - self._circuit_ids = {} - self._observable_ids = {} - - def _run( - self, - circuits: Sequence[QuantumCircuit], - observables: Sequence[BaseOperator | PauliSumOp], - parameter_values: Sequence[Sequence[float]], - **run_options, - ) -> AlgorithmJob: - circuit_indices = [] - for circuit in circuits: - key = _circuit_key(circuit) - index = self._circuit_ids.get(key) - if index is not None: - circuit_indices.append(index) - else: - circuit_indices.append(len(self._circuits)) - self._circuit_ids[key] = len(self._circuits) - self._circuits.append(circuit) - self._parameters.append(circuit.parameters) - observable_indices = [] - for observable in observables: - index = self._observable_ids.get(id(observable)) - if index is not None: - observable_indices.append(index) - else: - observable_indices.append(len(self._observables)) - self._observable_ids[id(observable)] = len(self._observables) - converted_observable = init_observable(observable) - _check_observable_is_diagonal(converted_observable) # check it's diagonal - self._observables.append(converted_observable) - job = AlgorithmJob( - self._call, circuit_indices, observable_indices, parameter_values, **run_options - ) - job.submit() - return job - - def _call( - self, - circuits: Sequence[int], - observables: Sequence[int], - parameter_values: Sequence[Sequence[float]], - **run_options, - ) -> _DiagonalEstimatorResult: - job = self.sampler.run( - [self._circuits[i] for i in circuits], - parameter_values, - **run_options, - ) - sampler_result = job.result() - samples = sampler_result.quasi_dists - - # a list of dictionaries containing: {state: (measurement probability, value)} - evaluations = [ - { - state: (probability, _evaluate_sparsepauli(state, self._observables[i])) - for state, probability in sampled.items() - } - for i, sampled in zip(observables, samples) - ] - - results = np.array([self.aggregation(evaluated.values()) for evaluated in evaluations]) - - # get the best measurements - best_measurements = [] - num_qubits = self._circuits[0].num_qubits - for evaluated in evaluations: - best_result = min(evaluated.items(), key=lambda x: x[1][1]) - best_measurements.append( - { - "state": best_result[0], - "bitstring": bin(best_result[0])[2:].zfill(num_qubits), - "value": best_result[1][1], - "probability": best_result[1][0], - } - ) - - if self.callback is not None: - self.callback(best_measurements) - - return _DiagonalEstimatorResult( - values=results, metadata=sampler_result.metadata, best_measurements=best_measurements - ) - - -def _get_cvar_aggregation(alpha): - """Get the aggregation function for CVaR with confidence level ``alpha``.""" - if alpha is None: - alpha = 1 - elif not 0 <= alpha <= 1: - raise ValueError(f"alpha must be in [0, 1] but was {alpha}") - - # if alpha is close to 1 we can avoid the sorting - if np.isclose(alpha, 1): - - def aggregate(measurements): - return sum(probability * value for probability, value in measurements) - - else: - - def aggregate(measurements): - # sort by values - sorted_measurements = sorted(measurements, key=lambda x: x[1]) - - accumulated_percent = 0 # once alpha is reached, stop - cvar = 0 - for probability, value in sorted_measurements: - cvar += value * min(probability, alpha - accumulated_percent) - accumulated_percent += probability - if accumulated_percent >= alpha: - break - - return cvar / alpha - - return aggregate - - -_PARITY = np.array([-1 if bin(i).count("1") % 2 else 1 for i in range(256)], dtype=np.complex128) - - -def _evaluate_sparsepauli(state: int, observable: SparsePauliOp) -> complex: - packed_uint8 = np.packbits(observable.paulis.z, axis=1, bitorder="little") - state_bytes = np.frombuffer(state.to_bytes(packed_uint8.shape[1], "little"), dtype=np.uint8) - reduced = np.bitwise_xor.reduce(packed_uint8 & state_bytes, axis=1) - return np.sum(observable.coeffs * _PARITY[reduced]) - - -def _check_observable_is_diagonal(observable: SparsePauliOp) -> None: - is_diagonal = not np.any(observable.paulis.x) - if not is_diagonal: - raise ValueError("The observable must be diagonal.") diff --git a/qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py b/qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py deleted file mode 100644 index 26087c053aef..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py +++ /dev/null @@ -1,97 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The minimum eigensolver interface and result.""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Any - -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..algorithm_result import AlgorithmResult -from ..list_or_dict import ListOrDict - - -class MinimumEigensolver(ABC): - """The minimum eigensolver interface. - - Algorithms that can compute a minimum eigenvalue for an operator may implement this interface to - allow different algorithms to be used interchangeably. - """ - - @abstractmethod - def compute_minimum_eigenvalue( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> "MinimumEigensolverResult": - """ - Computes the minimum eigenvalue. The ``operator`` and ``aux_operators`` are supplied here. - While an ``operator`` is required by algorithms, ``aux_operators`` are optional. - - Args: - operator: Qubit operator of the observable. - aux_operators: Optional list of auxiliary operators to be evaluated with the - parameters of the minimum eigenvalue main result and their expectation values - returned. For instance in chemistry these can be dipole operators and total particle - count operators, so we can get values for these at the ground state. - - Returns: - A minimum eigensolver result. - """ - return MinimumEigensolverResult() - - @classmethod - def supports_aux_operators(cls) -> bool: - """Whether computing the expectation value of auxiliary operators is supported. - - If the minimum eigensolver computes an eigenvalue of the main ``operator`` then it can - compute the expectation value of the ``aux_operators`` for that state. Otherwise they will - be ignored. - - Returns: - True if aux_operator expectations can be evaluated, False otherwise - """ - return False - - -class MinimumEigensolverResult(AlgorithmResult): - """Minimum eigensolver result.""" - - def __init__(self) -> None: - super().__init__() - self._eigenvalue: complex | None = None - self._aux_operators_evaluated: ListOrDict[tuple[complex, dict[str, Any]]] | None = None - - @property - def eigenvalue(self) -> complex | None: - """The computed minimum eigenvalue.""" - return self._eigenvalue - - @eigenvalue.setter - def eigenvalue(self, value: complex) -> None: - self._eigenvalue = value - - @property - def aux_operators_evaluated(self) -> ListOrDict[tuple[complex, dict[str, Any]]] | None: - """The aux operator expectation values. - - These values are in fact tuples formatted as (mean, (variance, shots)). - """ - return self._aux_operators_evaluated - - @aux_operators_evaluated.setter - def aux_operators_evaluated(self, value: ListOrDict[tuple[complex, dict[str, Any]]]) -> None: - self._aux_operators_evaluated = value diff --git a/qiskit/algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py b/qiskit/algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py deleted file mode 100644 index 93dc328b282c..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py +++ /dev/null @@ -1,106 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The NumPy minimum eigensolver algorithm and result.""" - -from __future__ import annotations - -from typing import Callable, List, Union, Optional -import logging -import numpy as np - -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info import Statevector -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..eigensolvers.numpy_eigensolver import NumPyEigensolver -from .minimum_eigensolver import MinimumEigensolver, MinimumEigensolverResult -from ..list_or_dict import ListOrDict - -logger = logging.getLogger(__name__) - -# future type annotations not supported in type aliases in 3.8 -FilterType = Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool] - - -class NumPyMinimumEigensolver(MinimumEigensolver): - """ - The NumPy minimum eigensolver algorithm. - """ - - def __init__( - self, - filter_criterion: FilterType | None = None, - ) -> None: - """ - Args: - filter_criterion: Callable that allows to filter eigenvalues/eigenstates. The minimum - eigensolver is only searching over feasible states and returns an eigenstate that - has the smallest eigenvalue among feasible states. The callable has the signature - ``filter(eigenstate, eigenvalue, aux_values)`` and must return a boolean to indicate - whether to consider this value or not. If there is no feasible element, the result - can even be empty. - """ - self._eigensolver = NumPyEigensolver(filter_criterion=filter_criterion) - - @property - def filter_criterion( - self, - ) -> FilterType | None: - """Returns the criterion for filtering eigenstates/eigenvalues.""" - return self._eigensolver.filter_criterion - - @filter_criterion.setter - def filter_criterion( - self, - filter_criterion: FilterType, - ) -> None: - self._eigensolver.filter_criterion = filter_criterion - - @classmethod - def supports_aux_operators(cls) -> bool: - return NumPyEigensolver.supports_aux_operators() - - def compute_minimum_eigenvalue( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> NumPyMinimumEigensolverResult: - super().compute_minimum_eigenvalue(operator, aux_operators) - eigensolver_result = self._eigensolver.compute_eigenvalues(operator, aux_operators) - result = NumPyMinimumEigensolverResult() - if eigensolver_result.eigenvalues is not None and len(eigensolver_result.eigenvalues) > 0: - result.eigenvalue = eigensolver_result.eigenvalues[0] - result.eigenstate = eigensolver_result.eigenstates[0] - if eigensolver_result.aux_operators_evaluated: - result.aux_operators_evaluated = eigensolver_result.aux_operators_evaluated[0] - - logger.debug("NumPy minimum eigensolver result: %s", result) - - return result - - -class NumPyMinimumEigensolverResult(MinimumEigensolverResult): - """NumPy minimum eigensolver result.""" - - def __init__(self) -> None: - super().__init__() - self._eigenstate: Statevector | None = None - - @property - def eigenstate(self) -> Statevector | None: - """Returns the eigenstate corresponding to the computed minimum eigenvalue.""" - return self._eigenstate - - @eigenstate.setter - def eigenstate(self, value: Statevector) -> None: - self._eigenstate = value diff --git a/qiskit/algorithms/minimum_eigensolvers/qaoa.py b/qiskit/algorithms/minimum_eigensolvers/qaoa.py deleted file mode 100644 index 825d4fa64cc7..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/qaoa.py +++ /dev/null @@ -1,144 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The quantum approximate optimization algorithm.""" - -from __future__ import annotations - -import warnings -from typing import Callable, Any -import numpy as np - -from qiskit.algorithms.optimizers import Minimizer, Optimizer -from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library.n_local.qaoa_ansatz import QAOAAnsatz -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseSampler -from qiskit.utils.validation import validate_min - -from .sampling_vqe import SamplingVQE - - -class QAOA(SamplingVQE): - r""" - The Quantum Approximate Optimization Algorithm (QAOA). - - QAOA is a well-known algorithm for finding approximate solutions to combinatorial-optimization - problems [1]. - - The QAOA implementation directly extends :class:`.SamplingVQE` and inherits its optimization - structure. However, unlike VQE, which can be configured with arbitrary ansatzes, QAOA uses its - own fine-tuned ansatz, which comprises :math:`p` parameterized global :math:`x` rotations and - :math:`p` different parameterizations of the problem hamiltonian. QAOA is thus principally - configured by the single integer parameter, ``reps``, which dictates the depth of the ansatz, - and thus affects the approximation quality. - - An optional array of :math:`2p` parameter values, as the :attr:`initial_point`, may be provided - as the starting :math:`\beta` and :math:`\gamma` parameters for the QAOA ansatz [1]. - - An operator or a parameterized quantum circuit may optionally also be provided as a custom - :attr:`mixer` Hamiltonian. This allows in the case of quantum annealing [2] and QAOA [3], to run - constrained optimization problems where the mixer constrains the evolution to a feasible - subspace of the full Hilbert space. - - The following attributes can be set via the initializer but can also be read and updated once - the QAOA object has been constructed. - - Attributes: - sampler (BaseSampler): The sampler primitive to sample the circuits. - optimizer (Optimizer | Minimizer): A classical optimizer to find the minimum energy. This - can either be a Qiskit :class:`.Optimizer` or a callable implementing the - :class:`.Minimizer` protocol. - reps (int): The integer parameter :math:`p`. Has a minimum valid value of 1. - initial_state: An optional initial state to prepend the QAOA circuit with. - mixer (QuantumCircuit | BaseOperator | PauliSumOp): The mixer Hamiltonian to evolve with or - a custom quantum circuit. Allows support of optimizations in constrained subspaces [2, - 3] as well as warm-starting the optimization [4]. - aggregation (float | Callable[[list[float]], float] | None): A float or callable to specify - how the objective function evaluated on the basis states should be aggregated. If a - float, this specifies the :math:`\alpha \in [0,1]` parameter for a CVaR expectation - value. - callback (Callable[[int, np.ndarray, float, dict[str, Any]], None] | None): A callback - that can access the intermediate data at each optimization step. These data are: the - evaluation count, the optimizer parameters for the ansatz, the evaluated value, the - the metadata dictionary, and the best measurement. - - References: - [1]: Farhi, E., Goldstone, J., Gutmann, S., "A Quantum Approximate Optimization Algorithm" - `arXiv:1411.4028 `__ - [2]: Hen, I., Spedalieri, F. M., "Quantum Annealing for Constrained Optimization" - `PhysRevApplied.5.034007 `__ - [3]: Hadfield, S. et al, "From the Quantum Approximate Optimization Algorithm to a Quantum - Alternating Operator Ansatz" `arXiv:1709.03489 `__ - [4]: Egger, D. J., Marecek, J., Woerner, S., "Warm-starting quantum optimization" - `arXiv: 2009.10095 `__ - """ - - def __init__( - self, - sampler: BaseSampler, - optimizer: Optimizer | Minimizer, - *, - reps: int = 1, - initial_state: QuantumCircuit | None = None, - mixer: QuantumCircuit | BaseOperator | PauliSumOp = None, - initial_point: np.ndarray | None = None, - aggregation: float | Callable[[list[float]], float] | None = None, - callback: Callable[[int, np.ndarray, float, dict[str, Any]], None] | None = None, - ) -> None: - r""" - Args: - sampler: The sampler primitive to sample the circuits. - optimizer: A classical optimizer to find the minimum energy. This can either be a - Qiskit :class:`.Optimizer` or a callable implementing the :class:`.Minimizer` - protocol. - reps: The integer parameter :math:`p`. Has a minimum valid value of 1. - initial_state: An optional initial state to prepend the QAOA circuit with. - mixer: The mixer Hamiltonian to evolve with or a custom quantum circuit. Allows support - of optimizations in constrained subspaces [2, 3] as well as warm-starting the - optimization [4]. - initial_point: An optional initial point (i.e. initial parameter values) for the - optimizer. The length of the initial point must match the number of :attr:`ansatz` - parameters. If ``None``, a random point will be generated within certain parameter - bounds. ``QAOA`` will look to the ansatz for these bounds. If the ansatz does not - specify bounds, bounds of :math:`-2\pi`, :math:`2\pi` will be used. - aggregation: A float or callable to specify how the objective function evaluated on the - basis states should be aggregated. If a float, this specifies the :math:`\alpha \in - [0,1]` parameter for a CVaR expectation value. - callback: A callback that can access the intermediate data at each optimization step. - These data are: the evaluation count, the optimizer parameters for the ansatz, the - evaluated value, the metadata dictionary. - """ - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - validate_min("reps", reps, 1) - - self.reps = reps - self.mixer = mixer - self.initial_state = initial_state - self._cost_operator = None - - super().__init__( - sampler=sampler, - ansatz=None, - optimizer=optimizer, - initial_point=initial_point, - aggregation=aggregation, - callback=callback, - ) - - def _check_operator_ansatz(self, operator: BaseOperator | PauliSumOp): - # Recreates a circuit based on operator parameter. - self.ansatz = QAOAAnsatz( - operator, self.reps, initial_state=self.initial_state, mixer_operator=self.mixer - ).decompose() # TODO remove decompose once #6674 is fixed diff --git a/qiskit/algorithms/minimum_eigensolvers/sampling_mes.py b/qiskit/algorithms/minimum_eigensolvers/sampling_mes.py deleted file mode 100644 index e193f53ce15c..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/sampling_mes.py +++ /dev/null @@ -1,138 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Sampling Minimum Eigensolver interface.""" - -from __future__ import annotations -from abc import ABC, abstractmethod -from collections.abc import Mapping -from typing import Any - -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.opflow import PauliSumOp -from qiskit.result import QuasiDistribution -from ..algorithm_result import AlgorithmResult -from ..list_or_dict import ListOrDict - - -class SamplingMinimumEigensolver(ABC): - """The Sampling Minimum Eigensolver Interface.""" - - @abstractmethod - def compute_minimum_eigenvalue( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> "SamplingMinimumEigensolverResult": - """Compute the minimum eigenvalue of a diagonal operator. - - Args: - operator: Diagonal qubit operator. - aux_operators: Optional list of auxiliary operators to be evaluated with the - final state. - - Returns: - A :class:`~.SamplingMinimumEigensolverResult` containing the optimization result. - """ - pass - - @classmethod - def supports_aux_operators(cls) -> bool: - """Whether computing the expectation value of auxiliary operators is supported. - - If the minimum eigensolver computes an eigenstate of the main operator then it - can compute the expectation value of the aux_operators for that state. Otherwise - they will be ignored. - - Returns: - True if aux_operator expectations can be evaluated, False otherwise - """ - return False - - -class SamplingMinimumEigensolverResult(AlgorithmResult): - """Sampling Minimum Eigensolver Result. - - In contrast to the result of a :class:`~.MinimumEigenSolver`, this result also contains - the best measurement of the overall optimization and the samples of the final state. - """ - - def __init__(self) -> None: - super().__init__() - self._eigenvalue: complex | None = None - self._eigenstate: QuasiDistribution | None = None - self._aux_operator_values: ListOrDict[tuple[complex, dict[str, Any]]] | None = None - self._best_measurement: Mapping[str, Any] | None = None - - @property - def eigenvalue(self) -> complex | None: - """Return the approximation to the eigenvalue.""" - return self._eigenvalue - - @eigenvalue.setter - def eigenvalue(self, value: complex | None) -> None: - """Set the approximation to the eigenvalue.""" - self._eigenvalue = value - - @property - def eigenstate(self) -> QuasiDistribution | None: - """Return the quasi-distribution sampled from the final state. - - The ansatz is sampled when parameterized with the optimal parameters that where obtained - computing the minimum eigenvalue. The keys represent a measured classical value and the - value is a float for the quasi-probability of that result. - """ - return self._eigenstate - - @eigenstate.setter - def eigenstate(self, value: QuasiDistribution | None) -> None: - """Set the quasi-distribution sampled from the final state.""" - self._eigenstate = value - - @property - def aux_operators_evaluated(self) -> ListOrDict[tuple[complex, dict[str, Any]]] | None: - """Return aux operator expectation values and metadata. - - These are formatted as (mean, metadata). - """ - return self._aux_operator_values - - @aux_operators_evaluated.setter - def aux_operators_evaluated( - self, value: ListOrDict[tuple[complex, dict[str, Any]]] | None - ) -> None: - self._aux_operator_values = value - - @property - def best_measurement(self) -> Mapping[str, Any] | None: - """Return the best measurement over the entire optimization. - - Possesses keys: ``state``, ``bitstring``, ``value``, ``probability``. - """ - return self._best_measurement - - @best_measurement.setter - def best_measurement(self, value: Mapping[str, Any]) -> None: - """Set the best measurement over the entire optimization.""" - self._best_measurement = value - - def __str__(self) -> str: - """Return a string representation of the result.""" - disp = ( - "SamplingMinimumEigensolverResult:\n" - + f"\tEigenvalue: {self.eigenvalue}\n" - + f"\tBest measurement\n: {self.best_measurement}\n" - ) - if self.aux_operators_evaluated is not None: - disp += f"\n\tAuxiliary operator values: {self.aux_operators_evaluated}\n" - - return disp diff --git a/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py b/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py deleted file mode 100644 index 2fb60355b2a5..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/sampling_vqe.py +++ /dev/null @@ -1,382 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Variational Quantum Eigensolver algorithm, optimized for diagonal Hamiltonians.""" - -from __future__ import annotations - -from collections.abc import Callable, Sequence -import logging -from time import time -from typing import Any - -import numpy as np - -from qiskit.circuit import QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseSampler -from qiskit.result import QuasiDistribution -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..exceptions import AlgorithmError -from ..list_or_dict import ListOrDict -from ..optimizers import Minimizer, Optimizer, OptimizerResult -from ..variational_algorithm import VariationalAlgorithm, VariationalResult -from .diagonal_estimator import _DiagonalEstimator -from .sampling_mes import ( - SamplingMinimumEigensolver, - SamplingMinimumEigensolverResult, -) -from ..observables_evaluator import estimate_observables -from ..utils import validate_initial_point, validate_bounds - -# private function as we expect this to be updated in the next released -from ..utils.set_batching import _set_default_batchsize - - -logger = logging.getLogger(__name__) - - -class SamplingVQE(VariationalAlgorithm, SamplingMinimumEigensolver): - r"""The Variational Quantum Eigensolver algorithm, optimized for diagonal Hamiltonians. - - VQE is a hybrid quantum-classical algorithm that uses a variational technique to find the - minimum eigenvalue of a given diagonal Hamiltonian operator :math:`H_{\text{diag}}`. - - In contrast to the :class:`~qiskit.algorithms.minimum_eigensolvers.VQE` class, the - ``SamplingVQE`` algorithm is executed using a :attr:`sampler` primitive. - - An instance of ``SamplingVQE`` also requires an :attr:`ansatz`, a parameterized - :class:`.QuantumCircuit`, to prepare the trial state :math:`|\psi(\vec\theta)\rangle`. It also - needs a classical :attr:`optimizer` which varies the circuit parameters :math:`\vec\theta` to - minimize the objective function, which depends on the chosen :attr:`aggregation`. - - The optimizer can either be one of Qiskit's optimizers, such as - :class:`~qiskit.algorithms.optimizers.SPSA` or a callable with the following signature: - - .. code-block:: python - - from qiskit.algorithms.optimizers import OptimizerResult - - def my_minimizer(fun, x0, jac=None, bounds=None) -> OptimizerResult: - # Note that the callable *must* have these argument names! - # Args: - # fun (callable): the function to minimize - # x0 (np.ndarray): the initial point for the optimization - # jac (callable, optional): the gradient of the objective function - # bounds (list, optional): a list of tuples specifying the parameter bounds - - result = OptimizerResult() - result.x = # optimal parameters - result.fun = # optimal function value - return result - - The above signature also allows one to use any SciPy minimizer, for instance as - - .. code-block:: python - - from functools import partial - from scipy.optimize import minimize - - optimizer = partial(minimize, method="L-BFGS-B") - - The following attributes can be set via the initializer but can also be read and updated once - the ``SamplingVQE`` object has been constructed. - - Attributes: - sampler (BaseSampler): The sampler primitive to sample the circuits. - ansatz (QuantumCircuit): A parameterized quantum circuit to prepare the trial state. - optimizer (Optimizer | Minimizer): A classical optimizer to find the minimum energy. This - can either be a Qiskit :class:`.Optimizer` or a callable implementing the - :class:`.Minimizer` protocol. - aggregation (float | Callable[[list[tuple[float, complex]], float] | None): - A float or callable to specify how the objective function evaluated on the basis states - should be aggregated. If a float, this specifies the :math:`\alpha \in [0,1]` parameter - for a CVaR expectation value [1]. If a callable, it takes a list of basis state - measurements specified as ``[(probability, objective_value)]`` and return an objective - value as float. If None, all an ordinary expectation value is calculated. - callback (Callable[[int, np.ndarray, float, dict[str, Any]], None] | None): A callback that - can access the intermediate data at each optimization step. These data are: the - evaluation count, the optimizer parameters for the ansatz, the evaluated value, and the - metadata dictionary. - - References: - [1]: Barkoutsos, P. K., Nannicini, G., Robert, A., Tavernelli, I., and Woerner, S., - "Improving Variational Quantum Optimization using CVaR" - `arXiv:1907.04769 `_ - """ - - def __init__( - self, - sampler: BaseSampler, - ansatz: QuantumCircuit, - optimizer: Optimizer | Minimizer, - *, - initial_point: Sequence[float] | None = None, - aggregation: float | Callable[[list[float]], float] | None = None, - callback: Callable[[int, np.ndarray, float, dict[str, Any]], None] | None = None, - ) -> None: - r""" - Args: - sampler: The sampler primitive to sample the circuits. - ansatz: A parameterized quantum circuit to prepare the trial state. - optimizer: A classical optimizer to find the minimum energy. This can either be a Qiskit - :class:`.Optimizer` or a callable implementing the :class:`.Minimizer` protocol. - initial_point: An optional initial point (i.e. initial parameter values) for the - optimizer. The length of the initial point must match the number of :attr:`ansatz` - parameters. If ``None``, a random point will be generated within certain parameter - bounds. ``SamplingVQE`` will look to the ansatz for these bounds. If the ansatz does - not specify bounds, bounds of :math:`-2\pi`, :math:`2\pi` will be used. - aggregation: A float or callable to specify how the objective function evaluated on the - basis states should be aggregated. - callback: A callback that can access the intermediate data at each optimization step. - These data are: the evaluation count, the optimizer parameters for the ansatz, the - estimated value, and the metadata dictionary. - """ - super().__init__() - - self.sampler = sampler - self.ansatz = ansatz - self.optimizer = optimizer - self.aggregation = aggregation - self.callback = callback - - # this has to go via getters and setters due to the VariationalAlgorithm interface - self._initial_point = initial_point - - @property - def initial_point(self) -> Sequence[float] | None: - """Return the initial point.""" - return self._initial_point - - @initial_point.setter - def initial_point(self, value: Sequence[float] | None) -> None: - """Set the initial point.""" - self._initial_point = value - - def _check_operator_ansatz(self, operator: BaseOperator | PauliSumOp): - """Check that the number of qubits of operator and ansatz match and that the ansatz is - parameterized. - """ - if operator.num_qubits != self.ansatz.num_qubits: - try: - logger.info( - "Trying to resize ansatz to match operator on %s qubits.", operator.num_qubits - ) - self.ansatz.num_qubits = operator.num_qubits - except AttributeError as error: - raise AlgorithmError( - "The number of qubits of the ansatz does not match the " - "operator, and the ansatz does not allow setting the " - "number of qubits using `num_qubits`." - ) from error - - if self.ansatz.num_parameters == 0: - raise AlgorithmError("The ansatz must be parameterized, but has no free parameters.") - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - def compute_minimum_eigenvalue( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> SamplingMinimumEigensolverResult: - # check that the number of qubits of operator and ansatz match, and resize if possible - self._check_operator_ansatz(operator) - - if len(self.ansatz.clbits) > 0: - self.ansatz.remove_final_measurements() - self.ansatz.measure_all() - - initial_point = validate_initial_point(self.initial_point, self.ansatz) - - bounds = validate_bounds(self.ansatz) - - evaluate_energy, best_measurement = self._get_evaluate_energy( - operator, self.ansatz, return_best_measurement=True - ) - - start_time = time() - - if callable(self.optimizer): - optimizer_result = self.optimizer(fun=evaluate_energy, x0=initial_point, bounds=bounds) - else: - # we always want to submit as many estimations per job as possible for minimal - # overhead on the hardware - was_updated = _set_default_batchsize(self.optimizer) - - optimizer_result = self.optimizer.minimize( - fun=evaluate_energy, x0=initial_point, bounds=bounds - ) - - # reset to original value - if was_updated: - self.optimizer.set_max_evals_grouped(None) - - optimizer_time = time() - start_time - - logger.info( - "Optimization complete in %s seconds.\nFound opt_params %s.", - optimizer_time, - optimizer_result.x, - ) - - final_state = self.sampler.run([self.ansatz], [optimizer_result.x]).result().quasi_dists[0] - - if aux_operators is not None: - aux_operators_evaluated = estimate_observables( - _DiagonalEstimator(sampler=self.sampler), - self.ansatz, - aux_operators, - optimizer_result.x, - ) - else: - aux_operators_evaluated = None - - return self._build_sampling_vqe_result( - self.ansatz.copy(), - optimizer_result, - aux_operators_evaluated, - best_measurement, - final_state, - optimizer_time, - ) - - def _get_evaluate_energy( - self, - operator: BaseOperator | PauliSumOp, - ansatz: QuantumCircuit, - return_best_measurement: bool = False, - ) -> Callable[[np.ndarray], np.ndarray | float] | tuple[ - Callable[[np.ndarray], np.ndarray | float], dict[str, Any] - ]: - """Returns a function handle to evaluate the energy at given parameters. - - This is the objective function to be passed to the optimizer that is used for evaluation. - - Args: - operator: The operator whose energy to evaluate. - ansatz: The ansatz preparing the quantum state. - return_best_measurement: If True, a handle to a dictionary containing the best - measurement evaluated with the cost function. - - Returns: - A tuple of a callable evaluating the energy and (optionally) a dictionary containing the - best measurement of the energy evaluation. - - Raises: - AlgorithmError: If the circuit is not parameterized (i.e. has 0 free parameters). - - """ - num_parameters = ansatz.num_parameters - if num_parameters == 0: - raise AlgorithmError("The ansatz must be parameterized, but has 0 free parameters.") - - # avoid creating an instance variable to remain stateless regarding results - eval_count = 0 - - best_measurement = {"best": None} - - def store_best_measurement(best): - for best_i in best: - if best_measurement["best"] is None or _compare_measurements( - best_i, best_measurement["best"] - ): - best_measurement["best"] = best_i - - estimator = _DiagonalEstimator( - sampler=self.sampler, callback=store_best_measurement, aggregation=self.aggregation - ) - - def evaluate_energy(parameters: np.ndarray) -> np.ndarray | float: - nonlocal eval_count - # handle broadcasting: ensure parameters is of shape [array, array, ...] - parameters = np.reshape(parameters, (-1, num_parameters)).tolist() - batch_size = len(parameters) - - estimator_result = estimator.run( - batch_size * [ansatz], batch_size * [operator], parameters - ).result() - values = estimator_result.values - - if self.callback is not None: - metadata = estimator_result.metadata - for params, value, meta in zip(parameters, values, metadata): - eval_count += 1 - self.callback(eval_count, params, value, meta) - - result = values if len(values) > 1 else values[0] - return np.real(result) - - if return_best_measurement: - return evaluate_energy, best_measurement - - return evaluate_energy - - def _build_sampling_vqe_result( - self, - ansatz: QuantumCircuit, - optimizer_result: OptimizerResult, - aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]], - best_measurement: dict[str, Any], - final_state: QuasiDistribution, - optimizer_time: float, - ) -> SamplingVQEResult: - result = SamplingVQEResult() - result.eigenvalue = optimizer_result.fun - result.cost_function_evals = optimizer_result.nfev - result.optimal_point = optimizer_result.x - result.optimal_parameters = dict(zip(self.ansatz.parameters, optimizer_result.x)) - result.optimal_value = optimizer_result.fun - result.optimizer_time = optimizer_time - result.aux_operators_evaluated = aux_operators_evaluated - result.optimizer_result = optimizer_result - result.best_measurement = best_measurement["best"] - result.eigenstate = final_state - result.optimal_circuit = ansatz - return result - - -class SamplingVQEResult(VariationalResult, SamplingMinimumEigensolverResult): - """VQE Result.""" - - def __init__(self) -> None: - super().__init__() - self._cost_function_evals: int | None = None - - @property - def cost_function_evals(self) -> int | None: - """Returns number of cost optimizer evaluations""" - return self._cost_function_evals - - @cost_function_evals.setter - def cost_function_evals(self, value: int) -> None: - """Sets number of cost function evaluations""" - self._cost_function_evals = value - - -def _compare_measurements(candidate, current_best): - """Compare two best measurements. Returns True if the candidate is better than current value. - - This compares the following two criteria, in this precedence: - - 1. The smaller objective value is better - 2. The higher probability for the objective value is better - - """ - if candidate["value"] < current_best["value"]: - return True - elif candidate["value"] == current_best["value"]: - return candidate["probability"] > current_best["probability"] - return False diff --git a/qiskit/algorithms/minimum_eigensolvers/vqe.py b/qiskit/algorithms/minimum_eigensolvers/vqe.py deleted file mode 100644 index f26d3687971c..000000000000 --- a/qiskit/algorithms/minimum_eigensolvers/vqe.py +++ /dev/null @@ -1,356 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The variational quantum eigensolver algorithm.""" - -from __future__ import annotations - -import logging -from time import time -from collections.abc import Callable, Sequence -from typing import Any - -import numpy as np - -from qiskit.algorithms.gradients import BaseEstimatorGradient -from qiskit.circuit import QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..exceptions import AlgorithmError -from ..list_or_dict import ListOrDict -from ..optimizers import Optimizer, Minimizer, OptimizerResult -from ..variational_algorithm import VariationalAlgorithm, VariationalResult -from .minimum_eigensolver import MinimumEigensolver, MinimumEigensolverResult -from ..observables_evaluator import estimate_observables -from ..utils import validate_initial_point, validate_bounds - -# private function as we expect this to be updated in the next released -from ..utils.set_batching import _set_default_batchsize - -logger = logging.getLogger(__name__) - - -class VQE(VariationalAlgorithm, MinimumEigensolver): - r"""The variational quantum eigensolver (VQE) algorithm. - - VQE is a hybrid quantum-classical algorithm that uses a variational technique to find the - minimum eigenvalue of a given Hamiltonian operator :math:`H`. - - The ``VQE`` algorithm is executed using an :attr:`estimator` primitive, which computes - expectation values of operators (observables). - - An instance of ``VQE`` also requires an :attr:`ansatz`, a parameterized - :class:`.QuantumCircuit`, to prepare the trial state :math:`|\psi(\vec\theta)\rangle`. It also - needs a classical :attr:`optimizer` which varies the circuit parameters :math:`\vec\theta` such - that the expectation value of the operator on the corresponding state approaches a minimum, - - .. math:: - - \min_{\vec\theta} \langle\psi(\vec\theta)|H|\psi(\vec\theta)\rangle. - - The :attr:`estimator` is used to compute this expectation value for every optimization step. - - The optimizer can either be one of Qiskit's optimizers, such as - :class:`~qiskit.algorithms.optimizers.SPSA` or a callable with the following signature: - - .. code-block:: python - - from qiskit.algorithms.optimizers import OptimizerResult - - def my_minimizer(fun, x0, jac=None, bounds=None) -> OptimizerResult: - # Note that the callable *must* have these argument names! - # Args: - # fun (callable): the function to minimize - # x0 (np.ndarray): the initial point for the optimization - # jac (callable, optional): the gradient of the objective function - # bounds (list, optional): a list of tuples specifying the parameter bounds - - result = OptimizerResult() - result.x = # optimal parameters - result.fun = # optimal function value - return result - - The above signature also allows one to use any SciPy minimizer, for instance as - - .. code-block:: python - - from functools import partial - from scipy.optimize import minimize - - optimizer = partial(minimize, method="L-BFGS-B") - - The following attributes can be set via the initializer but can also be read and updated once - the VQE object has been constructed. - - Attributes: - estimator (BaseEstimator): The estimator primitive to compute the expectation value of the - Hamiltonian operator. - ansatz (QuantumCircuit): A parameterized quantum circuit to prepare the trial state. - optimizer (Optimizer | Minimizer): A classical optimizer to find the minimum energy. This - can either be a Qiskit :class:`.Optimizer` or a callable implementing the - :class:`.Minimizer` protocol. - gradient (BaseEstimatorGradient | None): An optional estimator gradient to be used with the - optimizer. - callback (Callable[[int, np.ndarray, float, dict[str, Any]], None] | None): A callback that - can access the intermediate data at each optimization step. These data are: the - evaluation count, the optimizer parameters for the ansatz, the evaluated mean, and the - metadata dictionary. - - References: - [1]: Peruzzo, A., et al, "A variational eigenvalue solver on a quantum processor" - `arXiv:1304.3061 `__ - """ - - def __init__( - self, - estimator: BaseEstimator, - ansatz: QuantumCircuit, - optimizer: Optimizer | Minimizer, - *, - gradient: BaseEstimatorGradient | None = None, - initial_point: Sequence[float] | None = None, - callback: Callable[[int, np.ndarray, float, dict[str, Any]], None] | None = None, - ) -> None: - r""" - Args: - estimator: The estimator primitive to compute the expectation value of the - Hamiltonian operator. - ansatz: A parameterized quantum circuit to prepare the trial state. - optimizer: A classical optimizer to find the minimum energy. This can either be a - Qiskit :class:`.Optimizer` or a callable implementing the :class:`.Minimizer` - protocol. - gradient: An optional estimator gradient to be used with the optimizer. - initial_point: An optional initial point (i.e. initial parameter values) for the - optimizer. The length of the initial point must match the number of :attr:`ansatz` - parameters. If ``None``, a random point will be generated within certain parameter - bounds. ``VQE`` will look to the ansatz for these bounds. If the ansatz does not - specify bounds, bounds of :math:`-2\pi`, :math:`2\pi` will be used. - callback: A callback that can access the intermediate data at each optimization step. - These data are: the evaluation count, the optimizer parameters for the ansatz, the - estimated value, and the metadata dictionary. - """ - super().__init__() - - self.estimator = estimator - self.ansatz = ansatz - self.optimizer = optimizer - self.gradient = gradient - # this has to go via getters and setters due to the VariationalAlgorithm interface - self.initial_point = initial_point - self.callback = callback - - @property - def initial_point(self) -> Sequence[float] | None: - return self._initial_point - - @initial_point.setter - def initial_point(self, value: Sequence[float] | None) -> None: - self._initial_point = value - - def compute_minimum_eigenvalue( - self, - operator: BaseOperator | PauliSumOp, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - ) -> VQEResult: - self._check_operator_ansatz(operator) - - initial_point = validate_initial_point(self.initial_point, self.ansatz) - - bounds = validate_bounds(self.ansatz) - - start_time = time() - - evaluate_energy = self._get_evaluate_energy(self.ansatz, operator) - - if self.gradient is not None: - evaluate_gradient = self._get_evaluate_gradient(self.ansatz, operator) - else: - evaluate_gradient = None - - # perform optimization - if callable(self.optimizer): - optimizer_result = self.optimizer( - fun=evaluate_energy, x0=initial_point, jac=evaluate_gradient, bounds=bounds - ) - else: - # we always want to submit as many estimations per job as possible for minimal - # overhead on the hardware - was_updated = _set_default_batchsize(self.optimizer) - - optimizer_result = self.optimizer.minimize( - fun=evaluate_energy, x0=initial_point, jac=evaluate_gradient, bounds=bounds - ) - - # reset to original value - if was_updated: - self.optimizer.set_max_evals_grouped(None) - - optimizer_time = time() - start_time - - logger.info( - "Optimization complete in %s seconds.\nFound optimal point %s", - optimizer_time, - optimizer_result.x, - ) - - if aux_operators is not None: - aux_operators_evaluated = estimate_observables( - self.estimator, self.ansatz, aux_operators, optimizer_result.x - ) - else: - aux_operators_evaluated = None - - return self._build_vqe_result( - self.ansatz, optimizer_result, aux_operators_evaluated, optimizer_time - ) - - @classmethod - def supports_aux_operators(cls) -> bool: - return True - - def _get_evaluate_energy( - self, - ansatz: QuantumCircuit, - operator: BaseOperator | PauliSumOp, - ) -> Callable[[np.ndarray], np.ndarray | float]: - """Returns a function handle to evaluate the energy at given parameters for the ansatz. - This is the objective function to be passed to the optimizer that is used for evaluation. - - Args: - ansatz: The ansatz preparing the quantum state. - operator: The operator whose energy to evaluate. - - Returns: - A callable that computes and returns the energy of the hamiltonian of each parameter. - - Raises: - AlgorithmError: If the primitive job to evaluate the energy fails. - """ - num_parameters = ansatz.num_parameters - - # avoid creating an instance variable to remain stateless regarding results - eval_count = 0 - - def evaluate_energy(parameters: np.ndarray) -> np.ndarray | float: - nonlocal eval_count - - # handle broadcasting: ensure parameters is of shape [array, array, ...] - parameters = np.reshape(parameters, (-1, num_parameters)).tolist() - batch_size = len(parameters) - - try: - job = self.estimator.run(batch_size * [ansatz], batch_size * [operator], parameters) - estimator_result = job.result() - except Exception as exc: - raise AlgorithmError("The primitive job to evaluate the energy failed!") from exc - - values = estimator_result.values - - if self.callback is not None: - metadata = estimator_result.metadata - for params, value, meta in zip(parameters, values, metadata): - eval_count += 1 - self.callback(eval_count, params, value, meta) - - energy = values[0] if len(values) == 1 else values - - return energy - - return evaluate_energy - - def _get_evaluate_gradient( - self, - ansatz: QuantumCircuit, - operator: BaseOperator | PauliSumOp, - ) -> Callable[[np.ndarray], np.ndarray]: - """Get a function handle to evaluate the gradient at given parameters for the ansatz. - - Args: - ansatz: The ansatz preparing the quantum state. - operator: The operator whose energy to evaluate. - - Returns: - A function handle to evaluate the gradient at given parameters for the ansatz. - - Raises: - AlgorithmError: If the primitive job to evaluate the gradient fails. - """ - - def evaluate_gradient(parameters: np.ndarray) -> np.ndarray: - # broadcasting not required for the estimator gradients - try: - job = self.gradient.run([ansatz], [operator], [parameters]) - gradients = job.result().gradients - except Exception as exc: - raise AlgorithmError("The primitive job to evaluate the gradient failed!") from exc - - return gradients[0] - - return evaluate_gradient - - def _check_operator_ansatz(self, operator: BaseOperator | PauliSumOp): - """Check that the number of qubits of operator and ansatz match and that the ansatz is - parameterized. - """ - if operator.num_qubits != self.ansatz.num_qubits: - try: - logger.info( - "Trying to resize ansatz to match operator on %s qubits.", operator.num_qubits - ) - self.ansatz.num_qubits = operator.num_qubits - except AttributeError as error: - raise AlgorithmError( - "The number of qubits of the ansatz does not match the " - "operator, and the ansatz does not allow setting the " - "number of qubits using `num_qubits`." - ) from error - - if self.ansatz.num_parameters == 0: - raise AlgorithmError("The ansatz must be parameterized, but has no free parameters.") - - def _build_vqe_result( - self, - ansatz: QuantumCircuit, - optimizer_result: OptimizerResult, - aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]], - optimizer_time: float, - ) -> VQEResult: - result = VQEResult() - result.optimal_circuit = ansatz.copy() - result.eigenvalue = optimizer_result.fun - result.cost_function_evals = optimizer_result.nfev - result.optimal_point = optimizer_result.x - result.optimal_parameters = dict(zip(self.ansatz.parameters, optimizer_result.x)) - result.optimal_value = optimizer_result.fun - result.optimizer_time = optimizer_time - result.aux_operators_evaluated = aux_operators_evaluated - result.optimizer_result = optimizer_result - return result - - -class VQEResult(VariationalResult, MinimumEigensolverResult): - """Variational quantum eigensolver result.""" - - def __init__(self) -> None: - super().__init__() - self._cost_function_evals: int | None = None - - @property - def cost_function_evals(self) -> int | None: - """The number of cost optimizer evaluations.""" - return self._cost_function_evals - - @cost_function_evals.setter - def cost_function_evals(self, value: int) -> None: - self._cost_function_evals = value diff --git a/qiskit/algorithms/observables_evaluator.py b/qiskit/algorithms/observables_evaluator.py deleted file mode 100644 index 6d40239e229e..000000000000 --- a/qiskit/algorithms/observables_evaluator.py +++ /dev/null @@ -1,126 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Evaluator of observables for algorithms.""" - -from __future__ import annotations -from collections.abc import Sequence -from typing import Any - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info import SparsePauliOp -from .exceptions import AlgorithmError -from .list_or_dict import ListOrDict -from ..primitives import BaseEstimator -from ..quantum_info.operators.base_operator import BaseOperator - - -def estimate_observables( - estimator: BaseEstimator, - quantum_state: QuantumCircuit, - observables: ListOrDict[BaseOperator | PauliSumOp], - parameter_values: Sequence[float] | None = None, - threshold: float = 1e-12, -) -> ListOrDict[tuple[complex, dict[str, Any]]]: - """ - Accepts a sequence of operators and calculates their expectation values - means - and metadata. They are calculated with respect to a quantum state provided. A user - can optionally provide a threshold value which filters mean values falling below the threshold. - - Args: - estimator: An estimator primitive used for calculations. - quantum_state: A (parameterized) quantum circuit preparing a quantum state that expectation - values are computed against. - observables: A list or a dictionary of operators whose expectation values are to be - calculated. - parameter_values: Optional list of parameters values to evaluate the quantum circuit on. - threshold: A threshold value that defines which mean values should be neglected (helpful for - ignoring numerical instabilities close to 0). - - Returns: - A list or a dictionary of tuples (mean, metadata). - - Raises: - AlgorithmError: If a primitive job is not successful. - """ - - if isinstance(observables, dict): - observables_list = list(observables.values()) - else: - observables_list = observables - - if len(observables_list) > 0: - observables_list = _handle_zero_ops(observables_list) - quantum_state = [quantum_state] * len(observables) - if parameter_values is not None: - parameter_values = [parameter_values] * len(observables) - try: - estimator_job = estimator.run(quantum_state, observables_list, parameter_values) - expectation_values = estimator_job.result().values - except Exception as exc: - raise AlgorithmError("The primitive job failed!") from exc - - metadata = estimator_job.result().metadata - # Discard values below threshold - observables_means = expectation_values * (np.abs(expectation_values) > threshold) - # zip means and metadata into tuples - observables_results = list(zip(observables_means, metadata)) - else: - observables_results = [] - - return _prepare_result(observables_results, observables) - - -def _handle_zero_ops( - observables_list: list[BaseOperator | PauliSumOp], -) -> list[BaseOperator | PauliSumOp]: - """Replaces all occurrence of operators equal to 0 in the list with an equivalent ``PauliSumOp`` - operator.""" - if observables_list: - zero_op = SparsePauliOp.from_list([("I" * observables_list[0].num_qubits, 0)]) - for ind, observable in enumerate(observables_list): - if observable == 0: - observables_list[ind] = zero_op - return observables_list - - -def _prepare_result( - observables_results: list[tuple[complex, dict]], - observables: ListOrDict[BaseOperator | PauliSumOp], -) -> ListOrDict[tuple[complex, dict[str, Any]]]: - """ - Prepares a list of tuples of eigenvalues and metadata tuples from - ``observables_results`` and ``observables``. - - Args: - observables_results: A list of tuples (mean, metadata). - observables: A list or a dictionary of operators whose expectation values are to be - calculated. - - Returns: - A list or a dictionary of tuples (mean, metadata). - """ - - if isinstance(observables, list): - # by construction, all None values will be overwritten - observables_eigenvalues: ListOrDict[tuple[complex, complex]] = [None] * len(observables) - key_value_iterator = enumerate(observables_results) - else: - observables_eigenvalues = {} - key_value_iterator = zip(observables.keys(), observables_results) - - for key, value in key_value_iterator: - observables_eigenvalues[key] = value - return observables_eigenvalues diff --git a/qiskit/algorithms/optimizers/__init__.py b/qiskit/algorithms/optimizers/__init__.py deleted file mode 100644 index 11bf73d1fcaf..000000000000 --- a/qiskit/algorithms/optimizers/__init__.py +++ /dev/null @@ -1,182 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Optimizers (:mod:`qiskit.algorithms.optimizers`) -===================================================== -It contains a variety of classical optimizers for use by quantum variational algorithms, -such as :class:`~qiskit.algorithms.VQE`. -Logically, these optimizers can be divided into two categories: - -`Local Optimizers`_ - Given an optimization problem, a **local optimizer** is a function - that attempts to find an optimal value within the neighboring set of a candidate solution. - -`Global Optimizers`_ - Given an optimization problem, a **global optimizer** is a function - that attempts to find an optimal value among all possible solutions. - -.. currentmodule:: qiskit.algorithms.optimizers - -Optimizer Base Class -==================== - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - OptimizerResult - OptimizerSupportLevel - Optimizer - Minimizer - -Steppable Optimizer Base Class -============================== - -.. autosummary:: - :toctree: ../stubs/ - - optimizer_utils - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - SteppableOptimizer - AskData - TellData - OptimizerState - - - -Local Optimizers -================ - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - ADAM - AQGD - CG - COBYLA - L_BFGS_B - GSLS - GradientDescent - GradientDescentState - NELDER_MEAD - NFT - P_BFGS - POWELL - SLSQP - SPSA - QNSPSA - TNC - SciPyOptimizer - UMDA - -Qiskit also provides the following optimizers, which are built-out using the optimizers from -the `scikit-quant` package. The `scikit-quant` package is not installed by default but must be -explicitly installed, if desired, by the user - the optimizers therein are provided under various -licenses so it has been made an optional install for the end user to choose whether to do so or -not. To install the `scikit-quant` dependent package you can use -`pip install scikit-quant`. - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - BOBYQA - IMFIL - SNOBFIT - -Global Optimizers -================= -The global optimizers here all use NLopt for their core function and can only be -used if their dependent NLopt package is manually installed. - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - CRS - DIRECT_L - DIRECT_L_RAND - ESCH - ISRES - -""" - -from .adam_amsgrad import ADAM -from .aqgd import AQGD -from .bobyqa import BOBYQA -from .cg import CG -from .cobyla import COBYLA -from .gsls import GSLS -from .gradient_descent import GradientDescent, GradientDescentState -from .imfil import IMFIL -from .l_bfgs_b import L_BFGS_B -from .nelder_mead import NELDER_MEAD -from .nft import NFT -from .nlopts.crs import CRS -from .nlopts.direct_l import DIRECT_L -from .nlopts.direct_l_rand import DIRECT_L_RAND -from .nlopts.esch import ESCH -from .nlopts.isres import ISRES -from .steppable_optimizer import SteppableOptimizer, AskData, TellData, OptimizerState -from .optimizer import Minimizer, Optimizer, OptimizerResult, OptimizerSupportLevel -from .p_bfgs import P_BFGS -from .powell import POWELL -from .qnspsa import QNSPSA -from .scipy_optimizer import SciPyOptimizer -from .slsqp import SLSQP -from .snobfit import SNOBFIT -from .spsa import SPSA -from .tnc import TNC -from .umda import UMDA - -__all__ = [ - "Optimizer", - "OptimizerSupportLevel", - "SteppableOptimizer", - "AskData", - "TellData", - "OptimizerState", - "OptimizerResult", - "Minimizer", - "ADAM", - "AQGD", - "CG", - "COBYLA", - "GSLS", - "GradientDescent", - "GradientDescentState", - "L_BFGS_B", - "NELDER_MEAD", - "NFT", - "P_BFGS", - "POWELL", - "SciPyOptimizer", - "SLSQP", - "SPSA", - "QNSPSA", - "TNC", - "CRS", - "DIRECT_L", - "DIRECT_L_RAND", - "ESCH", - "ISRES", - "SNOBFIT", - "BOBYQA", - "IMFIL", - "UMDA", -] diff --git a/qiskit/algorithms/optimizers/adam_amsgrad.py b/qiskit/algorithms/optimizers/adam_amsgrad.py deleted file mode 100644 index 422aa17a5f01..000000000000 --- a/qiskit/algorithms/optimizers/adam_amsgrad.py +++ /dev/null @@ -1,270 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Adam and AMSGRAD optimizers.""" -from __future__ import annotations - -from collections.abc import Callable -from typing import Any -import os - -import csv -import numpy as np -from qiskit.utils.deprecation import deprecate_arg -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT - -# pylint: disable=invalid-name - - -class ADAM(Optimizer): - """Adam and AMSGRAD optimizers. - - Adam [1] is a gradient-based optimization algorithm that is relies on adaptive estimates of - lower-order moments. The algorithm requires little memory and is invariant to diagonal - rescaling of the gradients. Furthermore, it is able to cope with non-stationary objective - functions and noisy and/or sparse gradients. - - AMSGRAD [2] (a variant of Adam) uses a 'long-term memory' of past gradients and, thereby, - improves convergence properties. - - References: - - [1]: Kingma, Diederik & Ba, Jimmy (2014), Adam: A Method for Stochastic Optimization. - `arXiv:1412.6980 `_ - - [2]: Sashank J. Reddi and Satyen Kale and Sanjiv Kumar (2018), - On the Convergence of Adam and Beyond. - `arXiv:1904.09237 `_ - - .. note:: - - This component has some function that is normally random. If you want to reproduce behavior - then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). - - """ - - _OPTIONS = [ - "maxiter", - "tol", - "lr", - "beta_1", - "beta_2", - "noise_factor", - "eps", - "amsgrad", - "snapshot_dir", - ] - - def __init__( - self, - maxiter: int = 10000, - tol: float = 1e-6, - lr: float = 1e-3, - beta_1: float = 0.9, - beta_2: float = 0.99, - noise_factor: float = 1e-8, - eps: float = 1e-10, - amsgrad: bool = False, - snapshot_dir: str | None = None, - ) -> None: - """ - Args: - maxiter: Maximum number of iterations - tol: Tolerance for termination - lr: Value >= 0, Learning rate. - beta_1: Value in range 0 to 1, Generally close to 1. - beta_2: Value in range 0 to 1, Generally close to 1. - noise_factor: Value >= 0, Noise factor - eps : Value >=0, Epsilon to be used for finite differences if no analytic - gradient method is given. - amsgrad: True to use AMSGRAD, False if not - snapshot_dir: If not None save the optimizer's parameter - after every step to the given directory - """ - super().__init__() - for k, v in list(locals().items()): - if k in self._OPTIONS: - self._options[k] = v - self._maxiter = maxiter - self._snapshot_dir = snapshot_dir - self._tol = tol - self._lr = lr - self._beta_1 = beta_1 - self._beta_2 = beta_2 - self._noise_factor = noise_factor - self._eps = eps - self._amsgrad = amsgrad - - # runtime variables - self._t = 0 # time steps - self._m = np.zeros(1) - self._v = np.zeros(1) - if self._amsgrad: - self._v_eff = np.zeros(1) - - if self._snapshot_dir: - - with open(os.path.join(self._snapshot_dir, "adam_params.csv"), mode="w") as csv_file: - if self._amsgrad: - fieldnames = ["v", "v_eff", "m", "t"] - else: - fieldnames = ["v", "m", "t"] - writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writeheader() - - @property - def settings(self) -> dict[str, Any]: - return { - "maxiter": self._maxiter, - "tol": self._tol, - "lr": self._lr, - "beta_1": self._beta_1, - "beta_2": self._beta_2, - "noise_factor": self._noise_factor, - "eps": self._eps, - "amsgrad": self._amsgrad, - "snapshot_dir": self._snapshot_dir, - } - - def get_support_level(self): - """Return support level dictionary""" - return { - "gradient": OptimizerSupportLevel.supported, - "bounds": OptimizerSupportLevel.ignored, - "initial_point": OptimizerSupportLevel.supported, - } - - def save_params(self, snapshot_dir: str) -> None: - """Save the current iteration parameters to a file called ``adam_params.csv``. - - Note: - - The current parameters are appended to the file, if it exists already. - The file is not overwritten. - - Args: - snapshot_dir: The directory to store the file in. - """ - if self._amsgrad: - with open(os.path.join(snapshot_dir, "adam_params.csv"), mode="a") as csv_file: - fieldnames = ["v", "v_eff", "m", "t"] - writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writerow({"v": self._v, "v_eff": self._v_eff, "m": self._m, "t": self._t}) - else: - with open(os.path.join(snapshot_dir, "adam_params.csv"), mode="a") as csv_file: - fieldnames = ["v", "m", "t"] - writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writerow({"v": self._v, "m": self._m, "t": self._t}) - - def load_params(self, load_dir: str) -> None: - """Load iteration parameters for a file called ``adam_params.csv``. - - Args: - load_dir: The directory containing ``adam_params.csv``. - """ - with open(os.path.join(load_dir, "adam_params.csv")) as csv_file: - if self._amsgrad: - fieldnames = ["v", "v_eff", "m", "t"] - else: - fieldnames = ["v", "m", "t"] - reader = csv.DictReader(csv_file, fieldnames=fieldnames) - for line in reader: - v = line["v"] - if self._amsgrad: - v_eff = line["v_eff"] - m = line["m"] - t = line["t"] - - v = v[1:-1] - self._v = np.fromstring(v, dtype=float, sep=" ") - if self._amsgrad: - v_eff = v_eff[1:-1] - self._v_eff = np.fromstring(v_eff, dtype=float, sep=" ") - m = m[1:-1] - self._m = np.fromstring(m, dtype=float, sep=" ") - t = t[1:-1] - self._t = np.fromstring(t, dtype=int, sep=" ") - - @deprecate_arg("objective_function", new_alias="fun", since="0.19.0") - @deprecate_arg("initial_point", new_alias="fun", since="0.19.0") - @deprecate_arg("gradient_function", new_alias="jac", since="0.19.0") - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - # pylint:disable=unused-argument - objective_function: Callable[[np.ndarray], float] | None = None, - initial_point: np.ndarray | None = None, - gradient_function: Callable[[np.ndarray], float] | None = None, - # ) -> Tuple[np.ndarray, float, int]: - ) -> OptimizerResult: # TODO find proper way to deprecate return type - """Minimize the scalar function. - - Args: - fun: The scalar function to minimize. - x0: The initial point for the minimization. - jac: The gradient of the scalar function ``fun``. - bounds: Bounds for the variables of ``fun``. This argument might be ignored if the - optimizer does not support bounds. - objective_function: DEPRECATED. A function handle to the objective function. - initial_point: DEPRECATED. The initial iteration point. - gradient_function: DEPRECATED. A function handle to the gradient of the objective - function. - - Returns: - The result of the optimization, containing e.g. the result as attribute ``x``. - """ - if jac is None: - jac = Optimizer.wrap_function(Optimizer.gradient_num_diff, (fun, self._eps)) - - derivative = jac(x0) - self._t = 0 - self._m = np.zeros(np.shape(derivative)) - self._v = np.zeros(np.shape(derivative)) - if self._amsgrad: - self._v_eff = np.zeros(np.shape(derivative)) - - params = params_new = x0 - while self._t < self._maxiter: - if self._t > 0: - derivative = jac(params) - self._t += 1 - self._m = self._beta_1 * self._m + (1 - self._beta_1) * derivative - self._v = self._beta_2 * self._v + (1 - self._beta_2) * derivative * derivative - lr_eff = self._lr * np.sqrt(1 - self._beta_2**self._t) / (1 - self._beta_1**self._t) - if not self._amsgrad: - params_new = params - lr_eff * self._m.flatten() / ( - np.sqrt(self._v.flatten()) + self._noise_factor - ) - else: - self._v_eff = np.maximum(self._v_eff, self._v) - params_new = params - lr_eff * self._m.flatten() / ( - np.sqrt(self._v_eff.flatten()) + self._noise_factor - ) - - if self._snapshot_dir: - self.save_params(self._snapshot_dir) - - # check termination - if np.linalg.norm(params - params_new) < self._tol: - break - - params = params_new - - result = OptimizerResult() - result.x = params_new - result.fun = fun(params_new) - result.nfev = self._t - return result diff --git a/qiskit/algorithms/optimizers/aqgd.py b/qiskit/algorithms/optimizers/aqgd.py deleted file mode 100644 index ad8ad42ae9f1..000000000000 --- a/qiskit/algorithms/optimizers/aqgd.py +++ /dev/null @@ -1,367 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Analytical Quantum Gradient Descent (AQGD) optimizer.""" - -from __future__ import annotations -import logging -from collections.abc import Callable -from typing import Any -import warnings - -import numpy as np -from qiskit.utils.validation import validate_range_exclusive_max -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT -from ..exceptions import AlgorithmError - -logger = logging.getLogger(__name__) - - -class AQGD(Optimizer): - """Analytic Quantum Gradient Descent (AQGD) with Epochs optimizer. - Performs gradient descent optimization with a momentum term, analytic gradients, - and customized step length schedule for parameterized quantum gates, i.e. - Pauli Rotations. See, for example: - - * K. Mitarai, M. Negoro, M. Kitagawa, and K. Fujii. (2018). - Quantum circuit learning. Phys. Rev. A 98, 032309. - https://arxiv.org/abs/1803.00745 - - * Maria Schuld, Ville Bergholm, Christian Gogolin, Josh Izaac, Nathan Killoran. (2019). - Evaluating analytic gradients on quantum hardware. Phys. Rev. A 99, 032331. - https://arxiv.org/abs/1811.11184 - - for further details on analytic gradients of parameterized quantum gates. - - Gradients are computed "analytically" using the quantum circuit when evaluating - the objective function. - - """ - - _OPTIONS = ["maxiter", "eta", "tol", "disp", "momentum", "param_tol", "averaging"] - - def __init__( - self, - maxiter: int | list[int] = 1000, - eta: float | list[float] = 1.0, - tol: float = 1e-6, # this is tol - momentum: float | list[float] = 0.25, - param_tol: float = 1e-6, - averaging: int = 10, - ) -> None: - """ - Performs Analytical Quantum Gradient Descent (AQGD) with Epochs. - - Args: - maxiter: Maximum number of iterations (full gradient steps) - eta: The coefficient of the gradient update. Increasing this value - results in larger step sizes: param = previous_param - eta * deriv - tol: Tolerance for change in windowed average of objective values. - Convergence occurs when either objective tolerance is met OR parameter - tolerance is met. - momentum: Bias towards the previous gradient momentum in current - update. Must be within the bounds: [0,1) - param_tol: Tolerance for change in norm of parameters. - averaging: Length of window over which to average objective values for objective - convergence criterion - - Raises: - AlgorithmError: If the length of ``maxiter``, `momentum``, and ``eta`` is not the same. - """ - super().__init__() - if isinstance(maxiter, int): - maxiter = [maxiter] - if isinstance(eta, (int, float)): - eta = [eta] - if isinstance(momentum, (int, float)): - momentum = [momentum] - if len(maxiter) != len(eta) or len(maxiter) != len(momentum): - raise AlgorithmError( - "AQGD input parameter length mismatch. Parameters `maxiter`, " - "`eta`, and `momentum` must have the same length." - ) - for m in momentum: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - validate_range_exclusive_max("momentum", m, 0, 1) - - self._eta = eta - self._maxiter = maxiter - self._momenta_coeff = momentum - self._param_tol = param_tol - self._tol = tol - self._averaging = averaging - - # state - self._avg_objval: float | None = None - self._prev_param: np.ndarray | None = None - self._eval_count = 0 # function evaluations - self._prev_loss: list[float] = [] - self._prev_grad: list[list[float]] = [] - - def get_support_level(self) -> dict[str, OptimizerSupportLevel]: - """Support level dictionary - - Returns: - Dict[str, int]: gradient, bounds and initial point - support information that is ignored/required. - """ - return { - "gradient": OptimizerSupportLevel.ignored, - "bounds": OptimizerSupportLevel.ignored, - "initial_point": OptimizerSupportLevel.required, - } - - @property - def settings(self) -> dict[str, Any]: - return { - "maxiter": self._maxiter, - "eta": self._eta, - "momentum": self._momenta_coeff, - "param_tol": self._param_tol, - "tol": self._tol, - "averaging": self._averaging, - } - - def _compute_objective_fn_and_gradient( - self, params: np.ndarray | list[float], obj: Callable - ) -> tuple[float, np.ndarray]: - """ - Obtains the objective function value for params and the analytical quantum derivatives of - the objective function with respect to each parameter. Requires - 2*(number parameters) + 1 objective evaluations - - Args: - params: Current value of the parameters to evaluate the objective function - obj: Objective function of interest - - Returns: - Tuple containing the objective value and array of gradients for the given parameter set. - """ - num_params = len(params) - param_sets_to_eval = params + np.concatenate( - ( - np.zeros((1, num_params)), # copy of the parameters as is - np.eye(num_params) * np.pi / 2, # copy of the parameters with the positive shift - -np.eye(num_params) * np.pi / 2, - ), # copy of the parameters with the negative shift - axis=0, - ) - # Evaluate, - # reshaping to flatten, as expected by objective function - values = np.array(obj(param_sets_to_eval.reshape(-1))) - - # Update number of objective function evaluations - self._eval_count += 2 * num_params + 1 - - # return the objective function value - obj_value = values[0] - - # return the gradient values - gradient = 0.5 * (values[1 : num_params + 1] - values[1 + num_params :]) - return obj_value, gradient - - def _update( - self, - params: np.ndarray, - gradient: np.ndarray, - mprev: np.ndarray, - step_size: float, - momentum_coeff: float, - ) -> tuple[np.ndarray, np.ndarray]: - """ - Updates full parameter array based on a step that is a convex - combination of the gradient and previous momentum - - Args: - params: Current value of the parameters to evaluate the objective function at - gradient: Gradient of objective wrt parameters - mprev: Momentum vector for each parameter - step_size: The scaling of step to take - momentum_coeff: Bias towards previous momentum vector when updating current - momentum/step vector - - Returns: - Tuple of the updated parameter and momentum vectors respectively. - """ - # Momentum update: - # Convex combination of previous momentum and current gradient estimate - mnew = (1 - momentum_coeff) * gradient + momentum_coeff * mprev - params -= step_size * mnew - return params, mnew - - def _converged_objective(self, objval: float, tol: float, window_size: int) -> bool: - """ - Tests convergence based on the change in a moving windowed average of past objective values - - Args: - objval: Current value of the objective function - tol: tolerance below which (average) objective function change must be - window_size: size of averaging window - - Returns: - Bool indicating whether or not the optimization has converged. - """ - # If we haven't reached the required window length, - # append the current value, but we haven't converged - if len(self._prev_loss) < window_size: - self._prev_loss.append(objval) - return False - - # Update last value in list with current value - self._prev_loss.append(objval) - # (length now = n+1) - - # Calculate previous windowed average - # and current windowed average of objective values - prev_avg = np.mean(self._prev_loss[:window_size]) - curr_avg = np.mean(self._prev_loss[1 : window_size + 1]) - self._avg_objval = curr_avg - - # Update window of objective values - # (Remove earliest value) - self._prev_loss.pop(0) - - if np.absolute(prev_avg - curr_avg) < tol: - # converged - logger.info("Previous obj avg: %f\nCurr obj avg: %f", prev_avg, curr_avg) - return True - return False - - def _converged_parameter(self, parameter: np.ndarray, tol: float) -> bool: - """ - Tests convergence based on change in parameter - - Args: - parameter: current parameter values - tol: tolerance for change in norm of parameters - - Returns: - Bool indicating whether or not the optimization has converged - """ - if self._prev_param is None: - self._prev_param = np.copy(parameter) - return False - - order = np.inf - p_change = np.linalg.norm(self._prev_param - parameter, ord=order) - if p_change < tol: - # converged - logger.info("Change in parameters (%f norm): %f", order, p_change) - return True - return False - - def _converged_alt(self, gradient: list[float], tol: float, window_size: int) -> bool: - """ - Tests convergence from norm of windowed average of gradients - - Args: - gradient: current gradient - tol: tolerance for average gradient norm - window_size: size of averaging window - - Returns: - Bool indicating whether or not the optimization has converged - """ - # If we haven't reached the required window length, - # append the current value, but we haven't converged - if len(self._prev_grad) < window_size - 1: - self._prev_grad.append(gradient) - return False - - # Update last value in list with current value - self._prev_grad.append(gradient) - # (length now = n) - - # Calculate previous windowed average - # and current windowed average of objective values - avg_grad = np.mean(self._prev_grad, axis=0) - - # Update window of values - # (Remove earliest value) - self._prev_grad.pop(0) - - if np.linalg.norm(avg_grad, ord=np.inf) < tol: - # converged - logger.info("Avg. grad. norm: %f", np.linalg.norm(avg_grad, ord=np.inf)) - return True - return False - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - params = np.asarray(x0) - momentum = np.zeros(shape=(params.size,)) - # empty out history of previous objectives/gradients/parameters - # (in case this object is re-used) - self._prev_loss = [] - self._prev_grad = [] - self._prev_param = None - self._eval_count = 0 # function evaluations - - iter_count = 0 - logger.info("Initial Params: %s", params) - - epoch = 0 - converged = False - for (eta, mom_coeff) in zip(self._eta, self._momenta_coeff): - logger.info("Epoch: %4d | Stepsize: %6.4f | Momentum: %6.4f", epoch, eta, mom_coeff) - - sum_max_iters = sum(self._maxiter[0 : epoch + 1]) - while iter_count < sum_max_iters: - # update the iteration count - iter_count += 1 - - # Check for parameter convergence before potentially costly function evaluation - converged = self._converged_parameter(params, self._param_tol) - if converged: - break - - # Calculate objective function and estimate of analytical gradient - if jac is None: - objval, gradient = self._compute_objective_fn_and_gradient(params, fun) - else: - objval = fun(params) - gradient = jac(params) - - logger.info( - " Iter: %4d | Obj: %11.6f | Grad Norm: %f", - iter_count, - objval, - np.linalg.norm(gradient, ord=np.inf), - ) - - # Check for objective convergence - converged = self._converged_objective(objval, self._tol, self._averaging) - if converged: - break - - # Update parameters and momentum - params, momentum = self._update(params, gradient, momentum, eta, mom_coeff) - # end inner iteration - # if converged, end iterating over epochs - if converged: - break - epoch += 1 - # end epoch iteration - - result = OptimizerResult() - result.x = params - result.fun = objval - result.nfev = self._eval_count - result.nit = iter_count - - return result diff --git a/qiskit/algorithms/optimizers/bobyqa.py b/qiskit/algorithms/optimizers/bobyqa.py deleted file mode 100644 index 39250aef917a..000000000000 --- a/qiskit/algorithms/optimizers/bobyqa.py +++ /dev/null @@ -1,84 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Bound Optimization BY Quadratic Approximation (BOBYQA) optimizer.""" - -from __future__ import annotations - -from collections.abc import Callable -from typing import Any - -import numpy as np -from qiskit.utils import optionals as _optionals -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT - - -@_optionals.HAS_SKQUANT.require_in_instance -class BOBYQA(Optimizer): - """Bound Optimization BY Quadratic Approximation algorithm. - - BOBYQA finds local solutions to nonlinear, non-convex minimization problems with optional - bound constraints, without requirement of derivatives of the objective function. - - Uses skquant.opt installed with pip install scikit-quant. - For further detail, please refer to - https://github.com/scikit-quant/scikit-quant and https://qat4chem.lbl.gov/software. - """ - - def __init__( - self, - maxiter: int = 1000, - ) -> None: - """ - Args: - maxiter: Maximum number of function evaluations. - - Raises: - MissingOptionalLibraryError: scikit-quant not installed - """ - super().__init__() - self._maxiter = maxiter - - def get_support_level(self): - """Returns support level dictionary.""" - return { - "gradient": OptimizerSupportLevel.ignored, - "bounds": OptimizerSupportLevel.required, - "initial_point": OptimizerSupportLevel.required, - } - - @property - def settings(self) -> dict[str, Any]: - return {"maxiter": self._maxiter} - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - from skquant import opt as skq - - res, history = skq.minimize( - func=fun, - x0=np.asarray(x0), - bounds=np.array(bounds), - budget=self._maxiter, - method="bobyqa", - ) - - optimizer_result = OptimizerResult() - optimizer_result.x = res.optpar - optimizer_result.fun = res.optval - optimizer_result.nfev = len(history) - return optimizer_result diff --git a/qiskit/algorithms/optimizers/cg.py b/qiskit/algorithms/optimizers/cg.py deleted file mode 100644 index 670b4ac33868..000000000000 --- a/qiskit/algorithms/optimizers/cg.py +++ /dev/null @@ -1,70 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Conjugate Gradient optimizer.""" - -from __future__ import annotations - -from .scipy_optimizer import SciPyOptimizer - - -class CG(SciPyOptimizer): - """Conjugate Gradient optimizer. - - CG is an algorithm for the numerical solution of systems of linear equations whose matrices are - symmetric and positive-definite. It is an *iterative algorithm* in that it uses an initial - guess to generate a sequence of improving approximate solutions for a problem, - in which each approximation is derived from the previous ones. It is often used to solve - unconstrained optimization problems, such as energy minimization. - - Uses scipy.optimize.minimize CG. - For further detail, please refer to - https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html - """ - - _OPTIONS = ["maxiter", "disp", "gtol", "eps"] - - # pylint: disable=unused-argument - def __init__( - self, - maxiter: int = 20, - disp: bool = False, - gtol: float = 1e-5, - tol: float | None = None, - eps: float = 1.4901161193847656e-08, - options: dict | None = None, - max_evals_grouped: int = 1, - **kwargs, - ) -> None: - """ - Args: - maxiter: Maximum number of iterations to perform. - disp: Set to True to print convergence messages. - gtol: Gradient norm must be less than gtol before successful termination. - tol: Tolerance for termination. - eps: If jac is approximated, use this value for the step size. - options: A dictionary of solver options. - max_evals_grouped: Max number of default gradient evaluations performed simultaneously. - kwargs: additional kwargs for scipy.optimize.minimize. - """ - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__( - method="CG", - options=options, - tol=tol, - max_evals_grouped=max_evals_grouped, - **kwargs, - ) diff --git a/qiskit/algorithms/optimizers/cobyla.py b/qiskit/algorithms/optimizers/cobyla.py deleted file mode 100644 index 72a0938379e7..000000000000 --- a/qiskit/algorithms/optimizers/cobyla.py +++ /dev/null @@ -1,59 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Constrained Optimization By Linear Approximation optimizer.""" - -from __future__ import annotations - -from .scipy_optimizer import SciPyOptimizer - - -class COBYLA(SciPyOptimizer): - """ - Constrained Optimization By Linear Approximation optimizer. - - COBYLA is a numerical optimization method for constrained problems - where the derivative of the objective function is not known. - - Uses scipy.optimize.minimize COBYLA. - For further detail, please refer to - https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html - """ - - _OPTIONS = ["maxiter", "disp", "rhobeg"] - - # pylint: disable=unused-argument - def __init__( - self, - maxiter: int = 1000, - disp: bool = False, - rhobeg: float = 1.0, - tol: float | None = None, - options: dict | None = None, - **kwargs, - ) -> None: - """ - Args: - maxiter: Maximum number of function evaluations. - disp: Set to True to print convergence messages. - rhobeg: Reasonable initial changes to the variables. - tol: Final accuracy in the optimization (not precisely guaranteed). - This is a lower bound on the size of the trust region. - options: A dictionary of solver options. - kwargs: additional kwargs for scipy.optimize.minimize. - """ - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__(method="COBYLA", options=options, tol=tol, **kwargs) diff --git a/qiskit/algorithms/optimizers/gradient_descent.py b/qiskit/algorithms/optimizers/gradient_descent.py deleted file mode 100644 index bd39ffa83d85..000000000000 --- a/qiskit/algorithms/optimizers/gradient_descent.py +++ /dev/null @@ -1,401 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A standard gradient descent optimizer.""" -from __future__ import annotations - -from collections.abc import Generator -from dataclasses import dataclass, field -from typing import Any, Callable, SupportsFloat -import numpy as np -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT -from .steppable_optimizer import AskData, TellData, OptimizerState, SteppableOptimizer -from .optimizer_utils import LearningRate - -CALLBACK = Callable[[int, np.ndarray, float, SupportsFloat], None] - - -@dataclass -class GradientDescentState(OptimizerState): - """State of :class:`~.GradientDescent`. - - Dataclass with all the information of an optimizer plus the learning_rate and the stepsize. - """ - - stepsize: float | None - """Norm of the gradient on the last step.""" - - learning_rate: LearningRate = field(compare=False) - """Learning rate at the current step of the optimization process. - - It behaves like a generator, (use ``next(learning_rate)`` to get the learning rate for the - next step) but it can also return the current learning rate with ``learning_rate.current``. - - """ - - -class GradientDescent(SteppableOptimizer): - r"""The gradient descent minimization routine. - - For a function :math:`f` and an initial point :math:`\vec\theta_0`, the standard (or "vanilla") - gradient descent method is an iterative scheme to find the minimum :math:`\vec\theta^*` of - :math:`f` by updating the parameters in the direction of the negative gradient of :math:`f` - - .. math:: - - \vec\theta_{n+1} = \vec\theta_{n} - \eta_n \vec\nabla f(\vec\theta_{n}), - - for a small learning rate :math:`\eta_n > 0`. - - You can either provide the analytic gradient :math:`\vec\nabla f` as ``jac`` - in the :meth:`~.minimize` method, or, if you do not provide it, use a finite difference - approximation of the gradient. To adapt the size of the perturbation in the finite difference - gradients, set the ``perturbation`` property in the initializer. - - This optimizer supports a callback function. If provided in the initializer, the optimizer - will call the callback in each iteration with the following information in this order: - current number of function values, current parameters, current function value, norm of current - gradient. - - Examples: - - A minimum example that will use finite difference gradients with a default perturbation - of 0.01 and a default learning rate of 0.01. - - .. code-block:: python - - from qiskit.algorithms.optimizers import GradientDescent - - def f(x): - return (np.linalg.norm(x) - 1) ** 2 - - initial_point = np.array([1, 0.5, -0.2]) - - optimizer = GradientDescent(maxiter=100) - - result = optimizer.minimize(fun=fun, x0=initial_point) - - print(f"Found minimum {result.x} at a value" - "of {result.fun} using {result.nfev} evaluations.") - - An example where the learning rate is an iterator and we supply the analytic gradient. - Note how much faster this convergences (i.e. less ``nfev``) compared to the previous - example. - - .. code-block:: python - - from qiskit.algorithms.optimizers import GradientDescent - - def learning_rate(): - power = 0.6 - constant_coeff = 0.1 - def powerlaw(): - n = 0 - while True: - yield constant_coeff * (n ** power) - n += 1 - - return powerlaw() - - def f(x): - return (np.linalg.norm(x) - 1) ** 2 - - def grad_f(x): - return 2 * (np.linalg.norm(x) - 1) * x / np.linalg.norm(x) - - initial_point = np.array([1, 0.5, -0.2]) - - optimizer = GradientDescent(maxiter=100, learning_rate=learning_rate) - result = optimizer.minimize(fun=fun, jac=grad_f, x0=initial_point) - - print(f"Found minimum {result.x} at a value" - "of {result.fun} using {result.nfev} evaluations.") - - - An other example where the evaluation of the function has a chance of failing. The user, with - specific knowledge about his function can catch this errors and handle them before passing the - result to the optimizer. - - .. code-block:: python - - import random - import numpy as np - from qiskit.algorithms.optimizers import GradientDescent - - def objective(x): - if random.choice([True, False]): - return None - else: - return (np.linalg.norm(x) - 1) ** 2 - - def grad(x): - if random.choice([True, False]): - return None - else: - return 2 * (np.linalg.norm(x) - 1) * x / np.linalg.norm(x) - - - initial_point = np.random.normal(0, 1, size=(100,)) - - optimizer = GradientDescent(maxiter=20) - optimizer.start(x0=initial_point, fun=objective, jac=grad) - - while optimizer.continue_condition(): - ask_data = optimizer.ask() - evaluated_gradient = None - - while evaluated_gradient is None: - evaluated_gradient = grad(ask_data.x_center) - optimizer.state.njev += 1 - - optmizer.state.nit += 1 - - tell_data = TellData(eval_jac=evaluated_gradient) - optimizer.tell(ask_data=ask_data, tell_data=tell_data) - - result = optimizer.create_result() - - Users that aren't dealing with complicated functions and who are more familiar with step by step - optimization algorithms can use the :meth:`~.step` method which wraps the :meth:`~.ask` - and :meth:`~.tell` methods. In the same spirit the method :meth:`~.minimize` will optimize the - function and return the result. - - To see other libraries that use this interface one can visit: - https://optuna.readthedocs.io/en/stable/tutorial/20_recipes/009_ask_and_tell.html - - """ - - def __init__( - self, - maxiter: int = 100, - learning_rate: float - | list[float] - | np.ndarray - | Callable[[], Generator[float, None, None]] = 0.01, - tol: float = 1e-7, - callback: CALLBACK | None = None, - perturbation: float | None = None, - ) -> None: - """ - Args: - maxiter: The maximum number of iterations. - learning_rate: A constant, list, array or factory of generators yielding learning rates - for the parameter updates. See the docstring for an example. - tol: If the norm of the parameter update is smaller than this threshold, the - optimizer has converged. - perturbation: If no gradient is passed to :meth:`~.minimize` the gradient is - approximated with a forward finite difference scheme with ``perturbation`` - perturbation in both directions (defaults to 1e-2 if required). - Ignored when we have an explicit function for the gradient. - Raises: - ValueError: If ``learning_rate`` is an array and its length is less than ``maxiter``. - """ - super().__init__(maxiter=maxiter) - self.callback = callback - self._state: GradientDescentState | None = None - self._perturbation = perturbation - self._tol = tol - # if learning rate is an array, check it is sufficiently long. - if isinstance(learning_rate, (list, np.ndarray)): - if len(learning_rate) < maxiter: - raise ValueError( - f"Length of learning_rate ({len(learning_rate)}) " - f"is smaller than maxiter ({maxiter})." - ) - self.learning_rate = learning_rate - - @property - def state(self) -> GradientDescentState: - """Return the current state of the optimizer.""" - return self._state - - @state.setter - def state(self, state: GradientDescentState) -> None: - """Set the current state of the optimizer.""" - self._state = state - - @property - def tol(self) -> float: - """Returns the tolerance of the optimizer. - - Any step with smaller stepsize than this value will stop the optimization.""" - return self._tol - - @tol.setter - def tol(self, tol: float) -> None: - """Set the tolerance.""" - self._tol = tol - - @property - def perturbation(self) -> float | None: - """Returns the perturbation. - - This is the perturbation used in the finite difference gradient approximation. - """ - return self._perturbation - - @perturbation.setter - def perturbation(self, perturbation: float | None) -> None: - """Set the perturbation.""" - self._perturbation = perturbation - - def _callback_wrapper(self) -> None: - """ - Wraps the callback function to accommodate GradientDescent. - - Will call :attr:`~.callback` and pass the following arguments: - current number of function values, current parameters, current function value, - norm of current gradient. - """ - if self.callback is not None: - self.callback( - self.state.nfev, - self.state.x, - self.state.fun(self.state.x), - self.state.stepsize, - ) - - @property - def settings(self) -> dict[str, Any]: - # if learning rate or perturbation are custom iterators expand them - if callable(self.learning_rate): - iterator = self.learning_rate() - learning_rate: float | np.ndarray = np.array( - [next(iterator) for _ in range(self.maxiter)] - ) - else: - learning_rate = self.learning_rate - - return { - "maxiter": self.maxiter, - "tol": self.tol, - "learning_rate": learning_rate, - "perturbation": self.perturbation, - "callback": self.callback, - } - - def ask(self) -> AskData: - """Returns an object with the data needed to evaluate the gradient. - - If this object contains a gradient function the gradient can be evaluated directly. Otherwise - approximate it with a finite difference scheme. - """ - return AskData( - x_jac=self.state.x, - ) - - def tell(self, ask_data: AskData, tell_data: TellData) -> None: - """ - Updates :attr:`.~GradientDescentState.x` by an amount proportional to the learning - rate and value of the gradient at that point. - - Args: - ask_data: The data used to evaluate the function. - tell_data: The data from the function evaluation. - - Raises: - ValueError: If the gradient passed doesn't have the right dimension. - """ - if np.shape(self.state.x) != np.shape(tell_data.eval_jac): - raise ValueError("The gradient does not have the correct dimension") - self.state.x = self.state.x - next(self.state.learning_rate) * tell_data.eval_jac - self.state.stepsize = np.linalg.norm(tell_data.eval_jac) - self.state.nit += 1 - - def evaluate(self, ask_data: AskData) -> TellData: - """Evaluates the gradient. - - It does so either by evaluating an analytic gradient or by approximating it with a - finite difference scheme. It will either add ``1`` to the number of gradient evaluations or add - ``N+1`` to the number of function evaluations (Where N is the dimension of the gradient). - - Args: - ask_data: It contains the point where the gradient is to be evaluated and the gradient - function or, in its absence, the objective function to perform a finite difference - approximation. - - Returns: - The data containing the gradient evaluation. - """ - if self.state.jac is None: - eps = 0.01 if (self.perturbation is None) else self.perturbation - grad = Optimizer.gradient_num_diff( - x_center=ask_data.x_jac, - f=self.state.fun, - epsilon=eps, - max_evals_grouped=self._max_evals_grouped, - ) - self.state.nfev += 1 + len(ask_data.x_jac) - else: - grad = self.state.jac(ask_data.x_jac) - self.state.njev += 1 - - return TellData(eval_jac=grad) - - def create_result(self) -> OptimizerResult: - """Creates a result of the optimization process. - - This result contains the best point, the best function value, the number of function/gradient - evaluations and the number of iterations. - - Returns: - The result of the optimization process. - """ - result = OptimizerResult() - result.x = self.state.x - result.fun = self.state.fun(self.state.x) - result.nfev = self.state.nfev - result.njev = self.state.njev - result.nit = self.state.nit - return result - - def start( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> None: - - self.state = GradientDescentState( - fun=fun, - jac=jac, - x=np.asarray(x0), - nit=0, - nfev=0, - njev=0, - learning_rate=LearningRate(learning_rate=self.learning_rate), - stepsize=None, - ) - - def continue_condition(self) -> bool: - """ - Condition that indicates the optimization process should come to an end. - - When the stepsize is smaller than the tolerance, the optimization process is considered - finished. - - Returns: - ``True`` if the optimization process should continue, ``False`` otherwise. - """ - if self.state.stepsize is None: - return True - else: - return (self.state.stepsize > self.tol) and super().continue_condition() - - def get_support_level(self): - """Get the support level dictionary.""" - return { - "gradient": OptimizerSupportLevel.supported, - "bounds": OptimizerSupportLevel.ignored, - "initial_point": OptimizerSupportLevel.required, - } diff --git a/qiskit/algorithms/optimizers/gsls.py b/qiskit/algorithms/optimizers/gsls.py deleted file mode 100644 index 6ae423fb52c8..000000000000 --- a/qiskit/algorithms/optimizers/gsls.py +++ /dev/null @@ -1,378 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Line search with Gaussian-smoothed samples on a sphere.""" - -from __future__ import annotations - -import warnings - -from collections.abc import Callable -from typing import Any, SupportsFloat -import numpy as np - -from qiskit.utils import algorithm_globals -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT - - -class GSLS(Optimizer): - """Gaussian-smoothed Line Search. - - An implementation of the line search algorithm described in - https://arxiv.org/pdf/1905.01332.pdf, using gradient approximation - based on Gaussian-smoothed samples on a sphere. - - .. note:: - - This component has some function that is normally random. If you want to reproduce behavior - then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). - """ - - _OPTIONS = [ - "maxiter", - "max_eval", - "disp", - "sampling_radius", - "sample_size_factor", - "initial_step_size", - "min_step_size", - "step_size_multiplier", - "armijo_parameter", - "min_gradient_norm", - "max_failed_rejection_sampling", - ] - - # pylint: disable=unused-argument - def __init__( - self, - maxiter: int = 10000, - max_eval: int = 10000, - disp: bool = False, - sampling_radius: float = 1.0e-6, - sample_size_factor: int = 1, - initial_step_size: float = 1.0e-2, - min_step_size: float = 1.0e-10, - step_size_multiplier: float = 0.4, - armijo_parameter: float = 1.0e-1, - min_gradient_norm: float = 1e-8, - max_failed_rejection_sampling: int = 50, - ) -> None: - """ - Args: - maxiter: Maximum number of iterations. - max_eval: Maximum number of evaluations. - disp: Set to True to display convergence messages. - sampling_radius: Sampling radius to determine gradient estimate. - sample_size_factor: The size of the sample set at each iteration is this number - multiplied by the dimension of the problem, rounded to the nearest integer. - initial_step_size: Initial step size for the descent algorithm. - min_step_size: Minimum step size for the descent algorithm. - step_size_multiplier: Step size reduction after unsuccessful steps, in the - interval (0, 1). - armijo_parameter: Armijo parameter for sufficient decrease criterion, in the - interval (0, 1). - min_gradient_norm: If the gradient norm is below this threshold, the algorithm stops. - max_failed_rejection_sampling: Maximum number of attempts to sample points within - bounds. - """ - super().__init__() - for k, v in list(locals().items()): - if k in self._OPTIONS: - self._options[k] = v - - def get_support_level(self) -> dict[str, int]: - """Return support level dictionary. - - Returns: - A dictionary containing the support levels for different options. - """ - return { - "gradient": OptimizerSupportLevel.ignored, - "bounds": OptimizerSupportLevel.supported, - "initial_point": OptimizerSupportLevel.required, - } - - @property - def settings(self) -> dict[str, Any]: - return {key: self._options.get(key, None) for key in self._OPTIONS} - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - if not isinstance(x0, np.ndarray): - x0 = np.asarray(x0) - - if bounds is None: - var_lb = np.array([-np.inf] * x0.size) - var_ub = np.array([np.inf] * x0.size) - else: - var_lb = np.array([l for (l, _) in bounds]) - var_ub = np.array([u for (_, u) in bounds]) - - x, fun, nfev, _ = self.ls_optimize(x0.size, fun, x0, var_lb, var_ub) - - result = OptimizerResult() - result.x = x - result.fun = fun - result.nfev = nfev - - return result - - def ls_optimize( - self, - n: int, - obj_fun: Callable[[POINT], float], - initial_point: np.ndarray, - var_lb: np.ndarray, - var_ub: np.ndarray, - ) -> tuple[np.ndarray, float, int, float]: - """Run the line search optimization. - - Args: - n: Dimension of the problem. - obj_fun: Objective function. - initial_point: Initial point. - var_lb: Vector of lower bounds on the decision variables. Vector elements can be -np.inf - if the corresponding variable is unbounded from below. - var_ub: Vector of upper bounds on the decision variables. Vector elements can be np.inf - if the corresponding variable is unbounded from below. - - Returns: - Final iterate as a vector, corresponding objective function value, - number of evaluations, and norm of the gradient estimate. - - Raises: - ValueError: If the number of dimensions mismatches the size of the initial point or - the length of the lower or upper bound. - """ - if len(initial_point) != n: - raise ValueError("Size of the initial point mismatches the number of dimensions.") - if len(var_lb) != n: - raise ValueError("Length of the lower bound mismatches the number of dimensions.") - if len(var_ub) != n: - raise ValueError("Length of the upper bound mismatches the number of dimensions.") - - # Initialize counters and data - iter_count = 0 - n_evals = 0 - prev_iter_successful = True - prev_directions, prev_sample_set_x, prev_sample_set_y = None, None, None - consecutive_fail_iter = 0 - alpha = self._options["initial_step_size"] - grad_norm: SupportsFloat = np.inf - sample_set_size = int(round(self._options["sample_size_factor"] * n)) - - # Initial point - x = initial_point - x_value = obj_fun(x) - n_evals += 1 - while iter_count < self._options["maxiter"] and n_evals < self._options["max_eval"]: - - # Determine set of sample points - directions, sample_set_x = self.sample_set(n, x, var_lb, var_ub, sample_set_size) - - if n_evals + len(sample_set_x) + 1 >= self._options["max_eval"]: - # The evaluation budget is too small to allow for - # another full iteration; we therefore exit now - break - - sample_set_y = np.array([obj_fun(point) for point in sample_set_x]) - n_evals += len(sample_set_x) - - # Expand sample set if we could not improve - if not prev_iter_successful: - directions = np.vstack((prev_directions, directions)) - sample_set_x = np.vstack((prev_sample_set_x, sample_set_x)) - sample_set_y = np.hstack((prev_sample_set_y, sample_set_y)) - - # Find gradient approximation and candidate point - grad = self.gradient_approximation( - n, x, x_value, directions, sample_set_x, sample_set_y - ) - grad_norm = np.linalg.norm(grad) - new_x = np.clip(x - alpha * grad, var_lb, var_ub) - new_x_value = obj_fun(new_x) - n_evals += 1 - - # Print information - if self._options["disp"]: - print(f"Iter {iter_count:d}") - print(f"Point {x} obj {x_value}") - print(f"Gradient {grad}") - print(f"Grad norm {grad_norm} new_x_value {new_x_value} step_size {alpha}") - print(f"Direction {directions}") - - # Test Armijo condition for sufficient decrease - if new_x_value <= x_value - self._options["armijo_parameter"] * alpha * grad_norm: - # Accept point - x, x_value = new_x, new_x_value - alpha /= 2 * self._options["step_size_multiplier"] - prev_iter_successful = True - consecutive_fail_iter = 0 - - # Reset sample set - prev_directions = None - prev_sample_set_x = None - prev_sample_set_y = None - else: - # Do not accept point - alpha *= self._options["step_size_multiplier"] - prev_iter_successful = False - consecutive_fail_iter += 1 - - # Store sample set to enlarge it - prev_directions = directions - prev_sample_set_x, prev_sample_set_y = sample_set_x, sample_set_y - - iter_count += 1 - - # Check termination criterion - if ( - grad_norm <= self._options["min_gradient_norm"] - or alpha <= self._options["min_step_size"] - ): - break - - return x, x_value, n_evals, grad_norm - - def sample_points( - self, n: int, x: np.ndarray, num_points: int - ) -> tuple[np.ndarray, np.ndarray]: - """Sample ``num_points`` points around ``x`` on the ``n``-sphere of specified radius. - - The radius of the sphere is ``self._options['sampling_radius']``. - - Args: - n: Dimension of the problem. - x: Point around which the sample set is constructed. - num_points: Number of points in the sample set. - - Returns: - A tuple containing the sampling points and the directions. - """ - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - normal_samples = algorithm_globals.random.normal(size=(num_points, n)) - row_norms = np.linalg.norm(normal_samples, axis=1, keepdims=True) - directions = normal_samples / row_norms - points = x + self._options["sampling_radius"] * directions - - return points, directions - - def sample_set( - self, n: int, x: np.ndarray, var_lb: np.ndarray, var_ub: np.ndarray, num_points: int - ) -> tuple[np.ndarray, np.ndarray]: - """Construct sample set of given size. - - Args: - n: Dimension of the problem. - x: Point around which the sample set is constructed. - var_lb: Vector of lower bounds on the decision variables. Vector elements can be -np.inf - if the corresponding variable is unbounded from below. - var_ub: Vector of lower bounds on the decision variables. Vector elements can be np.inf - if the corresponding variable is unbounded from above. - num_points: Number of points in the sample set. - - Returns: - Matrices of (unit-norm) sample directions and sample points, one per row. - Both matrices are 2D arrays of floats. - - Raises: - RuntimeError: If not enough samples could be generated within the bounds. - """ - # Generate points uniformly on the sphere - points, directions = self.sample_points(n, x, num_points) - - # Check bounds - if (points >= var_lb).all() and (points <= var_ub).all(): - # If all points are within bounds, return them - return directions, (x + self._options["sampling_radius"] * directions) - else: - # Otherwise we perform rejection sampling until we have - # enough points that satisfy the bounds - indices = np.where((points >= var_lb).all(axis=1) & (points <= var_ub).all(axis=1))[0] - accepted = directions[indices] - num_trials = 0 - - while ( - len(accepted) < num_points - and num_trials < self._options["max_failed_rejection_sampling"] - ): - # Generate points uniformly on the sphere - points, directions = self.sample_points(n, x, num_points) - indices = np.where((points >= var_lb).all(axis=1) & (points <= var_ub).all(axis=1))[ - 0 - ] - accepted = np.vstack((accepted, directions[indices])) - num_trials += 1 - - # When we are at a corner point, the expected fraction of acceptable points may be - # exponential small in the dimension of the problem. Thus, if we keep failing and - # do not have enough points by now, we switch to a different method that guarantees - # finding enough points, but they may not be uniformly distributed. - if len(accepted) < num_points: - points, directions = self.sample_points(n, x, num_points) - to_be_flipped = (points < var_lb) | (points > var_ub) - directions *= np.where(to_be_flipped, -1, 1) - points = x + self._options["sampling_radius"] * directions - indices = np.where((points >= var_lb).all(axis=1) & (points <= var_ub).all(axis=1))[ - 0 - ] - accepted = np.vstack((accepted, directions[indices])) - - # If we still do not have enough sampling points, we have failed. - if len(accepted) < num_points: - raise RuntimeError( - "Could not generate enough samples within bounds; try smaller radius." - ) - - return ( - accepted[:num_points], - x + self._options["sampling_radius"] * accepted[:num_points], - ) - - def gradient_approximation( - self, - n: int, - x: np.ndarray, - x_value: float, - directions: np.ndarray, - sample_set_x: np.ndarray, - sample_set_y: np.ndarray, - ) -> np.ndarray: - """Construct gradient approximation from given sample. - - Args: - n: Dimension of the problem. - x: Point around which the sample set was constructed. - x_value: Objective function value at x. - directions: Directions of the sample points wrt the central point x, as a 2D array. - sample_set_x: x-coordinates of the sample set, one point per row, as a 2D array. - sample_set_y: Objective function values of the points in sample_set_x, as a 1D array. - - Returns: - Gradient approximation at x, as a 1D array. - """ - ffd = sample_set_y - x_value - gradient = ( - float(n) - / len(sample_set_y) - * np.sum( - ffd.reshape(len(sample_set_y), 1) / self._options["sampling_radius"] * directions, 0 - ) - ) - return gradient diff --git a/qiskit/algorithms/optimizers/imfil.py b/qiskit/algorithms/optimizers/imfil.py deleted file mode 100644 index 2fca4da2c139..000000000000 --- a/qiskit/algorithms/optimizers/imfil.py +++ /dev/null @@ -1,86 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""IMplicit FILtering (IMFIL) optimizer.""" -from __future__ import annotations - -from collections.abc import Callable -from typing import Any - -from qiskit.utils import optionals as _optionals -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT - - -@_optionals.HAS_SKQUANT.require_in_instance -class IMFIL(Optimizer): - """IMplicit FILtering algorithm. - - Implicit filtering is a way to solve bound-constrained optimization problems for - which derivatives are not available. In comparison to methods that use interpolation to - reconstruct the function and its higher derivatives, implicit filtering builds upon - coordinate search followed by interpolation to get an approximate gradient. - - Uses skquant.opt installed with pip install scikit-quant. - For further detail, please refer to - https://github.com/scikit-quant/scikit-quant and https://qat4chem.lbl.gov/software. - """ - - def __init__( - self, - maxiter: int = 1000, - ) -> None: - """ - Args: - maxiter: Maximum number of function evaluations. - - Raises: - MissingOptionalLibraryError: scikit-quant not installed - """ - super().__init__() - self._maxiter = maxiter - - def get_support_level(self): - """Returns support level dictionary.""" - return { - "gradient": OptimizerSupportLevel.ignored, - "bounds": OptimizerSupportLevel.required, - "initial_point": OptimizerSupportLevel.required, - } - - @property - def settings(self) -> dict[str, Any]: - return { - "maxiter": self._maxiter, - } - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - from skquant import opt as skq - - res, history = skq.minimize( - func=fun, - x0=x0, - bounds=bounds, - budget=self._maxiter, - method="imfil", - ) - - optimizer_result = OptimizerResult() - optimizer_result.x = res.optpar - optimizer_result.fun = res.optval - optimizer_result.nfev = len(history) - return optimizer_result diff --git a/qiskit/algorithms/optimizers/l_bfgs_b.py b/qiskit/algorithms/optimizers/l_bfgs_b.py deleted file mode 100644 index 3c2d7a619ef3..000000000000 --- a/qiskit/algorithms/optimizers/l_bfgs_b.py +++ /dev/null @@ -1,88 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Limited-memory BFGS Bound optimizer.""" - -from __future__ import annotations -from typing import SupportsFloat - -import numpy as np - -from .scipy_optimizer import SciPyOptimizer - - -class L_BFGS_B(SciPyOptimizer): # pylint: disable=invalid-name - """ - Limited-memory BFGS Bound optimizer. - - The target goal of Limited-memory Broyden-Fletcher-Goldfarb-Shanno Bound (L-BFGS-B) - is to minimize the value of a differentiable scalar function :math:`f`. - This optimizer is a quasi-Newton method, meaning that, in contrast to Newtons's method, - it does not require :math:`f`'s Hessian (the matrix of :math:`f`'s second derivatives) - when attempting to compute :math:`f`'s minimum value. - - Like BFGS, L-BFGS is an iterative method for solving unconstrained, non-linear optimization - problems, but approximates BFGS using a limited amount of computer memory. - L-BFGS starts with an initial estimate of the optimal value, and proceeds iteratively - to refine that estimate with a sequence of better estimates. - - The derivatives of :math:`f` are used to identify the direction of steepest descent, - and also to form an estimate of the Hessian matrix (second derivative) of :math:`f`. - L-BFGS-B extends L-BFGS to handle simple, per-variable bound constraints. - - Uses ``scipy.optimize.fmin_l_bfgs_b``. - For further detail, please refer to - https://docs.scipy.org/doc/scipy/reference/optimize.minimize-lbfgsb.html - """ - - _OPTIONS = ["maxfun", "maxiter", "ftol", "iprint", "eps"] - - # pylint: disable=unused-argument - def __init__( - self, - maxfun: int = 15000, - maxiter: int = 15000, - ftol: SupportsFloat = 10 * np.finfo(float).eps, - iprint: int = -1, - eps: float = 1e-08, - options: dict | None = None, - max_evals_grouped: int = 1, - **kwargs, - ): - r""" - Args: - maxfun: Maximum number of function evaluations. - maxiter: Maximum number of iterations. - ftol: The iteration stops when - :math:`(f^k - f^{k+1}) / \max\{|f^k|, |f^{k+1}|,1\} \leq \text{ftol}`. - iprint: Controls the frequency of output. ``iprint < 0`` means no output; - ``iprint = 0`` print only one line at the last iteration; ``0 < iprint < 99`` - print also :math:`f` and :math:`|\text{proj} g|` every iprint iterations; - ``iprint = 99`` print details of every iteration except n-vectors; ``iprint = 100`` - print also the changes of active set and final :math:`x`; ``iprint > 100`` print - details of every iteration including :math:`x` and :math:`g`. - eps: If jac is approximated, use this value for the step size. - options: A dictionary of solver options. - max_evals_grouped: Max number of default gradient evaluations performed simultaneously. - kwargs: additional kwargs for ``scipy.optimize.minimize``. - """ - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__( - method="L-BFGS-B", - options=options, - max_evals_grouped=max_evals_grouped, - **kwargs, - ) diff --git a/qiskit/algorithms/optimizers/nelder_mead.py b/qiskit/algorithms/optimizers/nelder_mead.py deleted file mode 100644 index ff9d8708763f..000000000000 --- a/qiskit/algorithms/optimizers/nelder_mead.py +++ /dev/null @@ -1,73 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Nelder-Mead optimizer.""" -from __future__ import annotations - - -from .scipy_optimizer import SciPyOptimizer - - -class NELDER_MEAD(SciPyOptimizer): # pylint: disable=invalid-name - """ - Nelder-Mead optimizer. - - The Nelder-Mead algorithm performs unconstrained optimization; it ignores bounds - or constraints. It is used to find the minimum or maximum of an objective function - in a multidimensional space. It is based on the Simplex algorithm. Nelder-Mead - is robust in many applications, especially when the first and second derivatives of the - objective function are not known. - - However, if the numerical computation of the derivatives can be trusted to be accurate, - other algorithms using the first and/or second derivatives information might be preferred to - Nelder-Mead for their better performance in the general case, especially in consideration of - the fact that the Nelder–Mead technique is a heuristic search method that can converge to - non-stationary points. - - Uses scipy.optimize.minimize Nelder-Mead. - For further detail, please refer to - See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html - """ - - _OPTIONS = ["maxiter", "maxfev", "disp", "xatol", "adaptive"] - - # pylint: disable=unused-argument - def __init__( - self, - maxiter: int | None = None, - maxfev: int = 1000, - disp: bool = False, - xatol: float = 0.0001, - tol: float | None = None, - adaptive: bool = False, - options: dict | None = None, - **kwargs, - ) -> None: - """ - Args: - maxiter: Maximum allowed number of iterations. If both maxiter and maxfev are set, - minimization will stop at the first reached. - maxfev: Maximum allowed number of function evaluations. If both maxiter and - maxfev are set, minimization will stop at the first reached. - disp: Set to True to print convergence messages. - xatol: Absolute error in xopt between iterations that is acceptable for convergence. - tol: Tolerance for termination. - adaptive: Adapt algorithm parameters to dimensionality of problem. - options: A dictionary of solver options. - kwargs: additional kwargs for scipy.optimize.minimize. - """ - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__(method="Nelder-Mead", options=options, tol=tol, **kwargs) diff --git a/qiskit/algorithms/optimizers/nft.py b/qiskit/algorithms/optimizers/nft.py deleted file mode 100644 index 2a7503137daf..000000000000 --- a/qiskit/algorithms/optimizers/nft.py +++ /dev/null @@ -1,170 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Nakanishi-Fujii-Todo algorithm.""" -from __future__ import annotations - - -import numpy as np -from scipy.optimize import OptimizeResult - -from .scipy_optimizer import SciPyOptimizer - - -class NFT(SciPyOptimizer): - """ - Nakanishi-Fujii-Todo algorithm. - - See https://arxiv.org/abs/1903.12166 - """ - - _OPTIONS = ["maxiter", "maxfev", "disp", "reset_interval"] - - # pylint: disable=unused-argument - def __init__( - self, - maxiter: int | None = None, - maxfev: int = 1024, - disp: bool = False, - reset_interval: int = 32, - options: dict | None = None, - **kwargs, - ) -> None: - """ - Built out using scipy framework, for details, please refer to - https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html. - - Args: - maxiter: Maximum number of iterations to perform. - maxfev: Maximum number of function evaluations to perform. - disp: disp - reset_interval: The minimum estimates directly once - in ``reset_interval`` times. - options: A dictionary of solver options. - kwargs: additional kwargs for scipy.optimize.minimize. - - Notes: - In this optimization method, the optimization function have to satisfy - three conditions written in [1]_. - - References: - .. [1] K. M. Nakanishi, K. Fujii, and S. Todo. 2019. - Sequential minimal optimization for quantum-classical hybrid algorithms. - arXiv preprint arXiv:1903.12166. - """ - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__(method=nakanishi_fujii_todo, options=options, **kwargs) - - -# pylint: disable=invalid-name -def nakanishi_fujii_todo( - fun, x0, args=(), maxiter=None, maxfev=1024, reset_interval=32, eps=1e-32, callback=None, **_ -): - """ - Find the global minimum of a function using the nakanishi_fujii_todo - algorithm [1]. - Args: - fun (callable): ``f(x, *args)`` - Function to be optimized. ``args`` can be passed as an optional item - in the dict ``minimizer_kwargs``. - This function must satisfy the three condition written in Ref. [1]. - x0 (ndarray): shape (n,) - Initial guess. Array of real elements of size (n,), - where 'n' is the number of independent variables. - args (tuple, optional): - Extra arguments passed to the objective function. - maxiter (int): - Maximum number of iterations to perform. - Default: None. - maxfev (int): - Maximum number of function evaluations to perform. - Default: 1024. - reset_interval (int): - The minimum estimates directly once in ``reset_interval`` times. - Default: 32. - eps (float): eps - **_ : additional options - callback (callable, optional): - Called after each iteration. - Returns: - OptimizeResult: - The optimization result represented as a ``OptimizeResult`` object. - Important attributes are: ``x`` the solution array. See - `OptimizeResult` for a description of other attributes. - Notes: - In this optimization method, the optimization function have to satisfy - three conditions written in [2]_. - - References: - .. [2] K. M. Nakanishi, K. Fujii, and S. Todo. 2019. - Sequential minimal optimization for quantum-classical hybrid algorithms. - arXiv preprint arXiv:1903.12166. - """ - - x0 = np.asarray(x0) - recycle_z0 = None - niter = 0 - funcalls = 0 - - while True: - - idx = niter % x0.size - - if reset_interval > 0: - if niter % reset_interval == 0: - recycle_z0 = None - - if recycle_z0 is None: - z0 = fun(np.copy(x0), *args) - funcalls += 1 - else: - z0 = recycle_z0 - - p = np.copy(x0) - p[idx] = x0[idx] + np.pi / 2 - z1 = fun(p, *args) - funcalls += 1 - - p = np.copy(x0) - p[idx] = x0[idx] - np.pi / 2 - z3 = fun(p, *args) - funcalls += 1 - - z2 = z1 + z3 - z0 - c = (z1 + z3) / 2 - a = np.sqrt((z0 - z2) ** 2 + (z1 - z3) ** 2) / 2 - b = np.arctan((z1 - z3) / ((z0 - z2) + eps * (z0 == z2))) + x0[idx] - b += 0.5 * np.pi + 0.5 * np.pi * np.sign((z0 - z2) + eps * (z0 == z2)) - - x0[idx] = b - recycle_z0 = c - a - - niter += 1 - - if callback is not None: - callback(np.copy(x0)) - - if maxfev is not None: - if funcalls >= maxfev: - break - - if maxiter is not None: - if niter >= maxiter: - break - - return OptimizeResult( - fun=fun(np.copy(x0), *args), x=x0, nit=niter, nfev=funcalls, success=(niter > 1) - ) diff --git a/qiskit/algorithms/optimizers/nlopts/__init__.py b/qiskit/algorithms/optimizers/nlopts/__init__.py deleted file mode 100644 index e3165bc0b482..000000000000 --- a/qiskit/algorithms/optimizers/nlopts/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""NLopt based global optimizers""" diff --git a/qiskit/algorithms/optimizers/nlopts/crs.py b/qiskit/algorithms/optimizers/nlopts/crs.py deleted file mode 100644 index 77eb67b298b6..000000000000 --- a/qiskit/algorithms/optimizers/nlopts/crs.py +++ /dev/null @@ -1,35 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Controlled Random Search (CRS) with local mutation optimizer.""" - -from .nloptimizer import NLoptOptimizer, NLoptOptimizerType - - -class CRS(NLoptOptimizer): - """ - Controlled Random Search (CRS) with local mutation optimizer. - - Controlled Random Search (CRS) with local mutation is part of the family of the CRS optimizers. - The CRS optimizers start with a random population of points, and randomly evolve these points - by heuristic rules. In the case of CRS with local mutation, the evolution is a randomized - version of the :class:`NELDER_MEAD` local optimizer. - - - NLopt global optimizer, derivative-free. - For further detail, please refer to - https://nlopt.readthedocs.io/en/latest/NLopt_Algorithms/#controlled-random-search-crs-with-local-mutation - """ - - def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """Return NLopt optimizer type""" - return NLoptOptimizerType.GN_CRS2_LM diff --git a/qiskit/algorithms/optimizers/nlopts/direct_l.py b/qiskit/algorithms/optimizers/nlopts/direct_l.py deleted file mode 100644 index e7ed9be3e25f..000000000000 --- a/qiskit/algorithms/optimizers/nlopts/direct_l.py +++ /dev/null @@ -1,34 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""DIviding RECTangles Locally-biased optimizer.""" - -from .nloptimizer import NLoptOptimizer, NLoptOptimizerType - - -class DIRECT_L(NLoptOptimizer): # pylint: disable=invalid-name - """ - DIviding RECTangles Locally-biased optimizer. - - DIviding RECTangles (DIRECT) is a deterministic-search algorithms based on systematic division - of the search domain into increasingly smaller hyper-rectangles. - The DIRECT-L version is a "locally biased" variant of DIRECT that makes the algorithm more - biased towards local search, so that it is more efficient for functions with few local minima. - - NLopt global optimizer, derivative-free. - For further detail, please refer to - http://nlopt.readthedocs.io/en/latest/NLopt_Algorithms/#direct-and-direct-l - """ - - def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """Return NLopt optimizer type""" - return NLoptOptimizerType.GN_DIRECT_L diff --git a/qiskit/algorithms/optimizers/nlopts/direct_l_rand.py b/qiskit/algorithms/optimizers/nlopts/direct_l_rand.py deleted file mode 100644 index 15172ef00880..000000000000 --- a/qiskit/algorithms/optimizers/nlopts/direct_l_rand.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""DIviding RECTangles Locally-biased Randomized optimizer.""" - -from .nloptimizer import NLoptOptimizer, NLoptOptimizerType - - -class DIRECT_L_RAND(NLoptOptimizer): # pylint: disable=invalid-name - """ - DIviding RECTangles Locally-biased Randomized optimizer. - - DIRECT-L RAND is the "locally biased" variant with some randomization in near-tie decisions. - See also :class:`DIRECT_L` - - NLopt global optimizer, derivative-free. - For further detail, please refer to - http://nlopt.readthedocs.io/en/latest/NLopt_Algorithms/#direct-and-direct-l - """ - - def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """Return NLopt optimizer type""" - return NLoptOptimizerType.GN_DIRECT_L_RAND diff --git a/qiskit/algorithms/optimizers/nlopts/esch.py b/qiskit/algorithms/optimizers/nlopts/esch.py deleted file mode 100644 index 7e754f9447fe..000000000000 --- a/qiskit/algorithms/optimizers/nlopts/esch.py +++ /dev/null @@ -1,33 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""ESCH evolutionary optimizer.""" - -from .nloptimizer import NLoptOptimizer, NLoptOptimizerType - - -class ESCH(NLoptOptimizer): - """ - ESCH evolutionary optimizer. - - ESCH is an evolutionary algorithm for global optimization that supports bound constraints only. - Specifically, it does not support nonlinear constraints. - - NLopt global optimizer, derivative-free. - For further detail, please refer to - - http://nlopt.readthedocs.io/en/latest/NLopt_Algorithms/#esch-evolutionary-algorithm - """ - - def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """Return NLopt optimizer type""" - return NLoptOptimizerType.GN_ESCH diff --git a/qiskit/algorithms/optimizers/nlopts/isres.py b/qiskit/algorithms/optimizers/nlopts/isres.py deleted file mode 100644 index 1c37a9401e3a..000000000000 --- a/qiskit/algorithms/optimizers/nlopts/isres.py +++ /dev/null @@ -1,39 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Improved Stochastic Ranking Evolution Strategy optimizer.""" - -from .nloptimizer import NLoptOptimizer, NLoptOptimizerType - - -class ISRES(NLoptOptimizer): - """ - Improved Stochastic Ranking Evolution Strategy optimizer. - - Improved Stochastic Ranking Evolution Strategy (ISRES) is an algorithm for - non-linearly constrained global optimization. It has heuristics to escape local optima, - even though convergence to a global optima is not guaranteed. The evolution strategy is based - on a combination of a mutation rule and differential variation. The fitness ranking is simply - via the objective function for problems without nonlinear constraints. When nonlinear - constraints are included, the `stochastic ranking proposed by Runarsson and Yao - `__ - is employed. This method supports arbitrary nonlinear inequality and equality constraints, in - addition to the bound constraints. - - NLopt global optimizer, derivative-free. - For further detail, please refer to - http://nlopt.readthedocs.io/en/latest/NLopt_Algorithms/#isres-improved-stochastic-ranking-evolution-strategy - """ - - def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """Return NLopt optimizer type""" - return NLoptOptimizerType.GN_ISRES diff --git a/qiskit/algorithms/optimizers/nlopts/nloptimizer.py b/qiskit/algorithms/optimizers/nlopts/nloptimizer.py deleted file mode 100644 index 65f56b930482..000000000000 --- a/qiskit/algorithms/optimizers/nlopts/nloptimizer.py +++ /dev/null @@ -1,131 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Minimize using objective function""" -from __future__ import annotations - -from collections.abc import Callable -from enum import Enum -from abc import abstractmethod -import logging -import numpy as np - -from qiskit.utils import optionals as _optionals -from ..optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT - -logger = logging.getLogger(__name__) - - -class NLoptOptimizerType(Enum): - """NLopt Valid Optimizer""" - - GN_CRS2_LM = 1 - GN_DIRECT_L_RAND = 2 - GN_DIRECT_L = 3 - GN_ESCH = 4 - GN_ISRES = 5 - - -@_optionals.HAS_NLOPT.require_in_instance -class NLoptOptimizer(Optimizer): - """ - NLopt global optimizer base class - """ - - _OPTIONS = ["max_evals"] - - def __init__(self, max_evals: int = 1000) -> None: # pylint: disable=unused-argument - """ - Args: - max_evals: Maximum allowed number of function evaluations. - - Raises: - MissingOptionalLibraryError: NLopt library not installed. - """ - import nlopt - - super().__init__() - for k, v in list(locals().items()): - if k in self._OPTIONS: - self._options[k] = v - - self._optimizer_names = { - NLoptOptimizerType.GN_CRS2_LM: nlopt.GN_CRS2_LM, - NLoptOptimizerType.GN_DIRECT_L_RAND: nlopt.GN_DIRECT_L_RAND, - NLoptOptimizerType.GN_DIRECT_L: nlopt.GN_DIRECT_L, - NLoptOptimizerType.GN_ESCH: nlopt.GN_ESCH, - NLoptOptimizerType.GN_ISRES: nlopt.GN_ISRES, - } - - @abstractmethod - def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """return NLopt optimizer enum type""" - raise NotImplementedError - - def get_support_level(self): - """return support level dictionary""" - return { - "gradient": OptimizerSupportLevel.ignored, - "bounds": OptimizerSupportLevel.supported, - "initial_point": OptimizerSupportLevel.required, - } - - @property - def settings(self): - return {"max_evals": self._options.get("max_evals", 1000)} - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - import nlopt - - x0 = np.asarray(x0) - - if bounds is None: - bounds = [(None, None)] * x0.size - - threshold = 3 * np.pi - low = [(l if l is not None else -threshold) for (l, u) in bounds] - high = [(u if u is not None else threshold) for (l, u) in bounds] - - name = self._optimizer_names[self.get_nlopt_optimizer()] - opt = nlopt.opt(name, len(low)) - logger.debug(opt.get_algorithm_name()) - - opt.set_lower_bounds(low) - opt.set_upper_bounds(high) - - eval_count = 0 - - def wrap_objfunc_global(x, _grad): - nonlocal eval_count - eval_count += 1 - return fun(x) - - opt.set_min_objective(wrap_objfunc_global) - opt.set_maxeval(self._options.get("max_evals", 1000)) - - xopt = opt.optimize(x0) - minf = opt.last_optimum_value() - - logger.debug("Global minimize found %s eval count %s", minf, eval_count) - - result = OptimizerResult() - result.x = xopt - result.fun = minf - result.nfev = eval_count - - return result diff --git a/qiskit/algorithms/optimizers/optimizer.py b/qiskit/algorithms/optimizers/optimizer.py deleted file mode 100644 index e253167b5d9d..000000000000 --- a/qiskit/algorithms/optimizers/optimizer.py +++ /dev/null @@ -1,389 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Optimizer interface""" - -from __future__ import annotations - -from abc import ABC, abstractmethod -from collections.abc import Callable -from enum import IntEnum -import logging -from typing import Any, Union, Protocol - -import numpy as np -import scipy - -from qiskit.algorithms.algorithm_result import AlgorithmResult - -logger = logging.getLogger(__name__) - -POINT = Union[float, np.ndarray] - - -class OptimizerResult(AlgorithmResult): - """The result of an optimization routine.""" - - def __init__(self) -> None: - super().__init__() - self._x: POINT | None = None - self._fun: float | None = None - self._jac: POINT | None = None - self._nfev: int | None = None - self._njev: int | None = None - self._nit: int | None = None - - @property - def x(self) -> POINT | None: - """The final point of the minimization.""" - return self._x - - @x.setter - def x(self, x: POINT | None) -> None: - """Set the final point of the minimization.""" - self._x = x - - @property - def fun(self) -> float | None: - """The final value of the minimization.""" - return self._fun - - @fun.setter - def fun(self, fun: float | None) -> None: - """Set the final value of the minimization.""" - self._fun = fun - - @property - def jac(self) -> POINT | None: - """The final gradient of the minimization.""" - return self._jac - - @jac.setter - def jac(self, jac: POINT | None) -> None: - """Set the final gradient of the minimization.""" - self._jac = jac - - @property - def nfev(self) -> int | None: - """The total number of function evaluations.""" - return self._nfev - - @nfev.setter - def nfev(self, nfev: int | None) -> None: - """Set the total number of function evaluations.""" - self._nfev = nfev - - @property - def njev(self) -> int | None: - """The total number of gradient evaluations.""" - return self._njev - - @njev.setter - def njev(self, njev: int | None) -> None: - """Set the total number of gradient evaluations.""" - self._njev = njev - - @property - def nit(self) -> int | None: - """The total number of iterations.""" - return self._nit - - @nit.setter - def nit(self, nit: int | None) -> None: - """Set the total number of iterations.""" - self._nit = nit - - -class Minimizer(Protocol): - """Callable Protocol for minimizer. - - This interface is based on `SciPy's optimize module - `__. - - This protocol defines a callable taking the following parameters: - - fun - The objective function to minimize (for example the energy in the case of the VQE). - x0 - The initial point for the optimization. - jac - The gradient of the objective function. - bounds - Parameters bounds for the optimization. Note that these might not be supported - by all optimizers. - - and which returns a minimization result object (either SciPy's or Qiskit's). - """ - - # pylint: disable=invalid-name - def __call__( - self, - fun: Callable[[np.ndarray], float], - x0: np.ndarray, - jac: Callable[[np.ndarray], np.ndarray] | None, - bounds: list[tuple[float, float]] | None, - ) -> scipy.optimize.OptimizeResult | OptimizerResult: - """Minimize the objective function. - - This interface is based on `SciPy's optimize module `__. - - Args: - fun: The objective function to minimize (for example the energy in the case of the VQE). - x0: The initial point for the optimization. - jac: The gradient of the objective function. - bounds: Parameters bounds for the optimization. Note that these might not be supported - by all optimizers. - - Returns: - The minimization result object (either SciPy's or Qiskit's). - """ - ... - - -class OptimizerSupportLevel(IntEnum): - """Support Level enum for features such as bounds, gradient and initial point""" - - # pylint: disable=invalid-name - not_supported = 0 # Does not support the corresponding parameter in optimize() - ignored = 1 # Feature can be passed as non None but will be ignored - supported = 2 # Feature is supported - required = 3 # Feature is required and must be given, None is invalid - - -class Optimizer(ABC): - """Base class for optimization algorithm.""" - - @abstractmethod - def __init__(self): - """ - Initialize the optimization algorithm, setting the support - level for _gradient_support_level, _bound_support_level, - _initial_point_support_level, and empty options. - """ - self._gradient_support_level = self.get_support_level()["gradient"] - self._bounds_support_level = self.get_support_level()["bounds"] - self._initial_point_support_level = self.get_support_level()["initial_point"] - self._options = {} - self._max_evals_grouped = None - - @abstractmethod - def get_support_level(self): - """Return support level dictionary""" - raise NotImplementedError - - def set_options(self, **kwargs): - """ - Sets or updates values in the options dictionary. - - The options dictionary may be used internally by a given optimizer to - pass additional optional values for the underlying optimizer/optimization - function used. The options dictionary may be initially populated with - a set of key/values when the given optimizer is constructed. - - Args: - kwargs (dict): options, given as name=value. - """ - for name, value in kwargs.items(): - self._options[name] = value - logger.debug("options: %s", self._options) - - # pylint: disable=invalid-name - @staticmethod - def gradient_num_diff(x_center, f, epsilon, max_evals_grouped=None): - """ - We compute the gradient with the numeric differentiation in the parallel way, - around the point x_center. - - Args: - x_center (ndarray): point around which we compute the gradient - f (func): the function of which the gradient is to be computed. - epsilon (float): the epsilon used in the numeric differentiation. - max_evals_grouped (int): max evals grouped, defaults to 1 (i.e. no batching). - Returns: - grad: the gradient computed - - """ - if max_evals_grouped is None: # no batching by default - max_evals_grouped = 1 - - forig = f(*((x_center,))) - grad = [] - ei = np.zeros((len(x_center),), float) - todos = [] - for k in range(len(x_center)): - ei[k] = 1.0 - d = epsilon * ei - todos.append(x_center + d) - ei[k] = 0.0 - - counter = 0 - chunk = [] - chunks = [] - length = len(todos) - # split all points to chunks, where each chunk has batch_size points - for i in range(length): - x = todos[i] - chunk.append(x) - counter += 1 - # the last one does not have to reach batch_size - if counter == max_evals_grouped or i == length - 1: - chunks.append(chunk) - chunk = [] - counter = 0 - - for chunk in chunks: # eval the chunks in order - parallel_parameters = np.concatenate(chunk) - todos_results = f(parallel_parameters) # eval the points in a chunk (order preserved) - if isinstance(todos_results, float): - grad.append((todos_results - forig) / epsilon) - else: - for todor in todos_results: - grad.append((todor - forig) / epsilon) - - return np.array(grad) - - @staticmethod - def wrap_function(function, args): - """ - Wrap the function to implicitly inject the args at the call of the function. - - Args: - function (func): the target function - args (tuple): the args to be injected - Returns: - function_wrapper: wrapper - """ - - def function_wrapper(*wrapper_args): - return function(*(wrapper_args + args)) - - return function_wrapper - - @property - def setting(self): - """Return setting""" - ret = f"Optimizer: {self.__class__.__name__}\n" - params = "" - for key, value in self.__dict__.items(): - if key[0] == "_": - params += f"-- {key[1:]}: {value}\n" - ret += f"{params}" - return ret - - @property - def settings(self) -> dict[str, Any]: - """The optimizer settings in a dictionary format. - - The settings can for instance be used for JSON-serialization (if all settings are - serializable, which e.g. doesn't hold per default for callables), such that the - optimizer object can be reconstructed as - - .. code-block:: - - settings = optimizer.settings - # JSON serialize and send to another server - optimizer = OptimizerClass(**settings) - - """ - raise NotImplementedError("The settings method is not implemented per default.") - - @abstractmethod - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - """Minimize the scalar function. - - Args: - fun: The scalar function to minimize. - x0: The initial point for the minimization. - jac: The gradient of the scalar function ``fun``. - bounds: Bounds for the variables of ``fun``. This argument might be ignored if the - optimizer does not support bounds. - - Returns: - The result of the optimization, containing e.g. the result as attribute ``x``. - """ - raise NotImplementedError() - - @property - def gradient_support_level(self): - """Returns gradient support level""" - return self._gradient_support_level - - @property - def is_gradient_ignored(self): - """Returns is gradient ignored""" - return self._gradient_support_level == OptimizerSupportLevel.ignored - - @property - def is_gradient_supported(self): - """Returns is gradient supported""" - return self._gradient_support_level != OptimizerSupportLevel.not_supported - - @property - def is_gradient_required(self): - """Returns is gradient required""" - return self._gradient_support_level == OptimizerSupportLevel.required - - @property - def bounds_support_level(self): - """Returns bounds support level""" - return self._bounds_support_level - - @property - def is_bounds_ignored(self): - """Returns is bounds ignored""" - return self._bounds_support_level == OptimizerSupportLevel.ignored - - @property - def is_bounds_supported(self): - """Returns is bounds supported""" - return self._bounds_support_level != OptimizerSupportLevel.not_supported - - @property - def is_bounds_required(self): - """Returns is bounds required""" - return self._bounds_support_level == OptimizerSupportLevel.required - - @property - def initial_point_support_level(self): - """Returns initial point support level""" - return self._initial_point_support_level - - @property - def is_initial_point_ignored(self): - """Returns is initial point ignored""" - return self._initial_point_support_level == OptimizerSupportLevel.ignored - - @property - def is_initial_point_supported(self): - """Returns is initial point supported""" - return self._initial_point_support_level != OptimizerSupportLevel.not_supported - - @property - def is_initial_point_required(self): - """Returns is initial point required""" - return self._initial_point_support_level == OptimizerSupportLevel.required - - def print_options(self): - """Print algorithm-specific options.""" - for name in sorted(self._options): - logger.debug("%s = %s", name, str(self._options[name])) - - def set_max_evals_grouped(self, limit): - """Set max evals grouped""" - self._max_evals_grouped = limit diff --git a/qiskit/algorithms/optimizers/optimizer_utils/__init__.py b/qiskit/algorithms/optimizers/optimizer_utils/__init__.py deleted file mode 100644 index 33c5bc90b087..000000000000 --- a/qiskit/algorithms/optimizers/optimizer_utils/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Utils for optimizers - -Optimizer Utils (:mod:`qiskit.algorithms.optimizers.optimizer_utils`) -===================================================================== - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - LearningRate - -""" - -from .learning_rate import LearningRate - -__all__ = ["LearningRate"] diff --git a/qiskit/algorithms/optimizers/optimizer_utils/learning_rate.py b/qiskit/algorithms/optimizers/optimizer_utils/learning_rate.py deleted file mode 100644 index 7bfea636ce2c..000000000000 --- a/qiskit/algorithms/optimizers/optimizer_utils/learning_rate.py +++ /dev/null @@ -1,88 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A class to represent the Learning Rate.""" -from __future__ import annotations - -from collections.abc import Generator, Callable -from itertools import tee -import numpy as np - - -class LearningRate(Generator): - """Represents a Learning Rate. - Will be an attribute of :class:`~.GradientDescentState`. Note that :class:`~.GradientDescent` also - has a learning rate. That learning rate can be a float, a list, an array, a function returning - a generator and will be used to create a generator to be used during the - optimization process. - This class wraps ``Generator`` so that we can also access the last yielded value. - """ - - def __init__( - self, - learning_rate: float - | list[float] - | np.ndarray - | Callable[[], Generator[float, None, None]], - ): - """ - Args: - learning_rate: Used to create a generator to iterate on. - """ - if isinstance(learning_rate, (float, int)): - self._gen = constant(learning_rate) - elif isinstance(learning_rate, Generator): - learning_rate, self._gen = tee(learning_rate) - elif isinstance(learning_rate, (list, np.ndarray)): - self._gen = (eta for eta in learning_rate) - else: - self._gen = learning_rate() - - self._current: float | None = None - - def send(self, value): - """Send a value into the generator. - Return next yielded value or raise StopIteration. - """ - self._current = next(self._gen) - return self.current - - def throw(self, typ, val=None, tb=None): - """Raise an exception in the generator. - Return next yielded value or raise StopIteration. - """ - if val is None: - if tb is None: - raise typ - val = typ() - if tb is not None: - val = val.with_traceback(tb) - raise val - - @property - def current(self): - """Returns the current value of the learning rate.""" - return self._current - - -def constant(learning_rate: float = 0.01) -> Generator[float, None, None]: - """Returns a python generator that always yields the same value. - - Args: - learning_rate: The value to yield. - - Yields: - The learning rate for the next iteration. - """ - - while True: - yield learning_rate diff --git a/qiskit/algorithms/optimizers/p_bfgs.py b/qiskit/algorithms/optimizers/p_bfgs.py deleted file mode 100644 index f166160d98de..000000000000 --- a/qiskit/algorithms/optimizers/p_bfgs.py +++ /dev/null @@ -1,182 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Parallelized Limited-memory BFGS optimizer""" -from __future__ import annotations - -import logging -import multiprocessing -import platform -import warnings -from collections.abc import Callable -from typing import SupportsFloat - -import numpy as np - -from qiskit.utils import algorithm_globals -from qiskit.utils.validation import validate_min - -from .optimizer import OptimizerResult, POINT -from .scipy_optimizer import SciPyOptimizer - -logger = logging.getLogger(__name__) - - -class P_BFGS(SciPyOptimizer): # pylint: disable=invalid-name - """ - Parallelized Limited-memory BFGS optimizer. - - P-BFGS is a parallelized version of :class:`L_BFGS_B` with which it shares the same parameters. - P-BFGS can be useful when the target hardware is a quantum simulator running on a classical - machine. This allows the multiple processes to use simulation to potentially reach a minimum - faster. The parallelization may also help the optimizer avoid getting stuck at local optima. - - Uses scipy.optimize.fmin_l_bfgs_b. - For further detail, please refer to - https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html - """ - - _OPTIONS = ["maxfun", "ftol", "iprint"] - - # pylint: disable=unused-argument - def __init__( - self, - maxfun: int = 1000, - ftol: SupportsFloat = 10 * np.finfo(float).eps, - iprint: int = -1, - max_processes: int | None = None, - options: dict | None = None, - max_evals_grouped: int = 1, - **kwargs, - ) -> None: - r""" - Args: - maxfun: Maximum number of function evaluations. - ftol: The iteration stops when (f\^k - f\^{k+1})/max{\|f\^k\|,\|f\^{k+1}\|,1} <= ftol. - iprint: Controls the frequency of output. iprint < 0 means no output; - iprint = 0 print only one line at the last iteration; 0 < iprint < 99 - print also f and \|proj g\| every iprint iterations; iprint = 99 print - details of every iteration except n-vectors; iprint = 100 print also the - changes of active set and final x; iprint > 100 print details of - every iteration including x and g. - max_processes: maximum number of processes allowed, has a min. value of 1 if not None. - options: A dictionary of solver options. - max_evals_grouped: Max number of default gradient evaluations performed simultaneously. - kwargs: additional kwargs for scipy.optimize.minimize. - """ - if max_processes: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - validate_min("max_processes", max_processes, 1) - - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__( - method="L-BFGS-B", - options=options, - max_evals_grouped=max_evals_grouped, - **kwargs, - ) - self._max_processes = max_processes - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - x0 = np.asarray(x0) - - num_procs = multiprocessing.cpu_count() - 1 - num_procs = ( - num_procs if self._max_processes is None else min(num_procs, self._max_processes) - ) - num_procs = num_procs if num_procs >= 0 else 0 - - if platform.system() == "Darwin": - # Changed in version 3.8: On macOS, the spawn start method is now the - # default. The fork start method should be considered unsafe as it can - # lead to crashes. - # However P_BFGS doesn't support spawn, so we revert to single process. - num_procs = 0 - logger.warning( - "For MacOS, python >= 3.8, using only current process. " - "Multiple core use not supported." - ) - elif platform.system() == "Windows": - num_procs = 0 - logger.warning( - "For Windows, using only current process. Multiple core use not supported." - ) - - queue: multiprocessing.queues.Queue[tuple[POINT, float, int]] = multiprocessing.Queue() - - # TODO: are automatic bounds a good idea? What if the circuit parameters are not - # just from plain Pauli rotations but have a coefficient? - - # bounds for additional initial points in case bounds has any None values - threshold = 2 * np.pi - if bounds is None: - bounds = [(-threshold, threshold)] * x0.size - low = [(l if l is not None else -threshold) for (l, u) in bounds] - high = [(u if u is not None else threshold) for (l, u) in bounds] - - def optimize_runner(_queue, _i_pt): # Multi-process sampling - _sol, _opt, _nfev = self._optimize(fun, _i_pt, jac, bounds) - _queue.put((_sol, _opt, _nfev)) - - # Start off as many other processes running the optimize (can be 0) - processes = [] - for _ in range(num_procs): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - i_pt = algorithm_globals.random.uniform(low, high) # Another random point in bounds - proc = multiprocessing.Process(target=optimize_runner, args=(queue, i_pt)) - processes.append(proc) - proc.start() - - # While the one optimize in this process below runs the other processes will - # be running too. This one runs - # with the supplied initial point. The process ones have their own random one - sol, opt, nfev = self._optimize(fun, x0, jac, bounds) - - for proc in processes: - # For each other process we wait now for it to finish and see if it has - # a better result than above - proc.join() - p_sol, p_opt, p_nfev = queue.get() - if p_opt < opt: - sol, opt = p_sol, p_opt - nfev += p_nfev - - result = OptimizerResult() - result.x = sol - result.fun = opt - result.nfev = nfev - - return result - - def _optimize( - self, - objective_function, - initial_point, - gradient_function=None, - variable_bounds=None, - ) -> tuple[POINT, float, int]: - result = super().minimize( - objective_function, initial_point, gradient_function, variable_bounds - ) - return result.x, result.fun, result.nfev diff --git a/qiskit/algorithms/optimizers/powell.py b/qiskit/algorithms/optimizers/powell.py deleted file mode 100644 index de8cbb1b9d18..000000000000 --- a/qiskit/algorithms/optimizers/powell.py +++ /dev/null @@ -1,64 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Powell optimizer.""" -from __future__ import annotations - -from .scipy_optimizer import SciPyOptimizer - - -class POWELL(SciPyOptimizer): - """ - Powell optimizer. - - The Powell algorithm performs unconstrained optimization; it ignores bounds or - constraints. Powell is a *conjugate direction method*: it performs sequential one-dimensional - minimization along each directional vector, which is updated at - each iteration of the main minimization loop. The function being minimized need not be - differentiable, and no derivatives are taken. - - Uses scipy.optimize.minimize Powell. - For further detail, please refer to - See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html - """ - - _OPTIONS = ["maxiter", "maxfev", "disp", "xtol"] - - # pylint: disable=unused-argument - def __init__( - self, - maxiter: int | None = None, - maxfev: int = 1000, - disp: bool = False, - xtol: float = 0.0001, - tol: float | None = None, - options: dict | None = None, - **kwargs, - ) -> None: - """ - Args: - maxiter: Maximum allowed number of iterations. If both maxiter and maxfev - are set, minimization will stop at the first reached. - maxfev: Maximum allowed number of function evaluations. If both maxiter and - maxfev are set, minimization will stop at the first reached. - disp: Set to True to print convergence messages. - xtol: Relative error in solution xopt acceptable for convergence. - tol: Tolerance for termination. - options: A dictionary of solver options. - kwargs: additional kwargs for scipy.optimize.minimize. - """ - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__("Powell", options=options, tol=tol, **kwargs) diff --git a/qiskit/algorithms/optimizers/qnspsa.py b/qiskit/algorithms/optimizers/qnspsa.py deleted file mode 100644 index be5907afbf17..000000000000 --- a/qiskit/algorithms/optimizers/qnspsa.py +++ /dev/null @@ -1,421 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The QN-SPSA optimizer.""" - -from __future__ import annotations - -from collections.abc import Iterator -from typing import Any, Callable - -import numpy as np -from qiskit.providers import Backend -from qiskit.circuit import ParameterVector, QuantumCircuit -from qiskit.opflow import StateFn, CircuitSampler, ExpectationBase -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_arg - -from qiskit.primitives import BaseSampler, Sampler -from qiskit.algorithms.state_fidelities import ComputeUncompute - -from .spsa import SPSA, CALLBACK, TERMINATIONCHECKER, _batch_evaluate - -# the function to compute the fidelity -FIDELITY = Callable[[np.ndarray, np.ndarray], float] - - -class QNSPSA(SPSA): - r"""The Quantum Natural SPSA (QN-SPSA) optimizer. - - The QN-SPSA optimizer [1] is a stochastic optimizer that belongs to the family of gradient - descent methods. This optimizer is based on SPSA but attempts to improve the convergence by - sampling the **natural gradient** instead of the vanilla, first-order gradient. It achieves - this by approximating Hessian of the ``fidelity`` of the ansatz circuit. - - Compared to natural gradients, which require :math:`\mathcal{O}(d^2)` expectation value - evaluations for a circuit with :math:`d` parameters, QN-SPSA only requires - :math:`\mathcal{O}(1)` and can therefore significantly speed up the natural gradient calculation - by sacrificing some accuracy. Compared to SPSA, QN-SPSA requires 4 additional function - evaluations of the fidelity. - - The stochastic approximation of the natural gradient can be systematically improved by - increasing the number of ``resamplings``. This leads to a Monte Carlo-style convergence to - the exact, analytic value. - - .. note:: - - This component has some function that is normally random. If you want to reproduce behavior - then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). - - Examples: - - This short example runs QN-SPSA for the ground state calculation of the ``Z ^ Z`` - observable where the ansatz is a ``PauliTwoDesign`` circuit. - - .. code-block:: python - - import numpy as np - from qiskit.algorithms.optimizers import QNSPSA - from qiskit.circuit.library import PauliTwoDesign - from qiskit.primitives import Estimator, Sampler - from qiskit.quantum_info import Pauli - - # problem setup - ansatz = PauliTwoDesign(2, reps=1, seed=2) - observable = Pauli("ZZ") - initial_point = np.random.random(ansatz.num_parameters) - - # loss function - estimator = Estimator() - - def loss(x): - result = estimator.run([ansatz], [observable], [x]).result() - return np.real(result.values[0]) - - # fidelity for estimation of the geometric tensor - sampler = Sampler() - fidelity = QNSPSA.get_fidelity(ansatz, sampler) - - # run QN-SPSA - qnspsa = QNSPSA(fidelity, maxiter=300) - result = qnspsa.optimize(ansatz.num_parameters, loss, initial_point=initial_point) - - This is a legacy version solving the same problem but using Qiskit Opflow instead - of the Qiskit Primitives. Note however, that this usage is deprecated. - - .. code-block:: python - - import numpy as np - from qiskit.algorithms.optimizers import QNSPSA - from qiskit.circuit.library import PauliTwoDesign - from qiskit.opflow import Z, StateFn - - ansatz = PauliTwoDesign(2, reps=1, seed=2) - observable = Z ^ Z - initial_point = np.random.random(ansatz.num_parameters) - - def loss(x): - bound = ansatz.assign_parameters(x) - return np.real((StateFn(observable, is_measurement=True) @ StateFn(bound)).eval()) - - fidelity = QNSPSA.get_fidelity(ansatz) - qnspsa = QNSPSA(fidelity, maxiter=300) - result = qnspsa.optimize(ansatz.num_parameters, loss, initial_point=initial_point) - - - References: - - [1] J. Gacon et al, "Simultaneous Perturbation Stochastic Approximation of the Quantum - Fisher Information", `arXiv:2103.09232 `_ - - """ - - def __init__( - self, - fidelity: FIDELITY, - maxiter: int = 100, - blocking: bool = True, - allowed_increase: float | None = None, - learning_rate: float | Callable[[], Iterator] | None = None, - perturbation: float | Callable[[], Iterator] | None = None, - resamplings: int | dict[int, int] = 1, - perturbation_dims: int | None = None, - regularization: float | None = None, - hessian_delay: int = 0, - lse_solver: Callable[[np.ndarray, np.ndarray], np.ndarray] | None = None, - initial_hessian: np.ndarray | None = None, - callback: CALLBACK | None = None, - termination_checker: TERMINATIONCHECKER | None = None, - ) -> None: - r""" - Args: - fidelity: A function to compute the fidelity of the ansatz state with itself for - two different sets of parameters. - maxiter: The maximum number of iterations. Note that this is not the maximal number - of function evaluations. - blocking: If True, only accepts updates that improve the loss (up to some allowed - increase, see next argument). - allowed_increase: If ``blocking`` is ``True``, this argument determines by how much - the loss can increase with the proposed parameters and still be accepted. - If ``None``, the allowed increases is calibrated automatically to be twice the - approximated standard deviation of the loss function. - learning_rate: The update step is the learning rate is multiplied with the gradient. - If the learning rate is a float, it remains constant over the course of the - optimization. It can also be a callable returning an iterator which yields the - learning rates for each optimization step. - If ``learning_rate`` is set ``perturbation`` must also be provided. - perturbation: Specifies the magnitude of the perturbation for the finite difference - approximation of the gradients. Can be either a float or a generator yielding - the perturbation magnitudes per step. - If ``perturbation`` is set ``learning_rate`` must also be provided. - resamplings: The number of times the gradient (and Hessian) is sampled using a random - direction to construct a gradient estimate. Per default the gradient is estimated - using only one random direction. If an integer, all iterations use the same number - of resamplings. If a dictionary, this is interpreted as - ``{iteration: number of resamplings per iteration}``. - perturbation_dims: The number of perturbed dimensions. Per default, all dimensions - are perturbed, but a smaller, fixed number can be perturbed. If set, the perturbed - dimensions are chosen uniformly at random. - regularization: To ensure the preconditioner is symmetric and positive definite, the - identity times a small coefficient is added to it. This generator yields that - coefficient. - hessian_delay: Start multiplying the gradient with the inverse Hessian only after a - certain number of iterations. The Hessian is still evaluated and therefore this - argument can be useful to first get a stable average over the last iterations before - using it as preconditioner. - lse_solver: The method to solve for the inverse of the Hessian. Per default an - exact LSE solver is used, but can e.g. be overwritten by a minimization routine. - initial_hessian: The initial guess for the Hessian. By default the identity matrix - is used. - callback: A callback function passed information in each iteration step. The - information is, in this order: the parameters, the function value, the number - of function evaluations, the stepsize, whether the step was accepted. - termination_checker: A callback function executed at the end of each iteration step. The - arguments are, in this order: the parameters, the function value, the number - of function evaluations, the stepsize, whether the step was accepted. If the callback - returns True, the optimization is terminated. - To prevent additional evaluations of the objective method, if the objective has not yet - been evaluated, the objective is estimated by taking the mean of the objective - evaluations used in the estimate of the gradient. - - - """ - super().__init__( - maxiter, - blocking, - allowed_increase, - # trust region *must* be false for natural gradients to work - trust_region=False, - learning_rate=learning_rate, - perturbation=perturbation, - resamplings=resamplings, - callback=callback, - second_order=True, - hessian_delay=hessian_delay, - lse_solver=lse_solver, - regularization=regularization, - perturbation_dims=perturbation_dims, - initial_hessian=initial_hessian, - termination_checker=termination_checker, - ) - - self.fidelity = fidelity - - def _point_sample(self, loss, x, eps, delta1, delta2): - loss_points = [x + eps * delta1, x - eps * delta1] - fidelity_points = [ - (x, x + eps * delta1), - (x, x - eps * delta1), - (x, x + eps * (delta1 + delta2)), - (x, x + eps * (-delta1 + delta2)), - ] - self._nfev += 6 - - loss_values = _batch_evaluate(loss, loss_points, self._max_evals_grouped) - fidelity_values = _batch_evaluate( - self.fidelity, fidelity_points, self._max_evals_grouped, unpack_points=True - ) - - # compute the gradient approximation and additionally return the loss function evaluations - gradient_estimate = (loss_values[0] - loss_values[1]) / (2 * eps) * delta1 - - # compute the preconditioner point estimate - fidelity_values = np.asarray(fidelity_values, dtype=float) - diff = fidelity_values[2] - fidelity_values[0] - diff = diff - (fidelity_values[3] - fidelity_values[1]) - diff = diff / (2 * eps**2) - - rank_one = np.outer(delta1, delta2) - # -0.5 factor comes from the fact that we need -0.5 * fidelity - hessian_estimate = -0.5 * diff * (rank_one + rank_one.T) / 2 - - return np.mean(loss_values), gradient_estimate, hessian_estimate - - @property - def settings(self) -> dict[str, Any]: - """The optimizer settings in a dictionary format.""" - # re-use serialization from SPSA - settings = super().settings - settings.update({"fidelity": self.fidelity}) - - # remove SPSA-specific arguments not in QNSPSA - settings.pop("trust_region") - settings.pop("second_order") - - return settings - - @staticmethod - @deprecate_arg( - "backend", - since="0.24.0", - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - # We allow passing a sampler as the second argument because that will become a positional - # argument for `sampler` after removing `backend` and `expectation`. - predicate=lambda backend: not isinstance(backend, BaseSampler), - ) - @deprecate_arg( - "expectation", - since="0.24.0", - additional_msg="See https://qisk.it/algo_migration for a migration guide.", - ) - def get_fidelity( - circuit: QuantumCircuit, - backend: Backend | QuantumInstance | None = None, - expectation: ExpectationBase | None = None, - *, - sampler: BaseSampler | None = None, - ) -> Callable[[np.ndarray, np.ndarray], float]: - r"""Get a function to compute the fidelity of ``circuit`` with itself. - - .. note:: - - Using this function with a backend and expectation converter is pending deprecation, - instead pass a Qiskit Primitive sampler, such as :class:`~.Sampler`. - The sampler can be passed as keyword argument or, positionally, as second argument. - - Let ``circuit`` be a parameterized quantum circuit performing the operation - :math:`U(\theta)` given a set of parameters :math:`\theta`. Then this method returns - a function to evaluate - - .. math:: - - F(\theta, \phi) = \big|\langle 0 | U^\dagger(\theta) U(\phi) |0\rangle \big|^2. - - The output of this function can be used as input for the ``fidelity`` to the - :class:`~.QNSPSA` optimizer. - - Args: - circuit: The circuit preparing the parameterized ansatz. - backend: Deprecated. A backend of quantum instance to evaluate the circuits. - If None, plain matrix multiplication will be used. - expectation: Deprecated. An expectation converter to specify how the expected - value is computed. If a shot-based readout is used this should be set to - ``PauliExpectation``. - sampler: A sampler primitive to sample from a quantum state. - - Returns: - A handle to the function :math:`F`. - - """ - # allow passing sampler by position - if isinstance(backend, BaseSampler): - sampler = backend - backend = None - - if expectation is None and backend is None and sampler is None: - sampler = Sampler() - - if expectation is not None or backend is not None: - return QNSPSA._legacy_get_fidelity(circuit, backend, expectation) - - fid = ComputeUncompute(sampler) - - num_parameters = circuit.num_parameters - - def fidelity(values_x, values_y): - values_x = np.reshape(values_x, (-1, num_parameters)).tolist() - batch_size_x = len(values_x) - - values_y = np.reshape(values_y, (-1, num_parameters)).tolist() - batch_size_y = len(values_y) - - result = fid.run( - batch_size_x * [circuit], batch_size_y * [circuit], values_x, values_y - ).result() - return np.asarray(result.fidelities) - - return fidelity - - @staticmethod - def _legacy_get_fidelity( - circuit: QuantumCircuit, - backend: Backend | QuantumInstance | None = None, - expectation: ExpectationBase | None = None, - ) -> Callable[[np.ndarray, np.ndarray], float]: - r"""Deprecated. Get a function to compute the fidelity of ``circuit`` with itself. - - .. note:: - - This method is deprecated. Instead use the :class:`~.ComputeUncompute` - class which implements the fidelity calculation in the same fashion as this method. - - Let ``circuit`` be a parameterized quantum circuit performing the operation - :math:`U(\theta)` given a set of parameters :math:`\theta`. Then this method returns - a function to evaluate - - .. math:: - - F(\theta, \phi) = \big|\langle 0 | U^\dagger(\theta) U(\phi) |0\rangle \big|^2. - - The output of this function can be used as input for the ``fidelity`` to the - :class:~`qiskit.algorithms.optimizers.QNSPSA` optimizer. - - Args: - circuit: The circuit preparing the parameterized ansatz. - backend: A backend of quantum instance to evaluate the circuits. If None, plain - matrix multiplication will be used. - expectation: An expectation converter to specify how the expected value is computed. - If a shot-based readout is used this should be set to ``PauliExpectation``. - - Returns: - A handle to the function :math:`F`. - - """ - params_x = ParameterVector("x", circuit.num_parameters) - params_y = ParameterVector("y", circuit.num_parameters) - - expression = ~StateFn(circuit.assign_parameters(params_x)) @ StateFn( - circuit.assign_parameters(params_y) - ) - - if expectation is not None: - expression = expectation.convert(expression) - - if backend is None: - - def fidelity(values_x, values_y): - value_dict = dict( - zip(params_x[:] + params_y[:], values_x.tolist() + values_y.tolist()) - ) - return np.abs(expression.assign_parameters(value_dict).eval()) ** 2 - - else: - sampler = CircuitSampler(backend) - - def fidelity(values_x, values_y=None): - # no batches - if isinstance(values_x, np.ndarray) and isinstance(values_y, np.ndarray): - value_dict = dict( - zip(params_x[:] + params_y[:], values_x.tolist() + values_y.tolist()) - ) - # legacy batching -- remove once QNSPSA.get_fidelity is only supported with sampler - elif values_y is None: - value_dict = {p: [] for p in params_x[:] + params_y[:]} - for values_xy in values_x: - for value_x, param_x in zip(values_xy[0, :], params_x): - value_dict[param_x].append(value_x) - - for value_y, param_y in zip(values_xy[1, :], params_y): - value_dict[param_y].append(value_y) - else: - value_dict = {p: [] for p in params_x[:] + params_y[:]} - for values_i_x, values_i_y in zip(values_x, values_y): - for value_x, param_x in zip(values_i_x, params_x): - value_dict[param_x].append(value_x) - - for value_y, param_y in zip(values_i_y, params_y): - value_dict[param_y].append(value_y) - - return np.abs(sampler.convert(expression, params=value_dict).eval()) ** 2 - - return fidelity diff --git a/qiskit/algorithms/optimizers/scipy_optimizer.py b/qiskit/algorithms/optimizers/scipy_optimizer.py deleted file mode 100644 index 3a22b41bcfbd..000000000000 --- a/qiskit/algorithms/optimizers/scipy_optimizer.py +++ /dev/null @@ -1,183 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Wrapper class of scipy.optimize.minimize.""" -from __future__ import annotations - -import warnings -from collections.abc import Callable -from typing import Any - -import numpy as np -from scipy.optimize import minimize - -from qiskit.utils.validation import validate_min - -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT - - -class SciPyOptimizer(Optimizer): - """A general Qiskit Optimizer wrapping scipy.optimize.minimize. - - For further detail, please refer to - https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html - """ - - _bounds_support_methods = {"l-bfgs-b", "tnc", "slsqp", "powell", "trust-constr"} - _gradient_support_methods = { - "cg", - "bfgs", - "newton-cg", - "l-bfgs-b", - "tnc", - "slsqp", - "dogleg", - "trust-ncg", - "trust-krylov", - "trust-exact", - "trust-constr", - } - - def __init__( - self, - method: str | Callable, - options: dict[str, Any] | None = None, - max_evals_grouped: int = 1, - **kwargs, - ): - """ - Args: - method: Type of solver. - options: A dictionary of solver options. - kwargs: additional kwargs for scipy.optimize.minimize. - max_evals_grouped: Max number of default gradient evaluations performed simultaneously. - """ - self._method = method.lower() if isinstance(method, str) else method - # Set support level - if self._method in self._bounds_support_methods: - self._bounds_support_level = OptimizerSupportLevel.supported - else: - self._bounds_support_level = OptimizerSupportLevel.ignored - if self._method in self._gradient_support_methods: - self._gradient_support_level = OptimizerSupportLevel.supported - else: - self._gradient_support_level = OptimizerSupportLevel.ignored - self._initial_point_support_level = OptimizerSupportLevel.required - - self._options = options if options is not None else {} - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - validate_min("max_evals_grouped", max_evals_grouped, 1) - - self._max_evals_grouped = max_evals_grouped - self._kwargs = kwargs - - def get_support_level(self): - """Return support level dictionary""" - return { - "gradient": self._gradient_support_level, - "bounds": self._bounds_support_level, - "initial_point": self._initial_point_support_level, - } - - @property - def settings(self) -> dict[str, Any]: - options = self._options.copy() - if hasattr(self, "_OPTIONS"): - # all _OPTIONS should be keys in self._options, but add a failsafe here - attributes = [ - option - for option in self._OPTIONS # pylint: disable=no-member - if option in options.keys() - ] - - settings = {attr: options.pop(attr) for attr in attributes} - else: - settings = {} - - settings["max_evals_grouped"] = self._max_evals_grouped - settings["options"] = options - settings.update(self._kwargs) - - # the subclasses don't need the "method" key as the class type specifies the method - if self.__class__ == SciPyOptimizer: - settings["method"] = self._method - - return settings - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - # Remove ignored parameters to suppress the warning of scipy.optimize.minimize - if self.is_bounds_ignored: - bounds = None - if self.is_gradient_ignored: - jac = None - - if self.is_gradient_supported and jac is None and self._max_evals_grouped > 1: - if "eps" in self._options: - epsilon = self._options["eps"] - else: - epsilon = ( - 1e-8 if self._method in {"l-bfgs-b", "tnc"} else np.sqrt(np.finfo(float).eps) - ) - jac = Optimizer.wrap_function( - Optimizer.gradient_num_diff, (fun, epsilon, self._max_evals_grouped) - ) - - # Workaround for L_BFGS_B because it does not accept np.ndarray. - # See https://github.com/Qiskit/qiskit-terra/pull/6373. - if jac is not None and self._method == "l-bfgs-b": - jac = self._wrap_gradient(jac) - - # Starting in scipy 1.9.0 maxiter is deprecated and maxfun (added in 1.5.0) - # should be used instead - swapped_deprecated_args = False - if self._method == "tnc" and "maxiter" in self._options: - swapped_deprecated_args = True - self._options["maxfun"] = self._options.pop("maxiter") - - raw_result = minimize( - fun=fun, - x0=x0, - method=self._method, - jac=jac, - bounds=bounds, - options=self._options, - **self._kwargs, - ) - if swapped_deprecated_args: - self._options["maxiter"] = self._options.pop("maxfun") - - result = OptimizerResult() - result.x = raw_result.x - result.fun = raw_result.fun - result.nfev = raw_result.nfev - result.njev = raw_result.get("njev", None) - result.nit = raw_result.get("nit", None) - - return result - - @staticmethod - def _wrap_gradient(gradient_function): - def wrapped_gradient(x): - gradient = gradient_function(x) - if isinstance(gradient, np.ndarray): - return gradient.tolist() - return gradient - - return wrapped_gradient diff --git a/qiskit/algorithms/optimizers/slsqp.py b/qiskit/algorithms/optimizers/slsqp.py deleted file mode 100644 index d02eb790afa9..000000000000 --- a/qiskit/algorithms/optimizers/slsqp.py +++ /dev/null @@ -1,73 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Sequential Least SQuares Programming optimizer""" -from __future__ import annotations - - -from .scipy_optimizer import SciPyOptimizer - - -class SLSQP(SciPyOptimizer): - """ - Sequential Least SQuares Programming optimizer. - - SLSQP minimizes a function of several variables with any combination of bounds, equality - and inequality constraints. The method wraps the SLSQP Optimization subroutine originally - implemented by Dieter Kraft. - - SLSQP is ideal for mathematical problems for which the objective function and the constraints - are twice continuously differentiable. Note that the wrapper handles infinite values in bounds - by converting them into large floating values. - - Uses scipy.optimize.minimize SLSQP. - For further detail, please refer to - See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html - """ - - _OPTIONS = ["maxiter", "disp", "ftol", "eps"] - - # pylint: disable=unused-argument - def __init__( - self, - maxiter: int = 100, - disp: bool = False, - ftol: float = 1e-06, - tol: float | None = None, - eps: float = 1.4901161193847656e-08, - options: dict | None = None, - max_evals_grouped: int = 1, - **kwargs, - ) -> None: - """ - Args: - maxiter: Maximum number of iterations. - disp: Set to True to print convergence messages. - ftol: Precision goal for the value of f in the stopping criterion. - tol: Tolerance for termination. - eps: Step size used for numerical approximation of the Jacobian. - options: A dictionary of solver options. - max_evals_grouped: Max number of default gradient evaluations performed simultaneously. - kwargs: additional kwargs for scipy.optimize.minimize. - """ - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__( - "SLSQP", - options=options, - tol=tol, - max_evals_grouped=max_evals_grouped, - **kwargs, - ) diff --git a/qiskit/algorithms/optimizers/snobfit.py b/qiskit/algorithms/optimizers/snobfit.py deleted file mode 100644 index 8d6a3bde1d07..000000000000 --- a/qiskit/algorithms/optimizers/snobfit.py +++ /dev/null @@ -1,130 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Stable Noisy Optimization by Branch and FIT algorithm (SNOBFIT) optimizer.""" -from __future__ import annotations - -from collections.abc import Callable -from typing import Any - -import numpy as np -from qiskit.exceptions import QiskitError -from qiskit.utils import optionals as _optionals -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT - - -@_optionals.HAS_SKQUANT.require_in_instance -@_optionals.HAS_SQSNOBFIT.require_in_instance -class SNOBFIT(Optimizer): - """Stable Noisy Optimization by Branch and FIT algorithm. - - SnobFit is used for the optimization of derivative-free, noisy objective functions providing - robust and fast solutions of problems with continuous variables varying within bound. - - Uses skquant.opt installed with pip install scikit-quant. - For further detail, please refer to - https://github.com/scikit-quant/scikit-quant and https://qat4chem.lbl.gov/software. - """ - - def __init__( - self, - maxiter: int = 1000, - maxfail: int = 10, - maxmp: int = None, - verbose: bool = False, - ) -> None: - """ - Args: - maxiter: Maximum number of function evaluations. - maxmp: Maximum number of model points requested for the local fit. - Default = 2 * number of parameters + 6 set to this value when None. - maxfail: Maximum number of failures to improve the solution. Stops the algorithm - after maxfail is reached. - verbose: Provide verbose (debugging) output. - - Raises: - MissingOptionalLibraryError: scikit-quant or SQSnobFit not installed - QiskitError: If NumPy 1.24.0 or above is installed. - See https://github.com/scikit-quant/scikit-quant/issues/24 for more details. - """ - # check version - version = tuple(map(int, np.__version__.split("."))) - if version >= (1, 24, 0): - raise QiskitError( - "SnobFit is incompatible with NumPy 1.24.0 or above, please " - "install a previous version. See also scikit-quant/scikit-quant#24." - ) - - super().__init__() - self._maxiter = maxiter - self._maxfail = maxfail - self._maxmp = maxmp - self._verbose = verbose - - def get_support_level(self): - """Returns support level dictionary.""" - return { - "gradient": OptimizerSupportLevel.ignored, - "bounds": OptimizerSupportLevel.required, - "initial_point": OptimizerSupportLevel.required, - } - - @property - def settings(self) -> dict[str, Any]: - return { - "maxiter": self._maxiter, - "maxfail": self._maxfail, - "maxmp": self._maxmp, - "verbose": self._verbose, - } - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - import skquant.opt as skq - from SQSnobFit import optset - - if bounds is None or any(None in bound_tuple for bound_tuple in bounds): - raise ValueError("Optimizer SNOBFIT requires bounds for all parameters.") - - snobfit_settings = { - "maxmp": self._maxmp, - "maxfail": self._maxfail, - "verbose": self._verbose, - } - options = optset(optin=snobfit_settings) - # counters the error when initial point is outside the acceptable bounds - x0 = np.asarray(x0) - for idx, theta in enumerate(x0): - if abs(theta) > bounds[idx][0]: - x0[idx] = x0[idx] % bounds[idx][0] - elif abs(theta) > bounds[idx][1]: - x0[idx] = x0[idx] % bounds[idx][1] - - res, history = skq.minimize( - fun, - x0, - bounds=bounds, - budget=self._maxiter, - method="snobfit", - options=options, - ) - - optimizer_result = OptimizerResult() - optimizer_result.x = res.optpar - optimizer_result.fun = res.optval - optimizer_result.nfev = len(history) - return optimizer_result diff --git a/qiskit/algorithms/optimizers/spsa.py b/qiskit/algorithms/optimizers/spsa.py deleted file mode 100644 index f69de5baf299..000000000000 --- a/qiskit/algorithms/optimizers/spsa.py +++ /dev/null @@ -1,810 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Simultaneous Perturbation Stochastic Approximation (SPSA) optimizer. - -This implementation allows both, standard first-order as well as second-order SPSA. -""" -from __future__ import annotations - -from collections import deque -from collections.abc import Iterator -from typing import Callable, Any, SupportsFloat -import logging -import warnings -from time import time - -import scipy -import numpy as np - -from qiskit.utils import algorithm_globals -from qiskit.utils.deprecation import deprecate_func - -from .optimizer import Optimizer, OptimizerSupportLevel, OptimizerResult, POINT - -# number of function evaluations, parameters, loss, stepsize, accepted -CALLBACK = Callable[[int, np.ndarray, float, SupportsFloat, bool], None] -TERMINATIONCHECKER = Callable[[int, np.ndarray, float, SupportsFloat, bool], bool] - -logger = logging.getLogger(__name__) - - -class SPSA(Optimizer): - """Simultaneous Perturbation Stochastic Approximation (SPSA) optimizer. - - SPSA [1] is an gradient descent method for optimizing systems with multiple unknown parameters. - As an optimization method, it is appropriately suited to large-scale population models, - adaptive modeling, and simulation optimization. - - .. seealso:: - - Many examples are presented at the `SPSA Web site `__. - - The main feature of SPSA is the stochastic gradient approximation, which requires only two - measurements of the objective function, regardless of the dimension of the optimization - problem. - - Additionally to standard, first-order SPSA, where only gradient information is used, this - implementation also allows second-order SPSA (2-SPSA) [2]. In 2-SPSA we also estimate the - Hessian of the loss with a stochastic approximation and multiply the gradient with the - inverse Hessian to take local curvature into account and improve convergence. - Notably this Hessian estimate requires only a constant number of function evaluations - unlike an exact evaluation of the Hessian, which scales quadratically in the number of - function evaluations. - - .. note:: - - SPSA can be used in the presence of noise, and it is therefore indicated in situations - involving measurement uncertainty on a quantum computation when finding a minimum. - If you are executing a variational algorithm using an OpenQASM - simulator or a real device, SPSA would be the most recommended choice among the optimizers - provided here. - - The optimization process can includes a calibration phase if neither the ``learning_rate`` nor - ``perturbation`` is provided, which requires additional functional evaluations. - (Note that either both or none must be set.) For further details on the automatic calibration, - please refer to the supplementary information section IV. of [3]. - - .. note:: - - This component has some function that is normally random. If you want to reproduce behavior - then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). - - - Examples: - - This short example runs SPSA for the ground state calculation of the ``Z ^ Z`` - observable where the ansatz is a ``PauliTwoDesign`` circuit. - - .. code-block:: python - - import numpy as np - from qiskit.algorithms.optimizers import SPSA - from qiskit.circuit.library import PauliTwoDesign - from qiskit.opflow import Z, StateFn - - ansatz = PauliTwoDesign(2, reps=1, seed=2) - observable = Z ^ Z - initial_point = np.random.random(ansatz.num_parameters) - - def loss(x): - bound = ansatz.assign_parameters(x) - return np.real((StateFn(observable, is_measurement=True) @ StateFn(bound)).eval()) - - spsa = SPSA(maxiter=300) - result = spsa.optimize(ansatz.num_parameters, loss, initial_point=initial_point) - - To use the Hessian information, i.e. 2-SPSA, you can add `second_order=True` to the - initializer of the `SPSA` class, the rest of the code remains the same. - - .. code-block:: python - - two_spsa = SPSA(maxiter=300, second_order=True) - result = two_spsa.optimize(ansatz.num_parameters, loss, initial_point=initial_point) - - The `termination_checker` can be used to implement a custom termination criterion. - - .. code-block:: python - - import numpy as np - from qiskit.algorithms.optimizers import SPSA - - def objective(x): - return np.linalg.norm(x) + .04*np.random.rand(1) - - class TerminationChecker: - - def __init__(self, N : int): - self.N = N - self.values = [] - - def __call__(self, nfev, parameters, value, stepsize, accepted) -> bool: - self.values.append(value) - - if len(self.values) > self.N: - last_values = self.values[-self.N:] - pp = np.polyfit(range(self.N), last_values, 1) - slope = pp[0] / self.N - - if slope > 0: - return True - return False - - spsa = SPSA(maxiter=200, termination_checker=TerminationChecker(10)) - parameters, value, niter = spsa.optimize(2, objective, initial_point=[0.5, 0.5]) - print(f'SPSA completed after {niter} iterations') - - - References: - - [1]: J. C. Spall (1998). An Overview of the Simultaneous Perturbation Method for Efficient - Optimization, Johns Hopkins APL Technical Digest, 19(4), 482–492. - `Online at jhuapl.edu. `_ - - [2]: J. C. Spall (1997). Accelerated second-order stochastic optimization using only - function measurements, Proceedings of the 36th IEEE Conference on Decision and Control, - 1417-1424 vol.2. `Online at IEEE.org. `_ - - [3]: A. Kandala et al. (2017). Hardware-efficient Variational Quantum Eigensolver for - Small Molecules and Quantum Magnets. Nature 549, pages242–246(2017). - `arXiv:1704.05018v2 `_ - - """ - - def __init__( - self, - maxiter: int = 100, - blocking: bool = False, - allowed_increase: float | None = None, - trust_region: bool = False, - learning_rate: float | np.ndarray | Callable[[], Iterator] | None = None, - perturbation: float | np.ndarray | Callable[[], Iterator] | None = None, - last_avg: int = 1, - resamplings: int | dict[int, int] = 1, - perturbation_dims: int | None = None, - second_order: bool = False, - regularization: float | None = None, - hessian_delay: int = 0, - lse_solver: Callable[[np.ndarray, np.ndarray], np.ndarray] | None = None, - initial_hessian: np.ndarray | None = None, - callback: CALLBACK | None = None, - termination_checker: TERMINATIONCHECKER | None = None, - ) -> None: - r""" - Args: - maxiter: The maximum number of iterations. Note that this is not the maximal number - of function evaluations. - blocking: If True, only accepts updates that improve the loss (up to some allowed - increase, see next argument). - allowed_increase: If ``blocking`` is ``True``, this argument determines by how much - the loss can increase with the proposed parameters and still be accepted. - If ``None``, the allowed increases is calibrated automatically to be twice the - approximated standard deviation of the loss function. - trust_region: If ``True``, restricts the norm of the update step to be :math:`\leq 1`. - learning_rate: The update step is the learning rate is multiplied with the gradient. - If the learning rate is a float, it remains constant over the course of the - optimization. If a NumPy array, the :math:`i`-th element is the learning rate for - the :math:`i`-th iteration. It can also be a callable returning an iterator which - yields the learning rates for each optimization step. - If ``learning_rate`` is set ``perturbation`` must also be provided. - perturbation: Specifies the magnitude of the perturbation for the finite difference - approximation of the gradients. See ``learning_rate`` for the supported types. - If ``perturbation`` is set ``learning_rate`` must also be provided. - last_avg: Return the average of the ``last_avg`` parameters instead of just the - last parameter values. - resamplings: The number of times the gradient (and Hessian) is sampled using a random - direction to construct a gradient estimate. Per default the gradient is estimated - using only one random direction. If an integer, all iterations use the same number - of resamplings. If a dictionary, this is interpreted as - ``{iteration: number of resamplings per iteration}``. - perturbation_dims: The number of perturbed dimensions. Per default, all dimensions - are perturbed, but a smaller, fixed number can be perturbed. If set, the perturbed - dimensions are chosen uniformly at random. - second_order: If True, use 2-SPSA instead of SPSA. In 2-SPSA, the Hessian is estimated - additionally to the gradient, and the gradient is preconditioned with the inverse - of the Hessian to improve convergence. - regularization: To ensure the preconditioner is symmetric and positive definite, the - identity times a small coefficient is added to it. This generator yields that - coefficient. - hessian_delay: Start multiplying the gradient with the inverse Hessian only after a - certain number of iterations. The Hessian is still evaluated and therefore this - argument can be useful to first get a stable average over the last iterations before - using it as preconditioner. - lse_solver: The method to solve for the inverse of the Hessian. Per default an - exact LSE solver is used, but can e.g. be overwritten by a minimization routine. - initial_hessian: The initial guess for the Hessian. By default the identity matrix - is used. - callback: A callback function passed information in each iteration step. The - information is, in this order: the number of function evaluations, the parameters, - the function value, the stepsize, whether the step was accepted. - termination_checker: A callback function executed at the end of each iteration step. The - arguments are, in this order: the parameters, the function value, the number - of function evaluations, the stepsize, whether the step was accepted. If the callback - returns True, the optimization is terminated. - To prevent additional evaluations of the objective method, if the objective has not yet - been evaluated, the objective is estimated by taking the mean of the objective - evaluations used in the estimate of the gradient. - - - Raises: - ValueError: If ``learning_rate`` or ``perturbation`` is an array with less elements - than the number of iterations. - - - """ - super().__init__() - - # general optimizer arguments - self.maxiter = maxiter - self.trust_region = trust_region - self.callback = callback - self.termination_checker = termination_checker - - # if learning rate and perturbation are arrays, check they are sufficiently long - for attr, name in zip([learning_rate, perturbation], ["learning_rate", "perturbation"]): - if isinstance(attr, (list, np.ndarray)): - if len(attr) < maxiter: - raise ValueError(f"Length of {name} is smaller than maxiter ({maxiter}).") - - self.learning_rate = learning_rate - self.perturbation = perturbation - - # SPSA specific arguments - self.blocking = blocking - self.allowed_increase = allowed_increase - self.last_avg = last_avg - self.resamplings = resamplings - self.perturbation_dims = perturbation_dims - - # 2-SPSA specific arguments - if regularization is None: - regularization = 0.01 - - self.second_order = second_order - self.hessian_delay = hessian_delay - self.lse_solver = lse_solver - self.regularization = regularization - self.initial_hessian = initial_hessian - - # runtime arguments - self._nfev: int | None = None # the number of function evaluations - self._smoothed_hessian: np.ndarray | None = None # smoothed average of the Hessians - - @staticmethod - def calibrate( - loss: Callable[[np.ndarray], float], - initial_point: np.ndarray, - c: float = 0.2, - stability_constant: float = 0, - target_magnitude: float | None = None, # 2 pi / 10 - alpha: float = 0.602, - gamma: float = 0.101, - modelspace: bool = False, - max_evals_grouped: int = 1, - ) -> tuple[Callable, Callable]: - r"""Calibrate SPSA parameters with a powerseries as learning rate and perturbation coeffs. - - The powerseries are: - - .. math:: - - a_k = \frac{a}{(A + k + 1)^\alpha}, c_k = \frac{c}{(k + 1)^\gamma} - - Args: - loss: The loss function. - initial_point: The initial guess of the iteration. - c: The initial perturbation magnitude. - stability_constant: The value of `A`. - target_magnitude: The target magnitude for the first update step, defaults to - :math:`2\pi / 10`. - alpha: The exponent of the learning rate powerseries. - gamma: The exponent of the perturbation powerseries. - modelspace: Whether the target magnitude is the difference of parameter values - or function values (= model space). - max_evals_grouped: The number of grouped evaluations supported by the loss function. - Defaults to 1, i.e. no grouping. - - Returns: - tuple(generator, generator): A tuple of powerseries generators, the first one for the - learning rate and the second one for the perturbation. - """ - logger.info("SPSA: Starting calibration of learning rate and perturbation.") - if target_magnitude is None: - target_magnitude = 2 * np.pi / 10 - - dim = len(initial_point) - - # compute the average magnitude of the first step - steps = 25 - points = [] - for _ in range(steps): - # compute the random direction - pert = bernoulli_perturbation(dim) - points += [initial_point + c * pert, initial_point - c * pert] - - losses = _batch_evaluate(loss, points, max_evals_grouped) - - avg_magnitudes = 0.0 - for i in range(steps): - delta = losses[2 * i] - losses[2 * i + 1] - avg_magnitudes += np.abs(delta / (2 * c)) - - avg_magnitudes /= steps - - if modelspace: - a = target_magnitude / (avg_magnitudes**2) - else: - a = target_magnitude / avg_magnitudes - - # compute the rescaling factor for correct first learning rate - if a < 1e-10: - warnings.warn(f"Calibration failed, using {target_magnitude} for `a`") - a = target_magnitude - - logger.info("Finished calibration:") - logger.info( - " -- Learning rate: a / ((A + n) ^ alpha) with a = %s, A = %s, alpha = %s", - a, - stability_constant, - alpha, - ) - logger.info(" -- Perturbation: c / (n ^ gamma) with c = %s, gamma = %s", c, gamma) - - # set up the powerseries - def learning_rate(): - return powerseries(a, alpha, stability_constant) - - def perturbation(): - return powerseries(c, gamma) - - return learning_rate, perturbation - - @staticmethod - def estimate_stddev( - loss: Callable[[np.ndarray], float], - initial_point: np.ndarray, - avg: int = 25, - max_evals_grouped: int = 1, - ) -> float: - """Estimate the standard deviation of the loss function.""" - losses = _batch_evaluate(loss, avg * [initial_point], max_evals_grouped) - return np.std(losses) - - @property - def settings(self) -> dict[str, Any]: - # if learning rate or perturbation are custom iterators expand them - if callable(self.learning_rate): - iterator = self.learning_rate() - learning_rate = np.array([next(iterator) for _ in range(self.maxiter)]) - else: - learning_rate = self.learning_rate - - if callable(self.perturbation): - iterator = self.perturbation() - perturbation = np.array([next(iterator) for _ in range(self.maxiter)]) - else: - perturbation = self.perturbation - - return { - "maxiter": self.maxiter, - "learning_rate": learning_rate, - "perturbation": perturbation, - "trust_region": self.trust_region, - "blocking": self.blocking, - "allowed_increase": self.allowed_increase, - "resamplings": self.resamplings, - "perturbation_dims": self.perturbation_dims, - "second_order": self.second_order, - "hessian_delay": self.hessian_delay, - "regularization": self.regularization, - "lse_solver": self.lse_solver, - "initial_hessian": self.initial_hessian, - "callback": self.callback, - "termination_checker": self.termination_checker, - } - - def _point_sample(self, loss, x, eps, delta1, delta2): - """A single sample of the gradient at position ``x`` in direction ``delta``.""" - # points to evaluate - points = [x + eps * delta1, x - eps * delta1] - self._nfev += 2 - - if self.second_order: - points += [x + eps * (delta1 + delta2), x + eps * (-delta1 + delta2)] - self._nfev += 2 - - # batch evaluate the points (if possible) - values = _batch_evaluate(loss, points, self._max_evals_grouped) - - plus = values[0] - minus = values[1] - gradient_sample = (plus - minus) / (2 * eps) * delta1 - - hessian_sample = None - if self.second_order: - diff = (values[2] - plus) - (values[3] - minus) - diff /= 2 * eps**2 - - rank_one = np.outer(delta1, delta2) - hessian_sample = diff * (rank_one + rank_one.T) / 2 - - return np.mean(values), gradient_sample, hessian_sample - - def _point_estimate(self, loss, x, eps, num_samples): - """The gradient estimate at point x.""" - # set up variables to store averages - value_estimate = 0 - gradient_estimate = np.zeros(x.size) - hessian_estimate = np.zeros((x.size, x.size)) - - # iterate over the directions - deltas1 = [ - bernoulli_perturbation(x.size, self.perturbation_dims) for _ in range(num_samples) - ] - - if self.second_order: - deltas2 = [ - bernoulli_perturbation(x.size, self.perturbation_dims) for _ in range(num_samples) - ] - else: - deltas2 = None - - for i in range(num_samples): - delta1 = deltas1[i] - delta2 = deltas2[i] if self.second_order else None - - value_sample, gradient_sample, hessian_sample = self._point_sample( - loss, x, eps, delta1, delta2 - ) - value_estimate += value_sample - gradient_estimate += gradient_sample - - if self.second_order: - hessian_estimate += hessian_sample - - return ( - value_estimate / num_samples, - gradient_estimate / num_samples, - hessian_estimate / num_samples, - ) - - def _compute_update(self, loss, x, k, eps, lse_solver): - # compute the perturbations - if isinstance(self.resamplings, dict): - num_samples = self.resamplings.get(k, 1) - else: - num_samples = self.resamplings - - # accumulate the number of samples - value, gradient, hessian = self._point_estimate(loss, x, eps, num_samples) - - # precondition gradient with inverse Hessian, if specified - if self.second_order: - smoothed = k / (k + 1) * self._smoothed_hessian + 1 / (k + 1) * hessian - self._smoothed_hessian = smoothed - - if k > self.hessian_delay: - spd_hessian = _make_spd(smoothed, self.regularization) - - # solve for the gradient update - gradient = np.real(lse_solver(spd_hessian, gradient)) - - return value, gradient - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - # ensure learning rate and perturbation are correctly set: either none or both - # this happens only here because for the calibration the loss function is required - if self.learning_rate is None and self.perturbation is None: - get_eta, get_eps = self.calibrate(fun, x0, max_evals_grouped=self._max_evals_grouped) - else: - get_eta, get_eps = _validate_pert_and_learningrate( - self.perturbation, self.learning_rate - ) - eta, eps = get_eta(), get_eps() - - if self.lse_solver is None: - lse_solver = np.linalg.solve - else: - lse_solver = self.lse_solver - - # prepare some initials - x = np.asarray(x0) - if self.initial_hessian is None: - self._smoothed_hessian = np.identity(x.size) - else: - self._smoothed_hessian = self.initial_hessian - - self._nfev = 0 - - # if blocking is enabled we need to keep track of the function values - if self.blocking: - fx = fun(x) - - self._nfev += 1 - if self.allowed_increase is None: - self.allowed_increase = 2 * self.estimate_stddev( - fun, x, max_evals_grouped=self._max_evals_grouped - ) - - logger.info("SPSA: Starting optimization.") - start = time() - - # keep track of the last few steps to return their average - last_steps = deque([x]) - - # use a local variable and while loop to keep track of the number of iterations - # if the termination checker terminates early - k = 0 - while k < self.maxiter: - k += 1 - iteration_start = time() - # compute update - fx_estimate, update = self._compute_update(fun, x, k, next(eps), lse_solver) - - # trust region - if self.trust_region: - norm = np.linalg.norm(update) - if norm > 1: # stop from dividing by 0 - update = update / norm - - # compute next parameter value - update = update * next(eta) - x_next = x - update - fx_next = None - - # blocking - if self.blocking: - self._nfev += 1 - fx_next = fun(x_next) - - if fx + self.allowed_increase <= fx_next: # accept only if loss improved - if self.callback is not None: - self.callback( - self._nfev, # number of function evals - x_next, # next parameters - fx_next, # loss at next parameters - np.linalg.norm(update), # size of the update step - False, - ) # not accepted - - logger.info( - "Iteration %s/%s rejected in %s.", - k, - self.maxiter + 1, - time() - iteration_start, - ) - continue - fx = fx_next - - logger.info( - "Iteration %s/%s done in %s.", k, self.maxiter + 1, time() - iteration_start - ) - - if self.callback is not None: - # if we didn't evaluate the function yet, do it now - if not self.blocking: - self._nfev += 1 - fx_next = fun(x_next) - - self.callback( - self._nfev, # number of function evals - x_next, # next parameters - fx_next, # loss at next parameters - np.linalg.norm(update), # size of the update step - True, - ) # accepted - - # update parameters - x = x_next - - # update the list of the last ``last_avg`` parameters - if self.last_avg > 1: - last_steps.append(x_next) - if len(last_steps) > self.last_avg: - last_steps.popleft() - - if self.termination_checker is not None: - fx_check = fx_estimate if fx_next is None else fx_next - if self.termination_checker( - self._nfev, x_next, fx_check, np.linalg.norm(update), True - ): - logger.info("terminated optimization at {k}/{self.maxiter} iterations") - break - - logger.info("SPSA: Finished in %s", time() - start) - - if self.last_avg > 1: - x = np.mean(last_steps, axis=0) - - result = OptimizerResult() - result.x = x - result.fun = fun(x) - result.nfev = self._nfev - result.nit = k - - return result - - def get_support_level(self): - """Get the support level dictionary.""" - return { - "gradient": OptimizerSupportLevel.ignored, - "bounds": OptimizerSupportLevel.ignored, - "initial_point": OptimizerSupportLevel.required, - } - - # pylint: disable=bad-docstring-quotes - @deprecate_func( - additional_msg=( - "Instead, use ``SPSA.minimize`` as a replacement, which supports the same arguments " - "but follows the interface of scipy.optimize and returns a complete result object " - "containing additional information." - ), - since="0.21.0", - ) - def optimize( - self, - num_vars, # pylint: disable=unused-argument - objective_function, - gradient_function=None, # pylint: disable=unused-argument - variable_bounds=None, # pylint: disable=unused-argument - initial_point=None, - ): - """Perform optimization. - - Args: - num_vars (int): Number of parameters to be optimized. - objective_function (callable): A function that computes the objective function. - gradient_function (callable): Not supported for SPSA. - variable_bounds (list[(float, float)]): Not supported for SPSA. - initial_point (numpy.ndarray[float]): Initial point. - - Returns: - tuple: point, value, nfev - point: is a 1D numpy.ndarray[float] containing the solution - value: is a float with the objective function value - nfev: number of objective function calls made if available or None - """ - result = self.minimize(objective_function, initial_point) - return result.x, result.fun, result.nfev - - -def bernoulli_perturbation(dim, perturbation_dims=None): - """Get a Bernoulli random perturbation.""" - if perturbation_dims is None: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - return 1 - 2 * algorithm_globals.random.binomial(1, 0.5, size=dim) - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - pert = 1 - 2 * algorithm_globals.random.binomial(1, 0.5, size=perturbation_dims) - indices = algorithm_globals.random.choice( - list(range(dim)), size=perturbation_dims, replace=False - ) - result = np.zeros(dim) - result[indices] = pert - - return result - - -def powerseries(eta=0.01, power=2, offset=0): - """Yield a series decreasing by a powerlaw.""" - - n = 1 - while True: - yield eta / ((n + offset) ** power) - n += 1 - - -def constant(eta=0.01): - """Yield a constant series.""" - - while True: - yield eta - - -def _batch_evaluate(function, points, max_evals_grouped, unpack_points=False): - """Evaluate a function on all points with batches of max_evals_grouped. - - The points are a list of inputs, as ``[in1, in2, in3, ...]``. If the individual - inputs are tuples (because the function takes multiple inputs), set ``unpack_points`` to ``True``. - """ - - # if the function cannot handle lists of points as input, cover this case immediately - if max_evals_grouped is None or max_evals_grouped == 1: - # support functions with multiple arguments where the points are given in a tuple - return [ - function(*point) if isinstance(point, tuple) else function(point) for point in points - ] - - num_points = len(points) - - # get the number of batches - num_batches = num_points // max_evals_grouped - if num_points % max_evals_grouped != 0: - num_batches += 1 - - # split the points - batched_points = np.array_split(np.asarray(points), num_batches) - - results = [] - for batch in batched_points: - if unpack_points: - batch = _repack_points(batch) - results += _as_list(function(*batch)) - else: - results += _as_list(function(batch)) - - return results - - -def _as_list(obj): - """Convert a list or numpy array into a list.""" - return obj.tolist() if isinstance(obj, np.ndarray) else obj - - -def _repack_points(points): - """Turn a list of tuples of points into a tuple of lists of points. - E.g. turns - [(a1, a2, a3), (b1, b2, b3)] - into - ([a1, b1], [a2, b2], [a3, b3]) - where all elements are np.ndarray. - """ - num_sets = len(points[0]) # length of (a1, a2, a3) - return ([x[i] for x in points] for i in range(num_sets)) - - -def _make_spd(matrix, bias=0.01): - identity = np.identity(matrix.shape[0]) - psd = scipy.linalg.sqrtm(matrix.dot(matrix)) - return psd + bias * identity - - -def _validate_pert_and_learningrate(perturbation, learning_rate): - if learning_rate is None or perturbation is None: - raise ValueError("If one of learning rate or perturbation is set, both must be set.") - - if isinstance(perturbation, float): - - def get_eps(): - return constant(perturbation) - - elif isinstance(perturbation, (list, np.ndarray)): - - def get_eps(): - return iter(perturbation) - - else: - get_eps = perturbation - - if isinstance(learning_rate, float): - - def get_eta(): - return constant(learning_rate) - - elif isinstance(learning_rate, (list, np.ndarray)): - - def get_eta(): - return iter(learning_rate) - - else: - get_eta = learning_rate - - return get_eta, get_eps diff --git a/qiskit/algorithms/optimizers/steppable_optimizer.py b/qiskit/algorithms/optimizers/steppable_optimizer.py deleted file mode 100644 index ed9c75d86b04..000000000000 --- a/qiskit/algorithms/optimizers/steppable_optimizer.py +++ /dev/null @@ -1,303 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""SteppableOptimizer interface""" -from __future__ import annotations - -from abc import abstractmethod, ABC -from collections.abc import Callable -from dataclasses import dataclass -from .optimizer import Optimizer, POINT, OptimizerResult - - -@dataclass -class AskData(ABC): - """Base class for return type of :meth:`~.SteppableOptimizer.ask`. - - Args: - x_fun: Point or list of points where the function needs to be evaluated to compute the next - state of the optimizer. - x_jac: Point or list of points where the gradient/jacobian needs to be evaluated to compute - the next state of the optimizer. - - """ - - x_fun: POINT | list[POINT] | None = None - x_jac: POINT | list[POINT] | None = None - - -@dataclass -class TellData(ABC): - """Base class for argument type of :meth:`~.SteppableOptimizer.tell`. - - Args: - eval_fun: Image of the function at :attr:`~.ask_data.x_fun`. - eval_jac: Image of the gradient-jacobian at :attr:`~.ask_data.x_jac`. - - """ - - eval_fun: float | list[float] | None = None - eval_jac: POINT | list[POINT] | None = None - - -@dataclass -class OptimizerState: - """Base class representing the state of the optimizer. - - This class stores the current state of the optimizer, given by the current point and - (optionally) information like the function value, the gradient or the number of - function evaluations. This dataclass can also store any other individual variables that - change during the optimization. - - """ - - x: POINT - """Current optimization parameters.""" - fun: Callable[[POINT], float] | None - """Function being optimized.""" - jac: Callable[[POINT], POINT] | None - """Jacobian of the function being optimized.""" - nfev: int | None - """Number of function evaluations so far in the optimization.""" - njev: int | None - """Number of jacobian evaluations so far in the opimization.""" - nit: int | None - """Number of optimization steps performed so far in the optimization.""" - - -class SteppableOptimizer(Optimizer): - """ - Base class for a steppable optimizer. - - This family of optimizers uses the `ask and tell interface - `_. - When using this interface the user has to call :meth:`~.ask` to get information about - how to evaluate the function (we are asking the optimizer about how to do the evaluation). - This information is typically the next points at which the function is evaluated, but depending - on the optimizer it can also determine whether to evaluate the function or its gradient. - Once the function has been evaluated, the user calls the method :meth:`~..tell` - to tell the optimizer what the result of the function evaluation(s) is. The optimizer then - updates its state accordingly and the user can decide whether to stop the optimization process - or to repeat a step. - - This interface is more customizable, and allows the user to have full control over the evaluation - of the function. - - Examples: - - An example where the evaluation of the function has a chance of failing. The user, with - specific knowledge about his function can catch this errors and handle them before passing - the result to the optimizer. - - .. code-block:: python - - import random - import numpy as np - from qiskit.algorithms.optimizers import GradientDescent - - def objective(x): - if random.choice([True, False]): - return None - else: - return (np.linalg.norm(x) - 1) ** 2 - - def grad(x): - if random.choice([True, False]): - return None - else: - return 2 * (np.linalg.norm(x) - 1) * x / np.linalg.norm(x) - - - initial_point = np.random.normal(0, 1, size=(100,)) - - optimizer = GradientDescent(maxiter=20) - optimizer.start(x0=initial_point, fun=objective, jac=grad) - - while optimizer.continue_condition(): - ask_data = optimizer.ask() - evaluated_gradient = None - - while evaluated_gradient is None: - evaluated_gradient = grad(ask_data.x_center) - optimizer.state.njev += 1 - - optmizer.state.nit += 1 - - cf = TellData(eval_jac=evaluated_gradient) - optimizer.tell(ask_data=ask_data, tell_data=tell_data) - - result = optimizer.create_result() - - - Users that aren't dealing with complicated functions and who are more familiar with step by step - optimization algorithms can use the :meth:`~.step` method which wraps the :meth:`~.ask` - and :meth:`~.tell` methods. In the same spirit the method :meth:`~.minimize` will optimize the - function and return the result. - - To see other libraries that use this interface one can visit: - https://optuna.readthedocs.io/en/stable/tutorial/20_recipes/009_ask_and_tell.html - - - """ - - def __init__( - self, - maxiter: int = 100, - ): - """ - Args: - maxiter: Number of steps in the optimization process before ending the loop. - """ - super().__init__() - self._state: OptimizerState | None = None - self.maxiter = maxiter - - @property - def state(self) -> OptimizerState: - """Return the current state of the optimizer.""" - return self._state - - @state.setter - def state(self, state: OptimizerState) -> None: - """Set the current state of the optimizer.""" - self._state = state - - def ask(self) -> AskData: - """Ask the optimizer for a set of points to evaluate. - - This method asks the optimizer which are the next points to evaluate. - These points can, e.g., correspond to function values and/or its derivative. - It may also correspond to variables that let the user infer which points to evaluate. - It is the first method inside of a :meth:`~.step` in the optimization process. - - Returns: - An object containing the data needed to make the function evaluation to advance the - optimization process. - - """ - raise NotImplementedError - - def tell(self, ask_data: AskData, tell_data: TellData) -> None: - """Updates the optimization state using the results of the function evaluation. - - A canonical optimization example using :meth:`~.ask` and :meth:`~.tell` can be seen - in :meth:`~.step`. - - Args: - ask_data: Contains the information on how the evaluation was done. - tell_data: Contains all relevant information about the evaluation of the objective - function. - """ - raise NotImplementedError - - @abstractmethod - def evaluate(self, ask_data: AskData) -> TellData: - """Evaluates the function according to the instructions contained in :attr:`~.ask_data`. - - If the user decides to use :meth:`~.step` instead of :meth:`~.ask` and :meth:`~.tell` - this function will contain the logic on how to evaluate the function. - - Args: - ask_data: Contains the information on how to do the evaluation. - - Returns: - Data of all relevant information about the function evaluation. - - """ - raise NotImplementedError - - def _callback_wrapper(self) -> None: - """ - Wraps the callback function to accommodate each optimizer. - """ - pass - - def step(self) -> None: - """Performs one step in the optimization process. - - This method composes :meth:`~.ask`, :meth:`~.evaluate`, and :meth:`~.tell` to make a "step" - in the optimization process. - """ - ask_data = self.ask() - tell_data = self.evaluate(ask_data=ask_data) - self.tell(ask_data=ask_data, tell_data=tell_data) - - # pylint: disable=invalid-name - @abstractmethod - def start( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> None: - """Populates the state of the optimizer with the data provided and sets all the counters to 0. - - Args: - fun: Function to minimize. - x0: Initial point. - jac: Function to compute the gradient. - bounds: Bounds of the search space. - - """ - raise NotImplementedError - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - """Minimizes the function. - - For well behaved functions the user can call this method to minimize a function. - If the user wants more control on how to evaluate the function a custom loop can be - created using :meth:`~.ask` and :meth:`~.tell` and evaluating the function manually. - - Args: - fun: Function to minimize. - x0: Initial point. - jac: Function to compute the gradient. - bounds: Bounds of the search space. - - Returns: - Object containing the result of the optimization. - - """ - self.start(x0=x0, fun=fun, jac=jac, bounds=bounds) - while self.continue_condition(): - self.step() - self._callback_wrapper() - return self.create_result() - - @abstractmethod - def create_result(self) -> OptimizerResult: - """Returns the result of the optimization. - - All the information needed to create such a result should be stored in the optimizer state - and will typically contain the best point found, the function value and gradient at that point, - the number of function and gradient evaluation and the number of iterations in the optimization. - - Returns: - The result of the optimization process. - - """ - raise NotImplementedError - - def continue_condition(self) -> bool: - """Condition that indicates the optimization process should continue. - - Returns: - ``True`` if the optimization process should continue, ``False`` otherwise. - """ - return self.state.nit < self.maxiter diff --git a/qiskit/algorithms/optimizers/tnc.py b/qiskit/algorithms/optimizers/tnc.py deleted file mode 100644 index 06174e51ace9..000000000000 --- a/qiskit/algorithms/optimizers/tnc.py +++ /dev/null @@ -1,83 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Truncated Newton (TNC) optimizer.""" -from __future__ import annotations - - -from .scipy_optimizer import SciPyOptimizer - - -class TNC(SciPyOptimizer): - """ - Truncated Newton (TNC) optimizer. - - TNC uses a truncated Newton algorithm to minimize a function with variables subject to bounds. - This algorithm uses gradient information; it is also called Newton Conjugate-Gradient. - It differs from the :class:`CG` method as it wraps a C implementation and allows each variable - to be given upper and lower bounds. - - Uses scipy.optimize.minimize TNC - For further detail, please refer to - See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html - """ - - _OPTIONS = ["maxiter", "disp", "accuracy", "ftol", "xtol", "gtol", "eps"] - - # pylint: disable=unused-argument - def __init__( - self, - maxiter: int = 100, - disp: bool = False, - accuracy: float = 0, - ftol: float = -1, - xtol: float = -1, - gtol: float = -1, - tol: float | None = None, - eps: float = 1e-08, - options: dict | None = None, - max_evals_grouped: int = 1, - **kwargs, - ) -> None: - """ - Args: - maxiter: Maximum number of function evaluation. - disp: Set to True to print convergence messages. - accuracy: Relative precision for finite difference calculations. - If <= machine_precision, set to sqrt(machine_precision). Defaults to 0. - ftol: Precision goal for the value of f in the stopping criterion. - If ftol < 0.0, ftol is set to 0.0 defaults to -1. - xtol: Precision goal for the value of x in the stopping criterion - (after applying x scaling factors). - If xtol < 0.0, xtol is set to sqrt(machine_precision). Defaults to -1. - gtol: Precision goal for the value of the projected gradient in - the stopping criterion (after applying x scaling factors). - If gtol < 0.0, gtol is set to 1e-2 * sqrt(accuracy). - Setting it to 0.0 is not recommended. Defaults to -1. - tol: Tolerance for termination. - eps: Step size used for numerical approximation of the Jacobian. - options: A dictionary of solver options. - max_evals_grouped: Max number of default gradient evaluations performed simultaneously. - kwargs: additional kwargs for scipy.optimize.minimize. - """ - if options is None: - options = {} - for k, v in list(locals().items()): - if k in self._OPTIONS: - options[k] = v - super().__init__( - "TNC", - options=options, - tol=tol, - max_evals_grouped=max_evals_grouped, - **kwargs, - ) diff --git a/qiskit/algorithms/optimizers/umda.py b/qiskit/algorithms/optimizers/umda.py deleted file mode 100644 index edea27939ade..000000000000 --- a/qiskit/algorithms/optimizers/umda.py +++ /dev/null @@ -1,355 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Univariate Marginal Distribution Algorithm (Estimation-of-Distribution-Algorithm).""" - -from __future__ import annotations - -import warnings - -from collections.abc import Callable -from typing import Any -import numpy as np -from scipy.stats import norm -from qiskit.utils import algorithm_globals - -from .optimizer import OptimizerResult, POINT -from .scipy_optimizer import Optimizer, OptimizerSupportLevel - - -class UMDA(Optimizer): - """Continuous Univariate Marginal Distribution Algorithm (UMDA). - - UMDA [1] is a specific type of Estimation of Distribution Algorithm (EDA) where new individuals - are sampled from univariate normal distributions and are updated in each iteration of the - algorithm by the best individuals found in the previous iteration. - - .. seealso:: - - This original implementation of the UDMA optimizer for Qiskit was inspired by my - (Vicente P. Soloviev) work on the EDAspy Python package [2]. - - EDAs are stochastic search algorithms and belong to the family of the evolutionary algorithms. - The main difference is that EDAs have a probabilistic model which is updated in each iteration - from the best individuals of previous generations (elite selection). Depending on the complexity - of the probabilistic model, EDAs can be classified in different ways. In this case, UMDA is a - univariate EDA as the embedded probabilistic model is univariate. - - UMDA has been compared to some of the already implemented algorithms in Qiskit library to - optimize the parameters of variational algorithms such as QAOA or VQE and competitive results - have been obtained [1]. UMDA seems to provide very good solutions for those circuits in which - the number of layers is not big. - - The optimization process can be personalized depending on the parameters chosen in the - initialization. The main parameter is the population size. The bigger it is, the final result - will be better. However, this increases the complexity of the algorithm and the runtime will - be much heavier. In the work [1] different experiments have been performed where population - size has been set to 20 - 30. - - .. note:: - - The UMDA implementation has more parameters but these have default values for the - initialization for better understanding of the user. For example, ``\alpha`` parameter has - been set to 0.5 and is the percentage of the population which is selected in each iteration - to update the probabilistic model. - - - Example: - - This short example runs UMDA to optimize the parameters of a variational algorithm. Here we - will use the same operator as used in the algorithms introduction, which was originally - computed by Qiskit Nature for an H2 molecule. The minimum energy of the H2 Hamiltonian can - be found quite easily so we are able to set maxiters to a small value. - - .. code-block:: python - - from qiskit.opflow import X, Z, I - from qiskit import Aer - from qiskit.algorithms.optimizers import UMDA - from qiskit.algorithms import QAOA - from qiskit.utils import QuantumInstance - - - H2_op = (-1.052373245772859 * I ^ I) + \ - (0.39793742484318045 * I ^ Z) + \ - (-0.39793742484318045 * Z ^ I) + \ - (-0.01128010425623538 * Z ^ Z) + \ - (0.18093119978423156 * X ^ X) - - p = 2 # Toy example: 2 layers with 2 parameters in each layer: 4 variables - - opt = UMDA(maxiter=100, size_gen=20) - - backend = Aer.get_backend('statevector_simulator') - vqe = QAOA(opt, - quantum_instance=QuantumInstance(backend=backend), - reps=p) - - result = vqe.compute_minimum_eigenvalue(operator=H2_op) - - If it is desired to modify the percentage of individuals considered to update the - probabilistic model, then this code can be used. Here for example we set the 60% instead - of the 50% predefined. - - .. code-block:: python - - opt = UMDA(maxiter=100, size_gen=20, alpha = 0.6) - - backend = Aer.get_backend('statevector_simulator') - vqe = QAOA(opt, - quantum_instance=QuantumInstance(backend=backend), - reps=p) - - result = vqe.compute_minimum_eigenvalue(operator=qubit_op) - - - References: - - [1]: Vicente P. Soloviev, Pedro Larrañaga and Concha Bielza (2022, July). Quantum Parametric - Circuit Optimization with Estimation of Distribution Algorithms. In 2022 The Genetic and - Evolutionary Computation Conference (GECCO). DOI: https://doi.org/10.1145/3520304.3533963 - - [2]: Vicente P. Soloviev. Python package EDAspy. - https://github.com/VicentePerezSoloviev/EDAspy. - """ - - ELITE_FACTOR = 0.4 - STD_BOUND = 0.3 - - def __init__( - self, - maxiter: int = 100, - size_gen: int = 20, - alpha: float = 0.5, - callback: Callable[[int, np.array, float], None] | None = None, - ) -> None: - r""" - Args: - maxiter: Maximum number of iterations. - size_gen: Population size of each generation. - alpha: Percentage (0, 1] of the population to be selected as elite selection. - callback: A callback function passed information in each iteration step. The - information is, in this order: the number of function evaluations, the parameters, - the best function value in this iteration. - """ - - self.size_gen = size_gen - self.maxiter = maxiter - self.alpha = alpha - self._vector: np.ndarray | None = None - # initialization of generation - self._generation: np.ndarray | None = None - self._dead_iter = int(self._maxiter / 5) - - self._truncation_length = int(size_gen * alpha) - - super().__init__() - - self._best_cost_global: float | None = None - self._best_ind_global: int | None = None - self._evaluations: np.ndarray | None = None - - self._n_variables: int | None = None - - self.callback = callback - - def _initialization(self) -> np.ndarray: - vector = np.zeros((4, self._n_variables)) - - vector[0, :] = np.pi # mu - vector[1, :] = 0.5 # std - - return vector - - # build a generation of size SIZE_GEN from prob vector - def _new_generation(self): - """Build a new generation sampled from the vector of probabilities. - Updates the generation pandas dataframe - """ - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - gen = algorithm_globals.random.normal( - self._vector[0, :], self._vector[1, :], [self._size_gen, self._n_variables] - ) - - self._generation = self._generation[: int(self.ELITE_FACTOR * len(self._generation))] - self._generation = np.vstack((self._generation, gen)) - - # truncate the generation at alpha percent - def _truncation(self): - """Selection of the best individuals of the actual generation. - Updates the generation by selecting the best individuals. - """ - best_indices = self._evaluations.argsort()[: self._truncation_length] - self._generation = self._generation[best_indices, :] - self._evaluations = np.take(self._evaluations, best_indices) - - # check each individual of the generation - def _check_generation(self, objective_function): - """Check the cost of each individual in the cost function implemented by the user.""" - self._evaluations = np.apply_along_axis(objective_function, 1, self._generation) - - # update the probability vector - def _update_vector(self): - """From the best individuals update the vector of normal distributions in order to the next - generation can sample from it. Update the vector of normal distributions - """ - - for i in range(self._n_variables): - self._vector[0, i], self._vector[1, i] = norm.fit(self._generation[:, i]) - if self._vector[1, i] < self.STD_BOUND: - self._vector[1, i] = self.STD_BOUND - - def minimize( - self, - fun: Callable[[POINT], float], - x0: POINT, - jac: Callable[[POINT], POINT] | None = None, - bounds: list[tuple[float, float]] | None = None, - ) -> OptimizerResult: - - not_better_count = 0 - result = OptimizerResult() - - if isinstance(x0, float): - x0 = [x0] - self._n_variables = len(x0) - - self._best_cost_global = 999999999999 - self._best_ind_global = 9999999 - history = [] - self._evaluations = np.array(0) - - self._vector = self._initialization() - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - # initialization of generation - self._generation = algorithm_globals.random.normal( - self._vector[0, :], self._vector[1, :], [self._size_gen, self._n_variables] - ) - - for _ in range(self._maxiter): - self._check_generation(fun) - self._truncation() - self._update_vector() - - best_mae_local: float = min(self._evaluations) - - history.append(best_mae_local) - best_ind_local = np.where(self._evaluations == best_mae_local)[0][0] - best_ind_local = self._generation[best_ind_local] - - # update the best values ever - if best_mae_local < self._best_cost_global: - self._best_cost_global = best_mae_local - self._best_ind_global = best_ind_local - not_better_count = 0 - - else: - not_better_count += 1 - if not_better_count >= self._dead_iter: - break - - if self.callback is not None: - self.callback( - len(history) * self._size_gen, self._best_ind_global, self._best_cost_global - ) - - self._new_generation() - - result.x = self._best_ind_global - result.fun = self._best_cost_global - result.nfev = len(history) * self._size_gen - - return result - - @property - def size_gen(self) -> int: - """Returns the size of the generations (number of individuals per generation)""" - return self._size_gen - - @size_gen.setter - def size_gen(self, value: int): - """ - Sets the size of the generations of the algorithm. - - Args: - value: Size of the generations (number of individuals per generation). - - Raises: - ValueError: If `value` is lower than 1. - """ - if value <= 0: - raise ValueError("The size of the generation should be greater than 0.") - self._size_gen = value - - @property - def maxiter(self) -> int: - """Returns the maximum number of iterations""" - return self._maxiter - - @maxiter.setter - def maxiter(self, value: int): - """ - Sets the maximum number of iterations of the algorithm. - - Args: - value: Maximum number of iterations of the algorithm. - - Raises: - ValueError: If `value` is lower than 1. - """ - if value <= 0: - raise ValueError("The maximum number of iterations should be greater than 0.") - - self._maxiter = value - - @property - def alpha(self) -> float: - """Returns the alpha parameter value (percentage of population selected to update - probabilistic model)""" - return self._alpha - - @alpha.setter - def alpha(self, value: float): - """ - Sets the alpha parameter (percentage of individuals selected to update the probabilistic - model) - - Args: - value: Percentage (0,1] of generation selected to update the probabilistic model. - - Raises: - ValueError: If `value` is lower than 0 or greater than 1. - """ - if (value <= 0) or (value > 1): - raise ValueError(f"alpha must be in the range (0, 1], value given was {value}") - - self._alpha = value - - @property - def settings(self) -> dict[str, Any]: - return { - "maxiter": self.maxiter, - "alpha": self.alpha, - "size_gen": self.size_gen, - "callback": self.callback, - } - - def get_support_level(self): - """Get the support level dictionary.""" - return { - "gradient": OptimizerSupportLevel.ignored, - "bounds": OptimizerSupportLevel.ignored, - "initial_point": OptimizerSupportLevel.required, - } diff --git a/qiskit/algorithms/phase_estimators/__init__.py b/qiskit/algorithms/phase_estimators/__init__.py deleted file mode 100644 index 2ef5b089aaed..000000000000 --- a/qiskit/algorithms/phase_estimators/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Phase Estimators.""" - -from .phase_estimator import PhaseEstimator -from .phase_estimation import PhaseEstimation -from .phase_estimation_result import PhaseEstimationResult -from .phase_estimation_scale import PhaseEstimationScale -from .hamiltonian_phase_estimation import HamiltonianPhaseEstimation -from .hamiltonian_phase_estimation_result import HamiltonianPhaseEstimationResult -from .ipe import IterativePhaseEstimation - -__all__ = [ - "PhaseEstimator", - "PhaseEstimation", - "PhaseEstimationResult", - "PhaseEstimationScale", - "HamiltonianPhaseEstimation", - "HamiltonianPhaseEstimationResult", - "IterativePhaseEstimation", -] diff --git a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation.py b/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation.py deleted file mode 100644 index 87907c39e304..000000000000 --- a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation.py +++ /dev/null @@ -1,309 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Phase estimation for the spectrum of a Hamiltonian""" - -from __future__ import annotations - -import warnings - -from qiskit import QuantumCircuit -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_arg -from qiskit.opflow import ( - SummedOp, - PauliOp, - MatrixOp, - PauliSumOp, - StateFn, - EvolutionBase, - PauliTrotterEvolution, - I, -) -from qiskit.providers import Backend -from .phase_estimation import PhaseEstimation -from .hamiltonian_phase_estimation_result import HamiltonianPhaseEstimationResult -from .phase_estimation_scale import PhaseEstimationScale -from ...circuit.library import PauliEvolutionGate -from ...primitives import BaseSampler -from ...quantum_info import SparsePauliOp, Statevector, Pauli -from ...synthesis import EvolutionSynthesis - - -class HamiltonianPhaseEstimation: - r"""Run the Quantum Phase Estimation algorithm to find the eigenvalues of a Hermitian operator. - - This class is nearly the same as :class:`~qiskit.algorithms.PhaseEstimation`, differing only - in that the input in that class is a unitary operator, whereas here the input is a Hermitian - operator from which a unitary will be obtained by scaling and exponentiating. The scaling is - performed in order to prevent the phases from wrapping around :math:`2\pi`. - The problem of estimating eigenvalues :math:`\lambda_j` of the Hermitian operator - :math:`H` is solved by running a circuit representing - - .. math:: - - \exp(i b H) |\psi\rangle = \sum_j \exp(i b \lambda_j) c_j |\lambda_j\rangle, - - where the input state is - - .. math:: - - |\psi\rangle = \sum_j c_j |\lambda_j\rangle, - - and :math:`\lambda_j` are the eigenvalues of :math:`H`. - - Here, :math:`b` is a scaling factor sufficiently large to map positive :math:`\lambda` to - :math:`[0,\pi)` and negative :math:`\lambda` to :math:`[\pi,2\pi)`. Each time the circuit is - run, one measures a phase corresponding to :math:`lambda_j` with probability :math:`|c_j|^2`. - - If :math:`H` is a Pauli sum, the bound :math:`b` is computed from the sum of the absolute - values of the coefficients of the terms. There is no way to reliably recover eigenvalues - from phases very near the endpoints of these intervals. Because of this you should be aware - that for degenerate cases, such as :math:`H=Z`, the eigenvalues :math:`\pm 1` will be - mapped to the same phase, :math:`\pi`, and so cannot be distinguished. In this case, you need - to specify a larger bound as an argument to the method ``estimate``. - - This class uses and works together with :class:`~qiskit.algorithms.PhaseEstimationScale` to - manage scaling the Hamiltonian and the phases that are obtained by the QPE algorithm. This - includes setting, or computing, a bound on the eigenvalues of the operator, using this - bound to obtain a scale factor, scaling the operator, and shifting and scaling the measured - phases to recover the eigenvalues. - - Note that, although we speak of "evolving" the state according the Hamiltonian, in the - present algorithm, we are not actually considering time evolution. Rather, the role of time is - played by the scaling factor, which is chosen to best extract the eigenvalues of the - Hamiltonian. - - A few of the ideas in the algorithm may be found in Ref. [1]. - - **Reference:** - - [1]: Quantum phase estimation of multiple eigenvalues for small-scale (noisy) experiments - T.E. O'Brien, B. Tarasinski, B.M. Terhal - `arXiv:1809.09697 `_ - - """ - - @deprecate_arg( - "quantum_instance", - additional_msg=( - "Instead, use the ``sampler`` argument. See https://qisk.it/algo_migration for a " - "migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - num_evaluation_qubits: int, - quantum_instance: QuantumInstance | Backend | None = None, - sampler: BaseSampler | None = None, - ) -> None: - r""" - Args: - num_evaluation_qubits: The number of qubits used in estimating the phase. The phase will - be estimated as a binary string with this many bits. - quantum_instance: Deprecated: The quantum instance on which - the circuit will be run. - sampler: The sampler primitive on which the circuit will be sampled. - """ - # Avoid double warning on deprecated used of `quantum_instance`. - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - self._phase_estimation = PhaseEstimation( - num_evaluation_qubits=num_evaluation_qubits, - quantum_instance=quantum_instance, - sampler=sampler, - ) - - def _get_scale(self, hamiltonian, bound=None) -> PhaseEstimationScale: - if bound is None: - return PhaseEstimationScale.from_pauli_sum(hamiltonian) - - return PhaseEstimationScale(bound) - - def _get_unitary( - self, hamiltonian, pe_scale, evolution: EvolutionSynthesis | EvolutionBase - ) -> QuantumCircuit: - """Evolve the Hamiltonian to obtain a unitary. - - Apply the scaling to the Hamiltonian that has been computed from an eigenvalue bound - and compute the unitary by applying the evolution object. - """ - - if self._phase_estimation._sampler is not None: - - evo = PauliEvolutionGate(hamiltonian, -pe_scale.scale, synthesis=evolution) - unitary = QuantumCircuit(evo.num_qubits) - unitary.append(evo, unitary.qubits) - - return unitary.decompose().decompose() - else: - # scale so that phase does not wrap. - scaled_hamiltonian = -pe_scale.scale * hamiltonian - unitary = evolution.convert(scaled_hamiltonian.exp_i()) - if not isinstance(unitary, QuantumCircuit): - unitary = unitary.to_circuit() - - return unitary.decompose().decompose() - - # Decomposing twice allows some 1Q Hamiltonians to give correct results - # when using MatrixEvolution(), that otherwise would give incorrect results. - # It does not break any others that we tested. - - def estimate( - self, - hamiltonian: PauliOp | MatrixOp | SummedOp | Pauli | SparsePauliOp | PauliSumOp, - state_preparation: StateFn | QuantumCircuit | Statevector | None = None, - evolution: EvolutionSynthesis | EvolutionBase | None = None, - bound: float | None = None, - ) -> HamiltonianPhaseEstimationResult: - """Run the Hamiltonian phase estimation algorithm. - - Args: - hamiltonian: A Hermitian operator. If the algorithm is used with a ``Sampler`` - primitive, the allowed types are ``Pauli``, ``SparsePauliOp``, and ``PauliSumOp``. - If the algorithm is used with a ``QuantumInstance``, ``PauliOp, ``MatrixOp``, - ``PauliSumOp``, and ``SummedOp`` types are allowed. - state_preparation: The ``StateFn`` to be prepared, whose eigenphase will be - measured. If this parameter is omitted, no preparation circuit will be run and - input state will be the all-zero state in the computational basis. - evolution: An evolution converter that generates a unitary from ``hamiltonian``. If - ``None``, then the default ``PauliTrotterEvolution`` is used. - bound: An upper bound on the absolute value of the eigenvalues of - ``hamiltonian``. If omitted, then ``hamiltonian`` must be a Pauli sum, or a - ``PauliOp``, in which case a bound will be computed. If ``hamiltonian`` - is a ``MatrixOp``, then ``bound`` may not be ``None``. The tighter the bound, - the higher the resolution of computed phases. - - Returns: - ``HamiltonianPhaseEstimationResult`` instance containing the result of the estimation - and diagnostic information. - - Raises: - TypeError: If ``evolution`` is not of type ``EvolutionSynthesis`` when a ``Sampler`` is - provided. - TypeError: If ``hamiltonian`` type is not ``Pauli`` or ``SparsePauliOp`` or - ``PauliSumOp`` when a ``Sampler`` is provided. - ValueError: If ``bound`` is ``None`` and ``hamiltonian`` is not a Pauli sum, i.e. a - ``PauliSumOp`` or a ``SummedOp`` whose terms are of type ``PauliOp``. - TypeError: If ``evolution`` is not of type ``EvolutionBase`` when no ``Sampler`` is - provided. - """ - if self._phase_estimation._sampler is not None: - if evolution is not None and not isinstance(evolution, EvolutionSynthesis): - raise TypeError(f"Expecting type EvolutionSynthesis, got {type(evolution)}") - if not isinstance(hamiltonian, (Pauli, SparsePauliOp, PauliSumOp)): - raise TypeError( - f"Expecting Hamiltonian type Pauli, SparsePauliOp or PauliSumOp, " - f"got {type(hamiltonian)}." - ) - - if isinstance(state_preparation, Statevector): - circuit = QuantumCircuit(state_preparation.num_qubits) - circuit.prepare_state(state_preparation.data) - state_preparation = circuit - if isinstance(hamiltonian, PauliSumOp): - id_coefficient, hamiltonian_no_id = _remove_identity_pauli_sum_op(hamiltonian) - else: - id_coefficient = 0.0 - hamiltonian_no_id = hamiltonian - pe_scale = self._get_scale(hamiltonian_no_id, bound) - unitary = self._get_unitary(hamiltonian_no_id, pe_scale, evolution) - else: - if evolution is None: - evolution = PauliTrotterEvolution() - elif not isinstance(evolution, EvolutionBase): - raise TypeError(f"Expecting type EvolutionBase, got {type(evolution)}") - - if isinstance(hamiltonian, PauliSumOp): - hamiltonian = hamiltonian.to_pauli_op() - elif isinstance(hamiltonian, PauliOp): - hamiltonian = SummedOp([hamiltonian]) - - if isinstance(hamiltonian, SummedOp): - # remove identitiy terms - # The term prop to the identity is removed from hamiltonian. - # This is done for three reasons: - # 1. Work around an unknown bug that otherwise causes the energies to be wrong in some - # cases. - # 2. Allow working with a simpler Hamiltonian, one with fewer terms. - # 3. Tighten the bound on the eigenvalues so that the spectrum is better resolved, i.e. - # occupies more of the range of values representable by the qubit register. - # The coefficient of this term will be added to the eigenvalues. - id_coefficient, hamiltonian_no_id = _remove_identity(hamiltonian) - # get the rescaling object - pe_scale = self._get_scale(hamiltonian_no_id, bound) - - # get the unitary - unitary = self._get_unitary(hamiltonian_no_id, pe_scale, evolution) - - elif isinstance(hamiltonian, MatrixOp): - if bound is None: - raise ValueError("bound must be specified if Hermitian operator is MatrixOp") - - # Do not subtract an identity term from the matrix, so do not compensate. - id_coefficient = 0.0 - pe_scale = self._get_scale(hamiltonian, bound) - unitary = self._get_unitary(hamiltonian, pe_scale, evolution) - else: - raise TypeError(f"Hermitian operator of type {type(hamiltonian)} not supported.") - - if state_preparation is not None and isinstance(state_preparation, StateFn): - state_preparation = state_preparation.to_circuit_op().to_circuit() - # run phase estimation - phase_estimation_result = self._phase_estimation.estimate( - unitary=unitary, state_preparation=state_preparation - ) - return HamiltonianPhaseEstimationResult( - phase_estimation_result=phase_estimation_result, - id_coefficient=id_coefficient, - phase_estimation_scale=pe_scale, - ) - - -def _remove_identity(pauli_sum: SummedOp): - """Remove any identity operators from `pauli_sum`. Return - the sum of the coefficients of the identities and the new operator. - """ - idcoeff = 0.0 - ops = [] - for op in pauli_sum: - p = op.primitive - if p.x.any() or p.z.any(): - ops.append(op) - else: - idcoeff += op.coeff - - return idcoeff, SummedOp(ops) - - -def _remove_identity_pauli_sum_op(pauli_sum: PauliSumOp | SparsePauliOp): - """Remove any identity operators from ``pauli_sum``. Return - the sum of the coefficients of the identities and the new operator. - """ - - def _get_identity(size): - identity = I - for _ in range(size - 1): - identity = identity ^ I - return identity - - idcoeff = 0.0 - if isinstance(pauli_sum, PauliSumOp): - for operator in pauli_sum: - if operator.primitive.paulis == ["I" * pauli_sum.num_qubits]: - idcoeff += operator.primitive.coeffs[0] - pauli_sum = pauli_sum - operator.primitive.coeffs[0] * _get_identity( - pauli_sum.num_qubits - ) - - return idcoeff, pauli_sum.reduce() diff --git a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py b/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py deleted file mode 100644 index ce844427b04a..000000000000 --- a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py +++ /dev/null @@ -1,108 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Result from running HamiltonianPhaseEstimation""" - -from __future__ import annotations - -from collections.abc import Mapping -from typing import cast -from qiskit.algorithms.algorithm_result import AlgorithmResult -from .phase_estimation_result import PhaseEstimationResult -from .phase_estimation_scale import PhaseEstimationScale - - -class HamiltonianPhaseEstimationResult(AlgorithmResult): - """Store and manipulate results from running `HamiltonianPhaseEstimation`. - - This API of this class is nearly the same as `PhaseEstimatorResult`, differing only in - the presence of an additional keyword argument in the methods. If `scaled` - is `False`, then the phases are not translated and scaled to recover the - eigenvalues of the Hamiltonian. Instead `phi` in :math:`[0, 1)` is returned, - as is the case when then unitary is not derived from a Hamiltonian. - - This class is meant to be instantiated via `HamiltonianPhaseEstimation.estimate`. - """ - - def __init__( - self, - phase_estimation_result: PhaseEstimationResult, - phase_estimation_scale: PhaseEstimationScale, - id_coefficient: float, - ) -> None: - """ - Args: - phase_estimation_result: The result object returned by PhaseEstimation.estimate. - phase_estimation_scale: object used to scale phases to obtain eigenvalues. - id_coefficient: The coefficient of the identity term in the Hamiltonian. - Eigenvalues are computed without this term so that the - coefficient must added to give correct eigenvalues. - This is done automatically when retrieving eigenvalues. - """ - super().__init__() - self._phase_estimation_scale = phase_estimation_scale - self._id_coefficient = id_coefficient - self._phase_estimation_result = phase_estimation_result - - def filter_phases( - self, cutoff: float = 0.0, scaled: bool = True, as_float: bool = True - ) -> Mapping[str | float, float]: - """Filter phases as does `PhaseEstimatorResult.filter_phases`, with - the addition that `phi` is shifted and translated to return eigenvalues - of the Hamiltonian. - - Args: - cutoff: Minimum weight of number of counts required to keep a bit string. - The default value is `0.0`. - scaled: If False, return `phi` in :math:`[0, 1)` rather than the eigenvalues of - the Hamiltonian. - as_float: If `True`, returned keys are floats in :math:`[0.0, 1.0)`. If `False` - returned keys are bit strings. - - Raises: - ValueError: if `as_float` is `False` and `scaled` is `True`. - - Returns: - A dict of filtered phases. - """ - if scaled and not as_float: - raise ValueError("`as_float` must be `True` if `scaled` is `True`.") - - phases = self._phase_estimation_result.filter_phases(cutoff, as_float=as_float) - if scaled: - return cast( - dict, self._phase_estimation_scale.scale_phases(phases, self._id_coefficient) - ) - else: - return cast(dict, phases) - - @property - def phase(self) -> float: - """The most likely phase of the unitary corresponding to the Hamiltonian. - - Returns: - The most likely phase. - """ - return self._phase_estimation_result.phase - - @property - def most_likely_eigenvalue(self) -> float: - """The most likely eigenvalue of the Hamiltonian. - - This method calls `most_likely_phase` and scales the result to - obtain an eigenvalue. - - Returns: - The most likely eigenvalue of the Hamiltonian. - """ - phase = self.phase - return self._phase_estimation_scale.scale_phase(phase, self._id_coefficient) diff --git a/qiskit/algorithms/phase_estimators/ipe.py b/qiskit/algorithms/phase_estimators/ipe.py deleted file mode 100644 index e8e583027a92..000000000000 --- a/qiskit/algorithms/phase_estimators/ipe.py +++ /dev/null @@ -1,229 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - - -"""The Iterative Quantum Phase Estimation Algorithm.""" - -from __future__ import annotations - -import numpy - -import qiskit -from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.circuit.classicalregister import ClassicalRegister -from qiskit.providers import Backend -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_arg -from qiskit.algorithms.exceptions import AlgorithmError -from .phase_estimator import PhaseEstimator -from .phase_estimator import PhaseEstimatorResult -from ...primitives import BaseSampler - - -class IterativePhaseEstimation(PhaseEstimator): - """Run the Iterative quantum phase estimation (QPE) algorithm. - - Given a unitary circuit and a circuit preparing an eigenstate, return the phase of the - eigenvalue as a number in :math:`[0,1)` using the iterative phase estimation algorithm. - - [1]: Dobsicek et al. (2006), Arbitrary accuracy iterative phase estimation algorithm as a two - qubit benchmark, `arxiv/quant-ph/0610214 `_ - """ - - @deprecate_arg( - "quantum_instance", - additional_msg=( - "Instead, use the ``sampler`` argument. See https://qisk.it/algo_migration for a " - "migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - num_iterations: int, - quantum_instance: QuantumInstance | Backend | None = None, - sampler: BaseSampler | None = None, - ) -> None: - r""" - Args: - num_iterations: The number of iterations (rounds) of the phase estimation to run. - quantum_instance: Deprecated: The quantum instance on which the - circuit will be run. - sampler: The sampler primitive on which the circuit will be sampled. - - Raises: - ValueError: if num_iterations is not greater than zero. - AlgorithmError: If neither sampler nor quantum instance is provided. - """ - if sampler is None and quantum_instance is None: - raise AlgorithmError( - "Neither a sampler nor a quantum instance was provided. Please provide one of them." - ) - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - self._quantum_instance = quantum_instance - if num_iterations <= 0: - raise ValueError("`num_iterations` must be greater than zero.") - self._num_iterations = num_iterations - self._sampler = sampler - - def construct_circuit( - self, - unitary: QuantumCircuit, - state_preparation: QuantumCircuit, - k: int, - omega: float = 0.0, - measurement: bool = False, - ) -> QuantumCircuit: - """Construct the kth iteration Quantum Phase Estimation circuit. - - For details of parameters, see Fig. 2 in https://arxiv.org/pdf/quant-ph/0610214.pdf. - - Args: - unitary: The circuit representing the unitary operator whose eigenvalue (via phase) - will be measured. - state_preparation: The circuit that prepares the state whose eigenphase will be - measured. If this parameter is omitted, no preparation circuit - will be run and input state will be the all-zero state in the - computational basis. - k: the iteration idx. - omega: the feedback angle. - measurement: Boolean flag to indicate if measurement should - be included in the circuit. - - Returns: - QuantumCircuit: the quantum circuit per iteration - """ - k = self._num_iterations if k is None else k - # The auxiliary (phase measurement) qubit - phase_register = QuantumRegister(1, name="a") - eigenstate_register = QuantumRegister(unitary.num_qubits, name="q") - qc = QuantumCircuit(eigenstate_register) - qc.add_register(phase_register) - if isinstance(state_preparation, QuantumCircuit): - qc.append(state_preparation, eigenstate_register) - elif state_preparation is not None: - qc += state_preparation.construct_circuit("circuit", eigenstate_register) - # hadamard on phase_register[0] - qc.h(phase_register[0]) - # controlled-U - # TODO: We may want to allow flexibility in how the power is computed - # For example, it may be desirable to compute the power via Trotterization, if - # we are doing Trotterization anyway. - unitary_power = unitary.power(2 ** (k - 1)).control() - qc = qc.compose(unitary_power, [unitary.num_qubits] + list(range(0, unitary.num_qubits))) - qc.p(omega, phase_register[0]) - # hadamard on phase_register[0] - qc.h(phase_register[0]) - if measurement: - c = ClassicalRegister(1, name="c") - qc.add_register(c) - qc.measure(phase_register, c) - return qc - - def _estimate_phase_iteratively(self, unitary, state_preparation): - """ - Main loop of iterative phase estimation. - """ - omega_coef = 0 - # k runs from the number of iterations back to 1 - for k in range(self._num_iterations, 0, -1): - omega_coef /= 2 - - if self._sampler is not None: - - qc = self.construct_circuit( - unitary, state_preparation, k, -2 * numpy.pi * omega_coef, True - ) - try: - sampler_job = self._sampler.run([qc]) - result = sampler_job.result().quasi_dists[0] - except Exception as exc: - raise AlgorithmError("The primitive job failed!") from exc - x = 1 if result.get(1, 0) > result.get(0, 0) else 0 - - elif self._quantum_instance.is_statevector: - qc = self.construct_circuit( - unitary, state_preparation, k, -2 * numpy.pi * omega_coef, measurement=False - ) - result = self._quantum_instance.execute(qc) - complete_state_vec = result.get_statevector(qc) - ancilla_density_mat = qiskit.quantum_info.partial_trace( - complete_state_vec, range(unitary.num_qubits) - ) - ancilla_density_mat_diag = numpy.diag(ancilla_density_mat) - max_amplitude = max( - ancilla_density_mat_diag.min(), ancilla_density_mat_diag.max(), key=abs - ) - x = numpy.where(ancilla_density_mat_diag == max_amplitude)[0][0] - else: - qc = self.construct_circuit( - unitary, state_preparation, k, -2 * numpy.pi * omega_coef, measurement=True - ) - measurements = self._quantum_instance.execute(qc).get_counts(qc) - x = 1 if measurements.get("1", 0) > measurements.get("0", 0) else 0 - omega_coef = omega_coef + x / 2 - return omega_coef - - # pylint: disable=signature-differs - def estimate( - self, unitary: QuantumCircuit, state_preparation: QuantumCircuit - ) -> "IterativePhaseEstimationResult": - """ - Estimate the eigenphase of the input unitary and initial-state pair. - - Args: - unitary: The circuit representing the unitary operator whose eigenvalue (via phase) - will be measured. - state_preparation: The circuit that prepares the state whose eigenphase will be - measured. If this parameter is omitted, no preparation circuit - will be run and input state will be the all-zero state in the - computational basis. - - Returns: - Estimated phase in an IterativePhaseEstimationResult object. - - Raises: - AlgorithmError: If neither sampler nor quantum instance is provided. - """ - phase = self._estimate_phase_iteratively(unitary, state_preparation) - - return IterativePhaseEstimationResult(self._num_iterations, phase) - - -class IterativePhaseEstimationResult(PhaseEstimatorResult): - """Phase Estimation Result.""" - - def __init__(self, num_iterations: int, phase: float) -> None: - """ - Args: - num_iterations: number of iterations used in the phase estimation. - phase: the estimated phase. - """ - - self._num_iterations = num_iterations - self._phase = phase - - @property - def phase(self) -> float: - r"""Return the estimated phase as a number in :math:`[0.0, 1.0)`. - - 1.0 corresponds to a phase of :math:`2\pi`. It is assumed that the input vector is an - eigenvector of the unitary so that the peak of the probability density occurs at the bit - string that most closely approximates the true phase. - """ - return self._phase - - @property - def num_iterations(self) -> int: - r"""Return the number of iterations used in the estimation algorithm.""" - return self._num_iterations diff --git a/qiskit/algorithms/phase_estimators/phase_estimation.py b/qiskit/algorithms/phase_estimators/phase_estimation.py deleted file mode 100644 index a3bc1d59c60d..000000000000 --- a/qiskit/algorithms/phase_estimators/phase_estimation.py +++ /dev/null @@ -1,268 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - - -"""The Quantum Phase Estimation Algorithm.""" - -from __future__ import annotations - -import numpy - -from qiskit.circuit import QuantumCircuit -import qiskit -from qiskit import circuit -from qiskit.circuit.classicalregister import ClassicalRegister -from qiskit.providers import Backend -from qiskit.utils import QuantumInstance -from qiskit.utils.deprecation import deprecate_arg -from qiskit.result import Result -from qiskit.algorithms.exceptions import AlgorithmError -from .phase_estimation_result import PhaseEstimationResult, _sort_phases -from .phase_estimator import PhaseEstimator -from ...primitives import BaseSampler - - -class PhaseEstimation(PhaseEstimator): - r"""Run the Quantum Phase Estimation (QPE) algorithm. - - This runs QPE with a multi-qubit register for reading the phases [1] - of input states. - - The algorithm takes as input a unitary :math:`U` and a state :math:`|\psi\rangle`, - which may be written - - .. math:: - - |\psi\rangle = \sum_j c_j |\phi_j\rangle, - - where :math:`|\phi_j\rangle` are eigenstates of :math:`U`. We prepare the quantum register - in the state :math:`|\psi\rangle` then apply :math:`U` leaving the register in the state - - .. math:: - - U|\psi\rangle = \sum_j \exp(i \phi_j) c_j |\phi_j\rangle. - - In the ideal case, one then measures the phase :math:`\phi_j` with probability - :math:`|c_j|^2`. In practice, many (or all) of the bit strings may be measured due to - noise and the possibility that :math:`\phi_j` may not be representable exactly by the - output register. In the latter case the probability for each eigenphase will be spread - across bitstrings, with amplitudes that decrease with distance from the bitstring most - closely approximating the eigenphase. - - The main input to the constructor is the number of qubits in the phase-reading register. - For phase estimation, there are two methods: - - first. `estimate`, which takes a state preparation circuit to prepare an input state, and - a unitary that will act on the input state. In this case, an instance of - :class:`qiskit.circuit.PhaseEstimation`, a QPE circuit, containing - the state preparation and input unitary will be constructed. - second. `estimate_from_pe_circuit`, which takes a quantum-phase-estimation circuit in which - the unitary and state preparation are already embedded. - - In both estimation methods, the QPE circuit is run on a backend - and the frequencies or counts of the phases represented by bitstrings - are recorded. The results are returned as an instance of - :class:`~qiskit.algorithms.phase_estimator_result.PhaseEstimationResult`. - - **Reference:** - - [1]: Michael A. Nielsen and Isaac L. Chuang. 2011. - Quantum Computation and Quantum Information: 10th Anniversary Edition (10th ed.). - Cambridge University Press, New York, NY, USA. - - """ - - @deprecate_arg( - "quantum_instance", - additional_msg=( - "Instead, use the ``sampler`` argument. See https://qisk.it/algo_migration for a " - "migration guide." - ), - since="0.24.0", - ) - def __init__( - self, - num_evaluation_qubits: int, - quantum_instance: QuantumInstance | Backend | None = None, - sampler: BaseSampler | None = None, - ) -> None: - r""" - Args: - num_evaluation_qubits: The number of qubits used in estimating the phase. The phase will - be estimated as a binary string with this many bits. - quantum_instance: Deprecated: The quantum instance on which the - circuit will be run. - sampler: The sampler primitive on which the circuit will be sampled. - - Raises: - AlgorithmError: If neither sampler nor quantum instance is provided. - """ - if sampler is None and quantum_instance is None: - raise AlgorithmError( - "Neither a sampler nor a quantum instance was provided. Please provide one of them." - ) - self._measurements_added = False - if num_evaluation_qubits is not None: - self._num_evaluation_qubits = num_evaluation_qubits - - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - self._quantum_instance = quantum_instance - self._sampler = sampler - - def construct_circuit( - self, unitary: QuantumCircuit, state_preparation: QuantumCircuit | None = None - ) -> QuantumCircuit: - """Return the circuit to be executed to estimate phases. - - This circuit includes as sub-circuits the core phase estimation circuit, - with the addition of the state-preparation circuit and possibly measurement instructions. - """ - num_evaluation_qubits = self._num_evaluation_qubits - num_unitary_qubits = unitary.num_qubits - - pe_circuit = circuit.library.PhaseEstimation(num_evaluation_qubits, unitary) - - if state_preparation is not None: - pe_circuit.compose( - state_preparation, - qubits=range(num_evaluation_qubits, num_evaluation_qubits + num_unitary_qubits), - inplace=True, - front=True, - ) - - return pe_circuit - - def _add_measurement_if_required(self, pe_circuit): - if self._sampler is not None or not self._quantum_instance.is_statevector: - # Measure only the evaluation qubits. - regname = "meas" - creg = ClassicalRegister(self._num_evaluation_qubits, regname) - pe_circuit.add_register(creg) - pe_circuit.barrier() - pe_circuit.measure( - range(self._num_evaluation_qubits), range(self._num_evaluation_qubits) - ) - - return circuit - - def _compute_phases( - self, num_unitary_qubits: int, circuit_result: Result - ) -> numpy.ndarray | qiskit.result.Counts: - """Compute frequencies/counts of phases from the result of running the QPE circuit. - - How the frequencies are computed depends on whether the backend computes amplitude or - samples outcomes. - - 1) If the backend is a statevector simulator, then the reduced density matrix of the - phase-reading register is computed from the combined phase-reading- and input-state - registers. The elements of the diagonal :math:`(i, i)` give the probability to measure the - each of the states `i`. The index `i` expressed as a binary integer with the LSB rightmost - gives the state of the phase-reading register with the LSB leftmost when interpreted as a - phase. In order to maintain the compact representation, the phases are maintained as decimal - integers. They may be converted to other forms via the results object, - `PhaseEstimationResult` or `HamiltonianPhaseEstimationResult`. - - 2) If the backend samples bitstrings, then the counts are first retrieved as a dict. The - binary strings (the keys) are then reversed so that the LSB is rightmost and the counts are - converted to frequencies. Then the keys are sorted according to increasing phase, so that - they can be easily understood when displaying or plotting a histogram. - - Args: - num_unitary_qubits: The number of qubits in the unitary. - circuit_result: the result object returned by the backend that ran the QPE circuit. - - Returns: - Either a dict or numpy.ndarray representing the frequencies of the phases. - - """ - if self._quantum_instance.is_statevector: - state_vec = circuit_result.get_statevector() - evaluation_density_matrix = qiskit.quantum_info.partial_trace( - state_vec, - range( - self._num_evaluation_qubits, self._num_evaluation_qubits + num_unitary_qubits - ), - ) - phases = evaluation_density_matrix.probabilities() - else: - # return counts with keys sorted numerically - num_shots = circuit_result.results[0].shots - counts = circuit_result.get_counts() - phases = {k[::-1]: counts[k] / num_shots for k in counts.keys()} - phases = _sort_phases(phases) - phases = qiskit.result.Counts( - phases, memory_slots=counts.memory_slots, creg_sizes=counts.creg_sizes - ) - - return phases - - def estimate_from_pe_circuit( - self, pe_circuit: QuantumCircuit, num_unitary_qubits: int - ) -> PhaseEstimationResult: - """Run the phase estimation algorithm on a phase estimation circuit - - Args: - pe_circuit: The phase estimation circuit. - num_unitary_qubits: Must agree with the number of qubits in the unitary in `pe_circuit`. - - Returns: - An instance of qiskit.algorithms.phase_estimator_result.PhaseEstimationResult. - - Raises: - AlgorithmError: Primitive job failed. - """ - - self._add_measurement_if_required(pe_circuit) - - if self._sampler is not None: - try: - circuit_job = self._sampler.run([pe_circuit]) - circuit_result = circuit_job.result() - except Exception as exc: - raise AlgorithmError("The primitive job failed!") from exc - phases = circuit_result.quasi_dists[0] - phases_bitstrings = {} - for key, phase in phases.items(): - bitstring_key = self._get_reversed_bitstring(self._num_evaluation_qubits, key) - phases_bitstrings[bitstring_key] = phase - phases = phases_bitstrings - - else: - circuit_result = self._quantum_instance.execute(pe_circuit) - phases = self._compute_phases(num_unitary_qubits, circuit_result) - return PhaseEstimationResult( - self._num_evaluation_qubits, circuit_result=circuit_result, phases=phases - ) - - def estimate( - self, - unitary: QuantumCircuit, - state_preparation: QuantumCircuit | None = None, - ) -> PhaseEstimationResult: - """Build a phase estimation circuit and run the corresponding algorithm. - - Args: - unitary: The circuit representing the unitary operator whose eigenvalues (via phase) - will be measured. - state_preparation: The circuit that prepares the state whose eigenphase will be - measured. If this parameter is omitted, no preparation circuit - will be run and input state will be the all-zero state in the - computational basis. - - Returns: - An instance of qiskit.algorithms.phase_estimator_result.PhaseEstimationResult. - """ - pe_circuit = self.construct_circuit(unitary, state_preparation) - num_unitary_qubits = unitary.num_qubits - - return self.estimate_from_pe_circuit(pe_circuit, num_unitary_qubits) diff --git a/qiskit/algorithms/phase_estimators/phase_estimation_result.py b/qiskit/algorithms/phase_estimators/phase_estimation_result.py deleted file mode 100644 index 8d87571af7e7..000000000000 --- a/qiskit/algorithms/phase_estimators/phase_estimation_result.py +++ /dev/null @@ -1,174 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Result of running PhaseEstimation""" -from __future__ import annotations -import numpy - -from qiskit.utils.deprecation import deprecate_func -from qiskit.result import Result -from .phase_estimator import PhaseEstimatorResult - - -class PhaseEstimationResult(PhaseEstimatorResult): - """Store and manipulate results from running `PhaseEstimation`. - - This class is instantiated by the ``PhaseEstimation`` class, not via user code. - The ``PhaseEstimation`` class generates a list of phases and corresponding weights. Upon - completion it returns the results as an instance of this class. The main method for - accessing the results is `filter_phases`. - - The canonical phase satisfying the ``PhaseEstimator`` interface, returned by the - attribute `phase`, is the most likely phase. - """ - - def __init__( - self, - num_evaluation_qubits: int, - circuit_result: Result, - phases: numpy.ndarray | dict[str, float], - ) -> None: - """ - Args: - num_evaluation_qubits: number of qubits in phase-readout register. - circuit_result: result object returned by method running circuit. - phases: ndarray or dict of phases and frequencies determined by QPE. - """ - super().__init__() - - self._phases = phases - # int: number of qubits in phase-readout register - self._num_evaluation_qubits = num_evaluation_qubits - self._circuit_result = circuit_result - - @property - def phases(self) -> numpy.ndarray | dict: - """Return all phases and their frequencies computed by QPE. - - This is an array or dict whose values correspond to weights on bit strings. - """ - return self._phases - - @property - def circuit_result(self) -> Result: - """Return the result object returned by running the QPE circuit (on hardware or simulator). - - This is useful for inspecting and troubleshooting the QPE algorithm. - """ - return self._circuit_result - - @property - @deprecate_func( - additional_msg="Instead, use the property ``phase``, which behaves the same.", - since="0.18.0", - is_property=True, - ) - def most_likely_phase(self) -> float: - r"""DEPRECATED - Return the most likely phase as a number in :math:`[0.0, 1.0)`. - - 1.0 corresponds to a phase of :math:`2\pi`. This selects the phase corresponding - to the bit string with the highesest probability. This is the most likely phase. - """ - return self.phase - - @property - def phase(self) -> float: - r"""Return the most likely phase as a number in :math:`[0.0, 1.0)`. - - 1.0 corresponds to a phase of :math:`2\pi`. This selects the phase corresponding - to the bit string with the highesest probability. This is the most likely phase. - """ - if isinstance(self.phases, dict): - binary_phase_string = max(self.phases, key=self.phases.get) - else: - # numpy.argmax ignores complex part of number. But, we take abs anyway - idx = numpy.argmax(abs(self.phases)) - binary_phase_string = numpy.binary_repr(idx, self._num_evaluation_qubits)[::-1] - phase = _bit_string_to_phase(binary_phase_string) - return phase - - def filter_phases(self, cutoff: float = 0.0, as_float: bool = True) -> dict: - """Return a filtered dict of phases (keys) and frequencies (values). - - Only phases with frequencies (counts) larger than `cutoff` are included. - It is assumed that the `run` method has been called so that the phases have been computed. - When using a noiseless, shot-based simulator to read a single phase that can - be represented exactly by `num_evaluation_qubits`, all the weight will - be concentrated on a single phase. In all other cases, many, or all, bit - strings will have non-zero weight. This method is useful for filtering - out these uninteresting bit strings. - - Args: - cutoff: Minimum weight of number of counts required to keep a bit string. - The default value is `0.0`. - as_float: If `True`, returned keys are floats in :math:`[0.0, 1.0)`. If `False` - returned keys are bit strings. - - Returns: - A filtered dict of phases (keys) and frequencies (values). - """ - if isinstance(self.phases, dict): - counts = self.phases - if as_float: - phases = { - _bit_string_to_phase(k): counts[k] for k in counts.keys() if counts[k] > cutoff - } - else: - phases = {k: counts[k] for k in counts.keys() if counts[k] > cutoff} - - else: - phases = {} - for idx, amplitude in enumerate(self.phases): - if amplitude > cutoff: - # Each index corresponds to a computational basis state with the LSB rightmost. - # But, we chose to apply the unitaries such that the phase is recorded - # in reverse order. So, we reverse the bitstrings here. - binary_phase_string = numpy.binary_repr(idx, self._num_evaluation_qubits)[::-1] - if as_float: - _key: str | float = _bit_string_to_phase(binary_phase_string) - else: - _key = binary_phase_string - phases[_key] = amplitude - - phases = _sort_phases(phases) - - return phases - - -def _bit_string_to_phase(binary_string: str) -> float: - """Convert bit string to a normalized phase in :math:`[0,1)`. - - It is assumed that the bit string is correctly padded and that the order of - the bits has been reversed relative to their order when the counts - were recorded. The LSB is the right most when interpreting the bitstring as - a phase. - - Args: - binary_string: A string of characters '0' and '1'. - - Returns: - A phase scaled to :math:`[0,1)`. - """ - n_qubits = len(binary_string) - return int(binary_string, 2) / (2**n_qubits) - - -def _sort_phases(phases: dict) -> dict: - """Sort a dict of bit strings representing phases (keys) and frequencies (values) by bit string. - - The bit strings are sorted according to increasing phase. This relies on Python - preserving insertion order when building dicts. - """ - pkeys = list(phases.keys()) - pkeys.sort(reverse=False) # Sorts in order of the integer encoded by binary string - phases = {k: phases[k] for k in pkeys} - return phases diff --git a/qiskit/algorithms/phase_estimators/phase_estimation_scale.py b/qiskit/algorithms/phase_estimators/phase_estimation_scale.py deleted file mode 100644 index e22b3e18cd9e..000000000000 --- a/qiskit/algorithms/phase_estimators/phase_estimation_scale.py +++ /dev/null @@ -1,160 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Scaling for Hamiltonian and eigenvalues to avoid phase wrapping""" -from __future__ import annotations -import numpy as np - -from qiskit.opflow import SummedOp, PauliSumOp -from qiskit.quantum_info import SparsePauliOp, Operator -from qiskit.quantum_info.operators.base_operator import BaseOperator - - -class PhaseEstimationScale: - """Set and use a bound on eigenvalues of a Hermitian operator in order to ensure phases are in - the desired range and to convert measured phases into eigenvectors. - - The ``bound`` is set when constructing this class. Then the method ``scale`` is used to find the - factor by which to scale the operator. - - If ``bound`` is equal exactly to the largest eigenvalue, and the smallest eigenvalue is minus - the largest, then these two eigenvalues will not be distinguished. For example, if the Hermitian - operator is the Pauli Z operator with eigenvalues :math:`1` and :math:`-1`, and ``bound`` is - :math:`1`, then both eigenvalues will be mapped to :math:`1`. - This can be avoided by making ``bound`` a bit larger. - - Increasing ``bound`` decreases the part of the interval :math:`[0, 1)` that is used to map - eigenvalues to ``phi``. However, sometimes this results in a better determination of the - eigenvalues, because 1) although there are fewer discrete phases in the useful range, it may - shift one of the discrete phases closer to the actual phase. And, 2) If one of the discrete - phases is close to, or exactly equal to the actual phase, then artifacts (probability) in - neighboring phases will be reduced. This is important because the artifacts may be larger than - the probability in a phase representing another eigenvalue of interest whose corresponding - eigenstate has a relatively small weight in the input state. - - """ - - def __init__(self, bound: float) -> None: - """ - Args: - bound: an upper bound on the absolute value of the eigenvalues of a Hermitian operator. - (The operator is not needed here.) - """ - self._bound = bound - - @property - def scale(self) -> float: - r"""Return the Hamiltonian scaling factor. - - Return the scale factor by which a Hermitian operator must be multiplied - so that the phase of the corresponding unitary is restricted to :math:`[-\pi, \pi]`. - This factor is computed from the bound on the absolute values of the eigenvalues - of the operator. The methods ``scale_phase`` and ``scale_phases`` are used recover - the eigenvalues corresponding the original (unscaled) Hermitian operator. - - Returns: - The scale factor. - """ - return np.pi / self._bound - - def scale_phase(self, phi: float, id_coefficient: float = 0.0) -> float: - r"""Convert a phase into an eigenvalue. - - The input phase ``phi`` corresponds to the eigenvalue of a unitary obtained by - exponentiating a scaled Hermitian operator. Recall that the phase - is obtained from ``phi`` as :math:`2\pi\phi`. Furthermore, the Hermitian operator - was scaled so that ``phi`` is restricted to :math:`[-1/2, 1/2]`, corresponding to - phases in :math:`[-\pi, \pi]`. But the values of `phi` read from the phase-readout - register are in :math:`[0, 1)`. Any value of ``phi`` greater than :math:`1/2` corresponds - to a raw phase of minus the complement with respect to 1. After this possible - shift, the phase is scaled by the inverse of the factor by which the - Hermitian operator was scaled to recover the eigenvalue of the Hermitian - operator. - - Args: - phi: Normalized phase in :math:`[0, 1)` to be converted to an eigenvalue. - id_coefficient: All eigenvalues are shifted by this value. - - Returns: - An eigenvalue computed from the input phase. - """ - w = 2 * self._bound - if phi <= 0.5: - return phi * w + id_coefficient - else: - return (phi - 1) * w + id_coefficient - - def scale_phases(self, phases: list | dict, id_coefficient: float = 0.0) -> dict | list: - """Convert a list or dict of phases to eigenvalues. - - The values in the list, or keys in the dict, are values of ``phi` and - are converted as described in the description of ``scale_phase``. In case - ``phases`` is a dict, the values of the dict are passed unchanged. - - Args: - phases: a list or dict of values of ``phi``. - id_coefficient: All eigenvalues are shifted by this value. - - Returns: - Eigenvalues computed from phases. - """ - if isinstance(phases, list): - phases = [self.scale_phase(x, id_coefficient) for x in phases] - else: - phases = {self.scale_phase(x, id_coefficient): phases[x] for x in phases.keys()} - - return phases - - @classmethod - def from_pauli_sum( - cls, pauli_sum: SummedOp | PauliSumOp | SparsePauliOp | Operator - ) -> "PhaseEstimationScale" | float: - """Create a PhaseEstimationScale from a `SummedOp` representing a sum of Pauli Operators. - - It is assumed that the ``pauli_sum`` is the sum of ``PauliOp`` objects. The bound on - the absolute value of the eigenvalues of the sum is obtained as the sum of the - absolute values of the coefficients of the terms. This is the best bound available in - the generic case. A ``PhaseEstimationScale`` object is instantiated using this bound. - - Args: - pauli_sum: A ``SummedOp`` whose terms are ``PauliOp`` objects. - - Raises: - ValueError: if ``pauli_sum`` is not a sum of Pauli operators. - - Returns: - A ``PhaseEstimationScale`` object - """ - if isinstance(pauli_sum, PauliSumOp): - bound = abs(pauli_sum.coeff) * sum(abs(coeff) for coeff in pauli_sum.coeffs) - return PhaseEstimationScale(bound) - elif isinstance(pauli_sum, SparsePauliOp): - bound = sum(abs(coeff) for coeff in pauli_sum.coeffs) - return PhaseEstimationScale(bound) - elif isinstance(pauli_sum, Operator): - bound = np.sum(np.abs(np.linalg.eigvalsh(pauli_sum))) - return PhaseEstimationScale(bound) - elif isinstance(pauli_sum, BaseOperator): - raise ValueError( - f"For the operator of type {type(pauli_sum)} the bound needs to be provided in the " - f"algorithm." - ) - else: - if pauli_sum.primitive_strings() != {"Pauli"}: - raise ValueError( - "`pauli_sum` must be a sum of Pauli operators. Got primitives {}.".format( - pauli_sum.primitive_strings() - ) - ) - - bound = abs(pauli_sum.coeff) * sum(abs(pauli.coeff) for pauli in pauli_sum) - return PhaseEstimationScale(bound) diff --git a/qiskit/algorithms/phase_estimators/phase_estimator.py b/qiskit/algorithms/phase_estimators/phase_estimator.py deleted file mode 100644 index 09f8113e5f4a..000000000000 --- a/qiskit/algorithms/phase_estimators/phase_estimator.py +++ /dev/null @@ -1,58 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Phase Estimator interface.""" - -from __future__ import annotations -from abc import ABC, abstractmethod -from qiskit.circuit import QuantumCircuit -from qiskit.algorithms.algorithm_result import AlgorithmResult - - -class PhaseEstimator(ABC): - """The Phase Estimator interface. - - Algorithms that can compute a phase for a unitary operator and initial state may implement this - interface to allow different algorithms to be used interchangeably. - - The phase returned is a canonical phase determined by the specific algorithm, such as the most - likely phase. In addition, the algorithm may provide an interface to retrieve phases by other - criteria. - """ - - @abstractmethod - def estimate( - self, - unitary: QuantumCircuit, - state_preparation: QuantumCircuit | None = None, - ) -> "PhaseEstimatorResult": - """Estimate the phase.""" - raise NotImplementedError - - @staticmethod - def _get_reversed_bitstring(length: int, number: int) -> str: - return f"{number:b}".zfill(length)[::-1] - - -class PhaseEstimatorResult(AlgorithmResult): - """Phase Estimator Result.""" - - @property - @abstractmethod - def phase(self) -> float: - r"""Return the estimated phase as a number in :math:`[0.0, 1.0)`. - - 1.0 corresponds to a phase of :math:`2\pi`. In case the phase estimation algorithm - computes more than one phase, this attribute returns a canonical single phase; for - example, the most likely phase. - """ - raise NotImplementedError diff --git a/qiskit/algorithms/state_fidelities/__init__.py b/qiskit/algorithms/state_fidelities/__init__.py deleted file mode 100644 index ea8e4e03bf89..000000000000 --- a/qiskit/algorithms/state_fidelities/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -===================================================================== -State Fidelity Interfaces (:mod:`qiskit.algorithms.state_fidelities`) -===================================================================== - -.. currentmodule:: qiskit.algorithms.state_fidelities - -State Fidelities -================ - -.. autosummary:: - :toctree: ../stubs/ - - BaseStateFidelity - ComputeUncompute - -Results -======= - - .. autosummary:: - :toctree: ../stubs/ - - StateFidelityResult - -""" - -from .base_state_fidelity import BaseStateFidelity -from .compute_uncompute import ComputeUncompute -from .state_fidelity_result import StateFidelityResult - -__all__ = ["BaseStateFidelity", "ComputeUncompute", "StateFidelityResult"] diff --git a/qiskit/algorithms/state_fidelities/base_state_fidelity.py b/qiskit/algorithms/state_fidelities/base_state_fidelity.py deleted file mode 100644 index 9395889bc5da..000000000000 --- a/qiskit/algorithms/state_fidelities/base_state_fidelity.py +++ /dev/null @@ -1,308 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Base state fidelity interface -""" - -from __future__ import annotations -from abc import ABC, abstractmethod -from collections.abc import Sequence, Mapping -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import ParameterVector - -from ..algorithm_job import AlgorithmJob -from .state_fidelity_result import StateFidelityResult - - -class BaseStateFidelity(ABC): - r""" - An interface to calculate state fidelities (state overlaps) for pairs of - (parametrized) quantum circuits. The calculation depends on the particular - fidelity method implementation, but can be always defined as the state overlap: - - .. math:: - - |\langle\psi(x)|\phi(y)\rangle|^2 - - where :math:`x` and :math:`y` are optional parametrizations of the - states :math:`\psi` and :math:`\phi` prepared by the circuits - ``circuit_1`` and ``circuit_2``, respectively. - - """ - - def __init__(self) -> None: - - # use cache for preventing unnecessary circuit compositions - self._circuit_cache: Mapping[tuple[int, int], QuantumCircuit] = {} - - @staticmethod - def _preprocess_values( - circuits: QuantumCircuit | Sequence[QuantumCircuit], - values: Sequence[float] | Sequence[Sequence[float]] | None = None, - ) -> Sequence[Sequence[float]]: - """ - Checks whether the passed values match the shape of the parameters - of the corresponding circuits and formats values to 2D list. - - Args: - circuits: List of circuits to be checked. - values: Parameter values corresponding to the circuits to be checked. - - Returns: - A 2D value list if the values match the circuits, or an empty 2D list - if values is None. - - Raises: - ValueError: if the number of parameter values doesn't match the number of - circuit parameters - TypeError: if the input values are not a sequence. - """ - - if isinstance(circuits, QuantumCircuit): - circuits = [circuits] - - if values is None: - for circuit in circuits: - if circuit.num_parameters != 0: - raise ValueError( - f"`values` cannot be `None` because circuit <{circuit.name}> has " - f"{circuit.num_parameters} free parameters." - ) - return [[]] - else: - - # Support ndarray - if isinstance(values, np.ndarray): - values = values.tolist() - if len(values) > 0 and isinstance(values[0], np.ndarray): - values = [v.tolist() for v in values] - - if not isinstance(values, Sequence): - raise TypeError( - f"Expected a sequence of numerical parameter values, " - f"but got input type {type(values)} instead." - ) - - # ensure 2d - if len(values) > 0 and not isinstance(values[0], Sequence) or len(values) == 0: - values = [values] - - return values - - def _check_qubits_match(self, circuit_1: QuantumCircuit, circuit_2: QuantumCircuit) -> None: - """ - Checks that the number of qubits of 2 circuits matches. - Args: - circuit_1: (Parametrized) quantum circuit. - circuit_2: (Parametrized) quantum circuit. - - Raises: - ValueError: when ``circuit_1`` and ``circuit_2`` don't have the - same number of qubits. - """ - - if circuit_1.num_qubits != circuit_2.num_qubits: - raise ValueError( - f"The number of qubits for the first circuit ({circuit_1.num_qubits}) " - f"and second circuit ({circuit_2.num_qubits}) are not the same." - ) - - @abstractmethod - def create_fidelity_circuit( - self, circuit_1: QuantumCircuit, circuit_2: QuantumCircuit - ) -> QuantumCircuit: - """ - Implementation-dependent method to create a fidelity circuit - from 2 circuit inputs. - - Args: - circuit_1: (Parametrized) quantum circuit. - circuit_2: (Parametrized) quantum circuit. - - Returns: - The fidelity quantum circuit corresponding to ``circuit_1`` and ``circuit_2``. - """ - raise NotImplementedError - - def _construct_circuits( - self, - circuits_1: QuantumCircuit | Sequence[QuantumCircuit], - circuits_2: QuantumCircuit | Sequence[QuantumCircuit], - ) -> Sequence[QuantumCircuit]: - """ - Constructs the list of fidelity circuits to be evaluated. - These circuits represent the state overlap between pairs of input circuits, - and their construction depends on the fidelity method implementations. - - Args: - circuits_1: (Parametrized) quantum circuits. - circuits_2: (Parametrized) quantum circuits. - - Returns: - List of constructed fidelity circuits. - - Raises: - ValueError: if the length of the input circuit lists doesn't match. - """ - - if isinstance(circuits_1, QuantumCircuit): - circuits_1 = [circuits_1] - if isinstance(circuits_2, QuantumCircuit): - circuits_2 = [circuits_2] - - if len(circuits_1) != len(circuits_2): - raise ValueError( - f"The length of the first circuit list({len(circuits_1)}) " - f"and second circuit list ({len(circuits_2)}) is not the same." - ) - - circuits = [] - for (circuit_1, circuit_2) in zip(circuits_1, circuits_2): - - # TODO: improve caching, what if the circuit is modified without changing the id? - circuit = self._circuit_cache.get((id(circuit_1), id(circuit_2))) - - if circuit is not None: - circuits.append(circuit) - else: - self._check_qubits_match(circuit_1, circuit_2) - - # re-parametrize input circuits - # TODO: make smarter checks to avoid unnecesary reparametrizations - parameters_1 = ParameterVector("x", circuit_1.num_parameters) - parametrized_circuit_1 = circuit_1.assign_parameters(parameters_1) - parameters_2 = ParameterVector("y", circuit_2.num_parameters) - parametrized_circuit_2 = circuit_2.assign_parameters(parameters_2) - - circuit = self.create_fidelity_circuit( - parametrized_circuit_1, parametrized_circuit_2 - ) - circuits.append(circuit) - # update cache - self._circuit_cache[id(circuit_1), id(circuit_2)] = circuit - - return circuits - - def _construct_value_list( - self, - circuits_1: Sequence[QuantumCircuit], - circuits_2: Sequence[QuantumCircuit], - values_1: Sequence[float] | Sequence[Sequence[float]] | None = None, - values_2: Sequence[float] | Sequence[Sequence[float]] | None = None, - ) -> list[float]: - """ - Preprocesses input parameter values to match the fidelity - circuit parametrization, and return in list format. - - Args: - circuits_1: (Parametrized) quantum circuits preparing the - first list of quantum states. - circuits_2: (Parametrized) quantum circuits preparing the - second list of quantum states. - values_1: Numerical parameters to be bound to the first circuits. - values_2: Numerical parameters to be bound to the second circuits. - - Returns: - List of parameter values for fidelity circuit. - - """ - values_1 = self._preprocess_values(circuits_1, values_1) - values_2 = self._preprocess_values(circuits_2, values_2) - - values = [] - if len(values_2[0]) == 0: - values = list(values_1) - elif len(values_1[0]) == 0: - values = list(values_2) - else: - for (val_1, val_2) in zip(values_1, values_2): - values.append(val_1 + val_2) - - return values - - @abstractmethod - def _run( - self, - circuits_1: QuantumCircuit | Sequence[QuantumCircuit], - circuits_2: QuantumCircuit | Sequence[QuantumCircuit], - values_1: Sequence[float] | Sequence[Sequence[float]] | None = None, - values_2: Sequence[float] | Sequence[Sequence[float]] | None = None, - **options, - ) -> StateFidelityResult: - r""" - Computes the state overlap (fidelity) calculation between two - (parametrized) circuits (first and second) for a specific set of parameter - values (first and second). - - Args: - circuits_1: (Parametrized) quantum circuits preparing :math:`|\psi\rangle`. - circuits_2: (Parametrized) quantum circuits preparing :math:`|\phi\rangle`. - values_1: Numerical parameters to be bound to the first set of circuits - values_2: Numerical parameters to be bound to the second set of circuits. - options: Primitive backend runtime options used for circuit execution. The order - of priority is\: options in ``run`` method > fidelity's default - options > primitive's default setting. - Higher priority setting overrides lower priority setting. - - Returns: - The result of the fidelity calculation. - """ - raise NotImplementedError - - def run( - self, - circuits_1: QuantumCircuit | Sequence[QuantumCircuit], - circuits_2: QuantumCircuit | Sequence[QuantumCircuit], - values_1: Sequence[float] | Sequence[Sequence[float]] | None = None, - values_2: Sequence[float] | Sequence[Sequence[float]] | None = None, - **options, - ) -> AlgorithmJob: - r""" - Runs asynchronously the state overlap (fidelity) calculation between two - (parametrized) circuits (first and second) for a specific set of parameter - values (first and second). This calculation depends on the particular - fidelity method implementation. - - Args: - circuits_1: (Parametrized) quantum circuits preparing :math:`|\psi\rangle`. - circuits_2: (Parametrized) quantum circuits preparing :math:`|\phi\rangle`. - values_1: Numerical parameters to be bound to the first set of circuits. - values_2: Numerical parameters to be bound to the second set of circuits. - options: Primitive backend runtime options used for circuit execution. The order - of priority is\: options in ``run`` method > fidelity's default - options > primitive's default setting. - Higher priority setting overrides lower priority setting. - - Returns: - Primitive job for the fidelity calculation. - The job's result is an instance of ``StateFidelityResult``. - """ - - job = AlgorithmJob(self._run, circuits_1, circuits_2, values_1, values_2, **options) - - job.submit() - return job - - def _truncate_fidelities(self, fidelities: Sequence[float]) -> Sequence[float]: - """ - Ensures fidelity result in [0,1]. - - Args: - fidelities: Sequence of raw fidelity results. - - Returns: - List of truncated fidelities. - - """ - return np.clip(fidelities, 0, 1).tolist() diff --git a/qiskit/algorithms/state_fidelities/compute_uncompute.py b/qiskit/algorithms/state_fidelities/compute_uncompute.py deleted file mode 100644 index be0879fadc27..000000000000 --- a/qiskit/algorithms/state_fidelities/compute_uncompute.py +++ /dev/null @@ -1,249 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Compute-uncompute fidelity interface using primitives -""" - -from __future__ import annotations -from collections.abc import Sequence -from copy import copy - -from qiskit import QuantumCircuit -from qiskit.primitives import BaseSampler -from qiskit.providers import Options - -from ..exceptions import AlgorithmError -from .base_state_fidelity import BaseStateFidelity -from .state_fidelity_result import StateFidelityResult - - -class ComputeUncompute(BaseStateFidelity): - r""" - This class leverages the sampler primitive to calculate the state - fidelity of two quantum circuits following the compute-uncompute - method (see [1] for further reference). - The fidelity can be defined as the state overlap. - - .. math:: - - |\langle\psi(x)|\phi(y)\rangle|^2 - - where :math:`x` and :math:`y` are optional parametrizations of the - states :math:`\psi` and :math:`\phi` prepared by the circuits - ``circuit_1`` and ``circuit_2``, respectively. - - **Reference:** - [1] Havlíček, V., Córcoles, A. D., Temme, K., Harrow, A. W., Kandala, - A., Chow, J. M., & Gambetta, J. M. (2019). Supervised learning - with quantum-enhanced feature spaces. Nature, 567(7747), 209-212. - `arXiv:1804.11326v2 [quant-ph] `_ - - """ - - def __init__( - self, - sampler: BaseSampler, - options: Options | None = None, - local: bool = False, - ) -> None: - r""" - Args: - sampler: Sampler primitive instance. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > fidelity's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting. - local: If set to ``True``, the fidelity is averaged over - single-qubit projectors - - .. math:: - - \hat{O} = \frac{1}{N}\sum_{i=1}^N|0_i\rangle\langle 0_i|, - - instead of the global projector :math:`|0\rangle\langle 0|^{\otimes n}`. - This coincides with the standard (global) fidelity in the limit of - the fidelity approaching 1. Might be used to increase the variance - to improve trainability in algorithms such as :class:`~.time_evolvers.PVQD`. - - Raises: - ValueError: If the sampler is not an instance of ``BaseSampler``. - """ - if not isinstance(sampler, BaseSampler): - raise ValueError( - f"The sampler should be an instance of BaseSampler, " f"but got {type(sampler)}" - ) - self._sampler: BaseSampler = sampler - self._local = local - self._default_options = Options() - if options is not None: - self._default_options.update_options(**options) - super().__init__() - - def create_fidelity_circuit( - self, circuit_1: QuantumCircuit, circuit_2: QuantumCircuit - ) -> QuantumCircuit: - """ - Combines ``circuit_1`` and ``circuit_2`` to create the - fidelity circuit following the compute-uncompute method. - - Args: - circuit_1: (Parametrized) quantum circuit. - circuit_2: (Parametrized) quantum circuit. - - Returns: - The fidelity quantum circuit corresponding to circuit_1 and circuit_2. - """ - if len(circuit_1.clbits) > 0: - circuit_1.remove_final_measurements() - if len(circuit_2.clbits) > 0: - circuit_2.remove_final_measurements() - - circuit = circuit_1.compose(circuit_2.inverse()) - circuit.measure_all() - return circuit - - def _run( - self, - circuits_1: QuantumCircuit | Sequence[QuantumCircuit], - circuits_2: QuantumCircuit | Sequence[QuantumCircuit], - values_1: Sequence[float] | Sequence[Sequence[float]] | None = None, - values_2: Sequence[float] | Sequence[Sequence[float]] | None = None, - **options, - ) -> StateFidelityResult: - r""" - Computes the state overlap (fidelity) calculation between two - (parametrized) circuits (first and second) for a specific set of parameter - values (first and second) following the compute-uncompute method. - - Args: - circuits_1: (Parametrized) quantum circuits preparing :math:`|\psi\rangle`. - circuits_2: (Parametrized) quantum circuits preparing :math:`|\phi\rangle`. - values_1: Numerical parameters to be bound to the first circuits. - values_2: Numerical parameters to be bound to the second circuits. - options: Primitive backend runtime options used for circuit execution. - The order of priority is: options in ``run`` method > fidelity's - default options > primitive's default setting. - Higher priority setting overrides lower priority setting. - - Returns: - The result of the fidelity calculation. - - Raises: - ValueError: At least one pair of circuits must be defined. - AlgorithmError: If the sampler job is not completed successfully. - """ - - circuits = self._construct_circuits(circuits_1, circuits_2) - if len(circuits) == 0: - raise ValueError( - "At least one pair of circuits must be defined to calculate the state overlap." - ) - values = self._construct_value_list(circuits_1, circuits_2, values_1, values_2) - - # The priority of run options is as follows: - # options in `evaluate` method > fidelity's default options > - # primitive's default options. - opts = copy(self._default_options) - opts.update_options(**options) - - job = self._sampler.run(circuits=circuits, parameter_values=values, **opts.__dict__) - - try: - result = job.result() - except Exception as exc: - raise AlgorithmError("Sampler job failed!") from exc - - if self._local: - raw_fidelities = [ - self._get_local_fidelity(prob_dist, circuit.num_qubits) - for prob_dist, circuit in zip(result.quasi_dists, circuits) - ] - else: - raw_fidelities = [ - self._get_global_fidelity(prob_dist) for prob_dist in result.quasi_dists - ] - fidelities = self._truncate_fidelities(raw_fidelities) - - return StateFidelityResult( - fidelities=fidelities, - raw_fidelities=raw_fidelities, - metadata=result.metadata, - options=self._get_local_options(opts.__dict__), - ) - - @property - def options(self) -> Options: - """Return the union of estimator options setting and fidelity default options, - where, if the same field is set in both, the fidelity's default options override - the primitive's default setting. - - Returns: - The fidelity default + estimator options. - """ - return self._get_local_options(self._default_options.__dict__) - - def update_default_options(self, **options): - """Update the fidelity's default options setting. - - Args: - **options: The fields to update the default options. - """ - - self._default_options.update_options(**options) - - def _get_local_options(self, options: Options) -> Options: - """Return the union of the primitive's default setting, - the fidelity default options, and the options in the ``run`` method. - The order of priority is: options in ``run`` method > fidelity's - default options > primitive's default setting. - - Args: - options: The fields to update the options - - Returns: - The fidelity default + estimator + run options. - """ - opts = copy(self._sampler.options) - opts.update_options(**options) - return opts - - def _get_global_fidelity(self, probability_distribution: dict[int, float]) -> float: - """Process the probability distribution of a measurement to determine the - global fidelity. - - Args: - probability_distribution: Obtained from the measurement result - - Returns: - The global fidelity. - """ - return probability_distribution.get(0, 0) - - def _get_local_fidelity( - self, probability_distribution: dict[int, float], num_qubits: int - ) -> float: - """Process the probability distribution of a measurement to determine the - local fidelity by averaging over single-qubit projectors. - - Args: - probability_distribution: Obtained from the measurement result - - Returns: - The local fidelity. - """ - fidelity = 0.0 - for qubit in range(num_qubits): - for bitstring, prob in probability_distribution.items(): - # Check whether the bit representing the current qubit is 0 - if not bitstring >> qubit & 1: - fidelity += prob / num_qubits - return fidelity diff --git a/qiskit/algorithms/state_fidelities/state_fidelity_result.py b/qiskit/algorithms/state_fidelities/state_fidelity_result.py deleted file mode 100644 index 88dca035f94c..000000000000 --- a/qiskit/algorithms/state_fidelities/state_fidelity_result.py +++ /dev/null @@ -1,37 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Fidelity result class -""" - -from __future__ import annotations - -from collections.abc import Sequence, Mapping -from typing import Any -from dataclasses import dataclass - -from qiskit.providers import Options - - -@dataclass(frozen=True) -class StateFidelityResult: - """This class stores the result of StateFidelity computations.""" - - fidelities: Sequence[float] - """List of truncated fidelity values for each pair of input circuits, ensured to be in [0,1].""" - raw_fidelities: Sequence[float] - """List of raw fidelity values for each pair of input circuits, which might not be in [0,1] - depending on the error mitigation method used.""" - metadata: Sequence[Mapping[str, Any]] - """Additional information about the fidelity calculation.""" - options: Options - """Primitive runtime options for the execution of the fidelity job.""" diff --git a/qiskit/algorithms/time_evolvers/__init__.py b/qiskit/algorithms/time_evolvers/__init__.py deleted file mode 100644 index c2ad1fe7ec26..000000000000 --- a/qiskit/algorithms/time_evolvers/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Quantum Time Evolution package.""" - -from .imaginary_time_evolver import ImaginaryTimeEvolver -from .real_time_evolver import RealTimeEvolver -from .time_evolution_problem import TimeEvolutionProblem -from .time_evolution_result import TimeEvolutionResult -from .trotterization import TrotterQRTE -from .pvqd import PVQD, PVQDResult -from .classical_methods import SciPyImaginaryEvolver, SciPyRealEvolver -from .variational import VarQITE, VarQRTE, VarQTE, VarQTEResult - -__all__ = [ - "ImaginaryTimeEvolver", - "RealTimeEvolver", - "TimeEvolutionProblem", - "TimeEvolutionResult", - "TrotterQRTE", - "PVQD", - "PVQDResult", - "SciPyImaginaryEvolver", - "SciPyRealEvolver", - "VarQITE", - "VarQRTE", - "VarQTE", - "VarQTEResult", -] diff --git a/qiskit/algorithms/time_evolvers/classical_methods/__init__.py b/qiskit/algorithms/time_evolvers/classical_methods/__init__.py deleted file mode 100644 index 266b349fbef7..000000000000 --- a/qiskit/algorithms/time_evolvers/classical_methods/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Classical Methods for Quantum Time Evolution package.""" - -from .scipy_real_evolver import SciPyRealEvolver -from .scipy_imaginary_evolver import SciPyImaginaryEvolver - -__all__ = ["SciPyRealEvolver", "SciPyImaginaryEvolver"] diff --git a/qiskit/algorithms/time_evolvers/classical_methods/evolve.py b/qiskit/algorithms/time_evolvers/classical_methods/evolve.py deleted file mode 100644 index 18c679a03c8f..000000000000 --- a/qiskit/algorithms/time_evolvers/classical_methods/evolve.py +++ /dev/null @@ -1,219 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Auxiliary functions for SciPy Time Evolvers""" -from __future__ import annotations -import logging -from scipy.sparse import csr_matrix -from scipy.sparse.linalg import expm_multiply -import numpy as np - -from qiskit.quantum_info.states import Statevector -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from qiskit import QuantumCircuit -from qiskit.opflow import PauliSumOp -from ..time_evolution_problem import TimeEvolutionProblem -from ..time_evolution_result import TimeEvolutionResult -from ...exceptions import AlgorithmError - -from ...list_or_dict import ListOrDict - -logger = logging.getLogger(__name__) - - -def _create_observable_output( - ops_ev_mean: np.ndarray, - evolution_problem: TimeEvolutionProblem, -) -> tuple[ListOrDict[tuple[np.ndarray, np.ndarray]], np.ndarray]: - """Creates the right output format for the evaluated auxiliary operators. - Args: - ops_ev_mean: Array containing the expectation value of each observable at each timestep. - evolution_problem: Time Evolution Problem to create the output of. - - Returns: - An output with the observables mean value at the appropriate times depending on whether - the auxiliary operators in the time evolution problem are a `list` or a `dict`. - - """ - - aux_ops = evolution_problem.aux_operators - - time_array = np.linspace(0, evolution_problem.time, ops_ev_mean.shape[-1]) - zero_array = np.zeros(ops_ev_mean.shape[-1]) # std=0 since it is an exact method - - operators_number = 0 if aux_ops is None else len(aux_ops) - - observable_evolution = [(ops_ev_mean[i], zero_array) for i in range(operators_number)] - - if isinstance(aux_ops, dict): - observable_evolution = dict(zip(aux_ops.keys(), observable_evolution)) - - return observable_evolution, time_array - - -def _create_obs_final( - ops_ev_mean: np.ndarray, - evolution_problem: TimeEvolutionProblem, -) -> ListOrDict[tuple[complex, complex]]: - """Creates the right output format for the final value of the auxiliary operators. - - Args: - ops_ev_mean: Array containing the expectation value of each observable at the final timestep. - evolution_problem: Evolution problem to create the output of. - - Returns: - An output with the observables mean value at the appropriate times depending on whether - the auxiliary operators in the evolution problem are a `list` or a `dict`. - - """ - - aux_ops = evolution_problem.aux_operators - aux_ops_evaluated: ListOrDict[tuple[complex, complex]] = [(op_ev, 0) for op_ev in ops_ev_mean] - if isinstance(aux_ops, dict): - aux_ops_evaluated = dict(zip(aux_ops.keys(), aux_ops_evaluated)) - return aux_ops_evaluated - - -def _evaluate_aux_ops( - aux_ops: list[csr_matrix], - state: np.ndarray, -) -> np.ndarray: - """Evaluates the aux operators if they are provided and stores their value. - - Returns: - Mean of the aux operators for a given state. - """ - op_means = np.array([np.real(state.conjugate().dot(op.dot(state))) for op in aux_ops]) - return op_means - - -def _operator_to_matrix(operator: BaseOperator | PauliSumOp): - - if isinstance(operator, PauliSumOp): - op_matrix = operator.to_spmatrix() - else: - try: - op_matrix = operator.to_matrix(sparse=True) - except TypeError: - logger.debug( - "WARNING: operator of type `%s` does not support sparse matrices. " - "Trying dense computation", - type(operator), - ) - try: - op_matrix = operator.to_matrix() - except AttributeError as ex: - raise AlgorithmError(f"Unsupported operator type `{type(operator)}`.") from ex - return op_matrix - - -def _build_scipy_operators( - evolution_problem: TimeEvolutionProblem, num_timesteps: int, real_time: bool -) -> tuple[np.ndarray, list[csr_matrix], csr_matrix]: - """Returns the matrices and parameters needed for time evolution in the appropriate format. - - Args: - evolution_problem: The definition of the evolution problem. - num_timesteps: Number of timesteps to be performed. - real_time: If `True`, returned operators will correspond to real time evolution, - Else, they will correspond to imaginary time evolution. - - Returns: - A tuple with the initial state, the list of operators to evaluate and the operator to be - exponentiated to perform one timestep. - - Raises: - ValueError: If the Hamiltonian can not be converted into a sparse matrix or dense matrix. - """ - # Convert the initial state and Hamiltonian into sparse matrices. - if isinstance(evolution_problem.initial_state, QuantumCircuit): - state = Statevector(evolution_problem.initial_state).data - else: - state = evolution_problem.initial_state.data - - hamiltonian = _operator_to_matrix(operator=evolution_problem.hamiltonian) - - if isinstance(evolution_problem.aux_operators, list): - aux_ops = [ - _operator_to_matrix(operator=aux_op) for aux_op in evolution_problem.aux_operators - ] - elif isinstance(evolution_problem.aux_operators, dict): - aux_ops = [ - _operator_to_matrix(operator=aux_op) - for aux_op in evolution_problem.aux_operators.values() - ] - else: - aux_ops = [] - timestep = evolution_problem.time / num_timesteps - step_operator = -((1.0j) ** real_time) * timestep * hamiltonian - return state, aux_ops, step_operator - - -def _evolve( - evolution_problem: TimeEvolutionProblem, num_timesteps: int, real_time: bool -) -> TimeEvolutionResult: - r"""Performs either real or imaginary time evolution :math:`\exp(-i t H)|\Psi\rangle`. - - Args: - evolution_problem: The definition of the evolution problem. - num_timesteps: Number of timesteps to be performed. - real_time: If `True`, returned operators will correspond to real time evolution, - Else, they will correspond to imaginary time evolution. - - Returns: - Evolution result which includes an evolved quantum state. - - Raises: - ValueError: If the Hamiltonian is time dependent. - ValueError: If the initial state is `None`. - - """ - if num_timesteps <= 0: - raise ValueError("Variable `num_timesteps` needs to be a positive integer.") - - if evolution_problem.t_param is not None: - raise ValueError("Time dependent Hamiltonians are not supported.") - - if evolution_problem.initial_state is None: - raise ValueError("Initial state is `None`") - - state, aux_ops, step_operator = _build_scipy_operators( - evolution_problem=evolution_problem, num_timesteps=num_timesteps, real_time=real_time - ) - - # Create empty arrays to store the time evolution of the aux operators. - number_operators = ( - 0 if evolution_problem.aux_operators is None else len(evolution_problem.aux_operators) - ) - ops_ev_mean = np.empty(shape=(number_operators, num_timesteps + 1), dtype=complex) - - renormalize = ( - (lambda state: state) if real_time else (lambda state: state / np.linalg.norm(state)) - ) - - # Perform the time evolution and stores the value of the operators at each timestep. - for ts in range(num_timesteps): - ops_ev_mean[:, ts] = _evaluate_aux_ops(aux_ops, state) - state = expm_multiply(A=step_operator, B=state) - state = renormalize(state) - - ops_ev_mean[:, num_timesteps] = _evaluate_aux_ops(aux_ops, state) - - observable_history, times = _create_observable_output(ops_ev_mean, evolution_problem) - aux_ops_evaluated = _create_obs_final(ops_ev_mean[:, -1], evolution_problem) - - return TimeEvolutionResult( - evolved_state=Statevector(state), - aux_ops_evaluated=aux_ops_evaluated, - observables=observable_history, - times=times, - ) diff --git a/qiskit/algorithms/time_evolvers/classical_methods/scipy_imaginary_evolver.py b/qiskit/algorithms/time_evolvers/classical_methods/scipy_imaginary_evolver.py deleted file mode 100644 index f181da10f436..000000000000 --- a/qiskit/algorithms/time_evolvers/classical_methods/scipy_imaginary_evolver.py +++ /dev/null @@ -1,51 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Classical Quantum Imaginary Time Evolution.""" - -from ..time_evolution_problem import TimeEvolutionProblem -from ..time_evolution_result import TimeEvolutionResult -from ..imaginary_time_evolver import ImaginaryTimeEvolver -from .evolve import _evolve - - -class SciPyImaginaryEvolver(ImaginaryTimeEvolver): - r"""Classical Evolver for imaginary time evolution. - - Evolves an initial state :math:`|\Psi\rangle` for an imaginary time :math:`\tau = it` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - Note that the precision of the evolver does not depend on the number of - timesteps taken. - """ - - def __init__(self, num_timesteps: int): - r""" - Args: - num_timesteps: The number of timesteps in the simulation. - Raises: - ValueError: If `num_timesteps` is not a positive integer. - """ - self.num_timesteps = num_timesteps - - def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult: - r"""Perform imaginary time evolution :math:`\exp(-\tau H)|\Psi\rangle`. - - Evolves an initial state :math:`|\Psi\rangle` for an imaginary time :math:`\tau` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - - Args: - evolution_problem: The definition of the evolution problem. - - Returns: - Evolution result which includes an evolved quantum state. - """ - return _evolve(evolution_problem, self.num_timesteps, real_time=False) diff --git a/qiskit/algorithms/time_evolvers/classical_methods/scipy_real_evolver.py b/qiskit/algorithms/time_evolvers/classical_methods/scipy_real_evolver.py deleted file mode 100644 index b01c16205bfd..000000000000 --- a/qiskit/algorithms/time_evolvers/classical_methods/scipy_real_evolver.py +++ /dev/null @@ -1,50 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Classical Quantum Real Time Evolution.""" -from .evolve import _evolve -from ..time_evolution_problem import TimeEvolutionProblem -from ..time_evolution_result import TimeEvolutionResult -from ..real_time_evolver import RealTimeEvolver - - -class SciPyRealEvolver(RealTimeEvolver): - r"""Classical Evolver for real time evolution. - - Evolves an initial state :math:`|\Psi\rangle` for a time :math:`t` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - Note that the precision of the evolver does not depend on the number of - timesteps taken. - """ - - def __init__(self, num_timesteps: int): - """ - Args: - num_timesteps: The number of timesteps in the simulation. - Raises: - ValueError: If `steps` is not a positive integer. - """ - self.num_timesteps = num_timesteps - - def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult: - r"""Perform real time evolution :math:`\exp(-i t H)|\Psi\rangle`. - - Evolves an initial state :math:`|\Psi\rangle` for a time :math:`t` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - - Args: - evolution_problem: The definition of the evolution problem. - - Returns: - Evolution result which includes an evolved quantum state. - """ - return _evolve(evolution_problem, self.num_timesteps, real_time=True) diff --git a/qiskit/algorithms/time_evolvers/imaginary_time_evolver.py b/qiskit/algorithms/time_evolvers/imaginary_time_evolver.py deleted file mode 100644 index e62d02e5ab9c..000000000000 --- a/qiskit/algorithms/time_evolvers/imaginary_time_evolver.py +++ /dev/null @@ -1,37 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Interface for Quantum Imaginary Time Evolution.""" - -from abc import ABC, abstractmethod - -from .time_evolution_problem import TimeEvolutionProblem -from .time_evolution_result import TimeEvolutionResult - - -class ImaginaryTimeEvolver(ABC): - """Interface for Quantum Imaginary Time Evolution.""" - - @abstractmethod - def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult: - r"""Perform imaginary time evolution :math:`\exp(-\tau H)|\Psi\rangle`. - - Evolves an initial state :math:`|\Psi\rangle` for an imaginary time :math:`\tau` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - - Args: - evolution_problem: The definition of the evolution problem. - - Returns: - Evolution result which includes an evolved quantum state. - """ - raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolvers/pvqd/__init__.py b/qiskit/algorithms/time_evolvers/pvqd/__init__.py deleted file mode 100644 index 9377ce631b4e..000000000000 --- a/qiskit/algorithms/time_evolvers/pvqd/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The projected Variational Quantum Dynamic (p-VQD) module.""" - -from .pvqd_result import PVQDResult -from .pvqd import PVQD - -__all__ = ["PVQD", "PVQDResult"] diff --git a/qiskit/algorithms/time_evolvers/pvqd/pvqd.py b/qiskit/algorithms/time_evolvers/pvqd/pvqd.py deleted file mode 100644 index bbd48df86651..000000000000 --- a/qiskit/algorithms/time_evolvers/pvqd/pvqd.py +++ /dev/null @@ -1,435 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The projected Variational Quantum Dynamics Algorithm.""" -from __future__ import annotations - -import logging -import warnings -from collections.abc import Callable - -import numpy as np - -from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit -from qiskit.circuit.library import PauliEvolutionGate -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.synthesis import EvolutionSynthesis, LieTrotter -from qiskit.utils import algorithm_globals - -from ...exceptions import AlgorithmError, QiskitError -from ...optimizers import Minimizer, Optimizer -from ...state_fidelities.base_state_fidelity import BaseStateFidelity -from ..real_time_evolver import RealTimeEvolver -from ..time_evolution_problem import TimeEvolutionProblem -from ..time_evolution_result import TimeEvolutionResult -from .pvqd_result import PVQDResult -from .utils import _get_observable_evaluator, _is_gradient_supported - -logger = logging.getLogger(__name__) - - -class PVQD(RealTimeEvolver): - """The projected Variational Quantum Dynamics (p-VQD) Algorithm. - - In each timestep, this algorithm computes the next state with a Trotter formula - (specified by the ``evolution`` argument) and projects the timestep onto a variational form - (``ansatz``). The projection is determined by maximizing the fidelity of the Trotter-evolved - state and the ansatz, using a classical optimization routine. See Ref. [1] for details. - - The following attributes can be set via the initializer but can also be read and - updated once the PVQD object has been constructed. - - Attributes: - - ansatz (QuantumCircuit): The parameterized circuit representing the time-evolved state. - initial_parameters (np.ndarray): The parameters of the ansatz at time 0. - optimizer (Optional[Union[Optimizer, Minimizer]]): The classical optimization routine - used to maximize the fidelity of the Trotter step and ansatz. - num_timesteps (Optional[int]): The number of timesteps to take. If None, it is automatically - selected to achieve a timestep of approximately 0.01. - evolution (Optional[EvolutionSynthesis]): The method to perform the Trotter step. - Defaults to first-order Lie-Trotter evolution. - use_parameter_shift (bool): If True, use the parameter shift rule for loss function - gradients (if the ansatz supports). - initial_guess (Optional[np.ndarray]): The starting point for the first classical optimization - run, at time 0. Defaults to random values in :math:`[-0.01, 0.01]`. - - Example: - - This snippet computes the real time evolution of a quantum Ising model on two - neighboring sites and keeps track of the magnetization. - - .. code-block:: python - - import numpy as np - - from qiskit.algorithms.state_fidelities import ComputeUncompute - from qiskit.algorithms.time_evolvers import TimeEvolutionProblem, PVQD - from qiskit.primitives import Estimator, Sampler - from qiskit.circuit.library import EfficientSU2 - from qiskit.quantum_info import SparsePauliOp, Pauli - from qiskit.algorithms.optimizers import L_BFGS_B - - sampler = Sampler() - fidelity = ComputeUncompute(sampler) - estimator = Estimator() - hamiltonian = 0.1 * SparsePauliOp(["ZZ", "IX", "XI"]) - observable = Pauli("ZZ") - ansatz = EfficientSU2(2, reps=1) - initial_parameters = np.zeros(ansatz.num_parameters) - - time = 1 - optimizer = L_BFGS_B() - - # setup the algorithm - pvqd = PVQD( - fidelity, - ansatz, - initial_parameters, - estimator, - num_timesteps=100, - optimizer=optimizer, - ) - - # specify the evolution problem - problem = TimeEvolutionProblem( - hamiltonian, time, aux_operators=[hamiltonian, observable] - ) - - # and evolve! - result = pvqd.evolve(problem) - - References: - - [1] Stefano Barison, Filippo Vicentini, and Giuseppe Carleo (2021), An efficient - quantum algorithm for the time evolution of parameterized circuits, - `Quantum 5, 512 `_. - """ - - def __init__( - self, - fidelity: BaseStateFidelity, - ansatz: QuantumCircuit, - initial_parameters: np.ndarray, - estimator: BaseEstimator | None = None, - optimizer: Optimizer | Minimizer | None = None, - num_timesteps: int | None = None, - evolution: EvolutionSynthesis | None = None, - use_parameter_shift: bool = True, - initial_guess: np.ndarray | None = None, - ) -> None: - """ - Args: - fidelity: A fidelity primitive used by the algorithm. - ansatz: A parameterized circuit preparing the variational ansatz to model the - time evolved quantum state. - initial_parameters: The initial parameters for the ansatz. Together with the ansatz, - these define the initial state of the time evolution. - estimator: An estimator primitive used for calculating expected values of auxiliary - operators (if provided via the problem). - optimizer: The classical optimizers used to minimize the overlap between - Trotterization and ansatz. Can be either a :class:`.Optimizer` or a callable - using the :class:`.Minimizer` protocol. This argument is optional since it is - not required for :meth:`get_loss`, but it has to be set before :meth:`evolve` - is called. - num_timesteps: The number of time steps. If ``None`` it will be set such that the - timestep is close to 0.01. - evolution: The evolution synthesis to use for the construction of the Trotter step. - Defaults to first-order Lie-Trotter decomposition, see also - :mod:`~qiskit.synthesis.evolution` for different options. - use_parameter_shift: If True, use the parameter shift rule to compute gradients. - If False, the optimizer will not be passed a gradient callable. In that case, - Qiskit optimizers will use a finite difference rule to approximate the gradients. - initial_guess: The initial guess for the first VQE optimization. Afterwards the - previous iteration result is used as initial guess. If None, this is set to - a random vector with elements in the interval :math:`[-0.01, 0.01]`. - """ - super().__init__() - if evolution is None: - evolution = LieTrotter() - - self.ansatz = ansatz - self.initial_parameters = initial_parameters - self.num_timesteps = num_timesteps - self.optimizer = optimizer - self.initial_guess = initial_guess - self.estimator = estimator - self.fidelity_primitive = fidelity - self.evolution = evolution - self.use_parameter_shift = use_parameter_shift - - def step( - self, - hamiltonian: BaseOperator | PauliSumOp, - ansatz: QuantumCircuit, - theta: np.ndarray, - dt: float, - initial_guess: np.ndarray, - ) -> tuple[np.ndarray, float]: - """Perform a single time step. - - Args: - hamiltonian: The Hamiltonian under which to evolve. - ansatz: The parameterized quantum circuit which attempts to approximate the - time-evolved state. - theta: The current parameters. - dt: The time step. - initial_guess: The initial guess for the classical optimization of the - fidelity between the next variational state and the Trotter-evolved last state. - If None, this is set to a random vector with elements in the interval - :math:`[-0.01, 0.01]`. - - Returns: - A tuple consisting of the next parameters and the fidelity of the optimization. - """ - self._validate_setup() - - loss, gradient = self.get_loss(hamiltonian, ansatz, dt, theta) - - if initial_guess is None: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - initial_guess = algorithm_globals.random.random(self.initial_parameters.size) * 0.01 - - if isinstance(self.optimizer, Optimizer): - optimizer_result = self.optimizer.minimize(loss, initial_guess, gradient) - else: - optimizer_result = self.optimizer(loss, initial_guess, gradient) - - # clip the fidelity to [0, 1] - fidelity = np.clip(1 - optimizer_result.fun, 0, 1) - - return theta + optimizer_result.x, fidelity - - def get_loss( - self, - hamiltonian: BaseOperator | PauliSumOp, - ansatz: QuantumCircuit, - dt: float, - current_parameters: np.ndarray, - ) -> tuple[Callable[[np.ndarray], float], Callable[[np.ndarray], np.ndarray]] | None: - """Get a function to evaluate the infidelity between Trotter step and ansatz. - - Args: - hamiltonian: The Hamiltonian under which to evolve. - ansatz: The parameterized quantum circuit which attempts to approximate the - time-evolved state. - dt: The time step. - current_parameters: The current parameters. - - Returns: - A callable to evaluate the infidelity and, if gradients are supported and required, - a second callable to evaluate the gradient of the infidelity. - """ - self._validate_setup(skip={"optimizer"}) - - # use Trotterization to evolve the current state - trotterized = ansatz.assign_parameters(current_parameters) - - evolution_gate = PauliEvolutionGate(hamiltonian, time=dt, synthesis=self.evolution) - - trotterized.append(evolution_gate, ansatz.qubits) - - # define the overlap of the Trotterized state and the ansatz - x = ParameterVector("w", ansatz.num_parameters) - shifted = ansatz.assign_parameters(current_parameters + x) - - def evaluate_loss(displacement: np.ndarray | list[np.ndarray]) -> float | np.ndarray: - """Evaluate the overlap of the ansatz with the Trotterized evolution. - - Args: - displacement: The parameters for the ansatz. - - Returns: - The fidelity of the ansatz with parameters ``theta`` and the Trotterized evolution. - - Raises: - AlgorithmError: If a primitive job fails. - """ - if isinstance(displacement, list): - displacement = np.asarray(displacement) - value_dict = {x_i: displacement[:, i].tolist() for i, x_i in enumerate(x)} - else: - value_dict = dict(zip(x, displacement)) - - param_dicts = self._transpose_param_dicts(value_dict) - num_of_param_sets = len(param_dicts) - states1 = [trotterized] * num_of_param_sets - states2 = [shifted] * num_of_param_sets - param_dicts2 = [list(param_dict.values()) for param_dict in param_dicts] - # the first state does not have free parameters so values_1 will be None by default - try: - job = self.fidelity_primitive.run(states1, states2, values_2=param_dicts2) - fidelities = np.array(job.result().fidelities) - except Exception as exc: - raise AlgorithmError("The primitive job failed!") from exc - - if len(fidelities) == 1: - fidelities = fidelities[0] - - # in principle, we could add different loss functions here, but we're currently - # not aware of a use-case for a different one than in the paper - return 1 - fidelities - - if _is_gradient_supported(ansatz) and self.use_parameter_shift: - - def evaluate_gradient(displacement: np.ndarray) -> np.ndarray: - """Evaluate the gradient with the parameter-shift rule. - - This is hardcoded here since the gradient framework does not support computing - gradients for overlaps. - - Args: - displacement: The parameters for the ansatz. - - Returns: - The gradient. - """ - # construct lists where each element is shifted by plus (or minus) pi/2 - dim = displacement.size - plus_shifts = (displacement + np.pi / 2 * np.identity(dim)).tolist() - minus_shifts = (displacement - np.pi / 2 * np.identity(dim)).tolist() - - evaluated = evaluate_loss(plus_shifts + minus_shifts) - - gradient = (evaluated[:dim] - evaluated[dim:]) / 2 - - return gradient - - else: - evaluate_gradient = None - - return evaluate_loss, evaluate_gradient - - def _transpose_param_dicts(self, params: dict) -> list[dict[Parameter, float]]: - p_0 = list(params.values())[0] - if isinstance(p_0, (list, np.ndarray)): - num_parameterizations = len(p_0) - param_bindings = [ - {param: value_list[i] for param, value_list in params.items()} # type: ignore - for i in range(num_parameterizations) - ] - else: - param_bindings = [params] - - return param_bindings - - def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult: - r"""Perform real time evolution :math:`\exp(-i t H)|\Psi\rangle`. - - Evolves an initial state :math:`|\Psi\rangle` for a time :math:`t` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - - Args: - evolution_problem: The evolution problem containing the hamiltonian, total evolution - time and observables to evaluate. - - Returns: - A result object containing the evolution information and evaluated observables. - - Raises: - ValueError: If ``aux_operators`` provided in the time evolution problem but no estimator - provided to the algorithm. - NotImplementedError: If the evolution problem contains an initial state. - """ - self._validate_setup() - - time = evolution_problem.time - observables = evolution_problem.aux_operators - hamiltonian = evolution_problem.hamiltonian - - # determine the number of timesteps and set the timestep - num_timesteps = ( - int(np.ceil(time / 0.01)) if self.num_timesteps is None else self.num_timesteps - ) - timestep = time / num_timesteps - - if evolution_problem.initial_state is not None: - raise NotImplementedError( - "Setting an initial state for the evolution is not yet supported for PVQD." - ) - - # get the function to evaluate the observables for a given set of ansatz parameters - if observables is not None: - if self.estimator is None: - raise ValueError( - "The evolution problem contained aux_operators but no estimator was provided. " - ) - evaluate_observables = _get_observable_evaluator( - self.ansatz, observables, self.estimator - ) - observable_values = [evaluate_observables(self.initial_parameters)] - - fidelities = [1.0] - parameters = [self.initial_parameters] - times = np.linspace(0, time, num_timesteps + 1).tolist() # +1 to include initial time 0 - - initial_guess = self.initial_guess - - for _ in range(num_timesteps): - # perform VQE to find the next parameters - next_parameters, fidelity = self.step( - hamiltonian, self.ansatz, parameters[-1], timestep, initial_guess - ) - - # set initial guess to last parameter update - initial_guess = next_parameters - parameters[-1] - - parameters.append(next_parameters) - fidelities.append(fidelity) - if observables is not None: - observable_values.append(evaluate_observables(next_parameters)) - - evolved_state = self.ansatz.assign_parameters(parameters[-1]) - - result = PVQDResult( - evolved_state=evolved_state, - times=times, - parameters=parameters, - fidelities=fidelities, - estimated_error=1 - np.prod(fidelities), - ) - if observables is not None: - result.observables = observable_values - result.aux_ops_evaluated = observable_values[-1] - - return result - - def _validate_setup(self, skip=None): - """Validate the current setup and raise an error if something misses to run.""" - - if skip is None: - skip = {} - - required_attributes = {"optimizer"}.difference(skip) - - for attr in required_attributes: - if getattr(self, attr, None) is None: - raise ValueError(f"The {attr} cannot be None.") - - if self.num_timesteps is not None and self.num_timesteps <= 0: - raise ValueError( - f"The number of timesteps must be positive but is {self.num_timesteps}." - ) - - if self.ansatz.num_parameters == 0: - raise QiskitError( - "The ansatz cannot have 0 parameters, otherwise it cannot be trained." - ) - - if len(self.initial_parameters) != self.ansatz.num_parameters: - raise QiskitError( - f"Mismatching number of parameters in the ansatz ({self.ansatz.num_parameters}) " - f"and the initial parameters ({len(self.initial_parameters)})." - ) diff --git a/qiskit/algorithms/time_evolvers/pvqd/pvqd_result.py b/qiskit/algorithms/time_evolvers/pvqd/pvqd_result.py deleted file mode 100644 index 65c2a8b18604..000000000000 --- a/qiskit/algorithms/time_evolvers/pvqd/pvqd_result.py +++ /dev/null @@ -1,54 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Result object for p-VQD.""" -from __future__ import annotations - -from collections.abc import Sequence - -import numpy as np -from qiskit.circuit import QuantumCircuit -from ..time_evolution_result import TimeEvolutionResult - - -class PVQDResult(TimeEvolutionResult): - """The result object for the p-VQD algorithm.""" - - def __init__( - self, - evolved_state: QuantumCircuit, - aux_ops_evaluated: list[tuple[complex, complex]] | None = None, - times: list[float] | None = None, - parameters: list[np.ndarray] | None = None, - fidelities: Sequence[float] | None = None, - estimated_error: float | None = None, - observables: list[list[float]] | None = None, - ): - """ - Args: - evolved_state: An evolved quantum state. - aux_ops_evaluated: Optional list of observables for which expected values on an evolved - state are calculated. These values are in fact tuples formatted as (mean, standard - deviation). - times: The times evaluated during the time integration. - parameters: The parameter values at each evaluation time. - fidelities: The fidelity of the Trotter step and variational update at each iteration. - estimated_error: The overall estimated error evaluated as one minus the - product of all fidelities. - observables: The value of the observables evaluated at each iteration. - """ - super().__init__(evolved_state, aux_ops_evaluated) - self.times = times - self.parameters = parameters - self.fidelities = fidelities - self.estimated_error = estimated_error - self.observables = observables diff --git a/qiskit/algorithms/time_evolvers/pvqd/utils.py b/qiskit/algorithms/time_evolvers/pvqd/utils.py deleted file mode 100644 index 9b3f330dd350..000000000000 --- a/qiskit/algorithms/time_evolvers/pvqd/utils.py +++ /dev/null @@ -1,109 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - - -"""Utilities for p-VQD.""" -from __future__ import annotations -import logging -from collections.abc import Callable - -import numpy as np - -from qiskit.circuit import QuantumCircuit, Parameter, ParameterExpression -from qiskit.compiler import transpile -from qiskit.exceptions import QiskitError -from qiskit.opflow.gradients.circuit_gradients import ParamShift -from qiskit.primitives import BaseEstimator -from qiskit.quantum_info.operators.base_operator import BaseOperator -from ...exceptions import AlgorithmError - -logger = logging.getLogger(__name__) - - -def _is_gradient_supported(ansatz: QuantumCircuit) -> bool: - """Check whether we can apply a simple parameter shift rule to obtain gradients.""" - - # check whether the circuit can be unrolled to supported gates - try: - unrolled = transpile(ansatz, basis_gates=ParamShift.SUPPORTED_GATES, optimization_level=0) - except QiskitError: - # failed to map to supported basis - logger.log( - logging.INFO, - "No gradient support: Failed to unroll to gates supported by parameter-shift.", - ) - return False - - # check whether all parameters are unique and we do not need to apply the chain rule - # (since it's not implemented yet) - total_num_parameters = 0 - for circuit_instruction in unrolled.data: - for param in circuit_instruction.operation.params: - if isinstance(param, ParameterExpression): - if isinstance(param, Parameter): - total_num_parameters += 1 - else: - logger.log( - logging.INFO, - "No gradient support: Circuit is only allowed to have plain parameters, " - "as the chain rule is not yet implemented.", - ) - return False - - if total_num_parameters != ansatz.num_parameters: - logger.log( - logging.INFO, - "No gradient support: Circuit is only allowed to have unique parameters, " - "as the product rule is not yet implemented.", - ) - return False - - return True - - -def _get_observable_evaluator( - ansatz: QuantumCircuit, - observables: BaseOperator | list[BaseOperator], - estimator: BaseEstimator, -) -> Callable[[np.ndarray], float | list[float]]: - """Get a callable to evaluate a (list of) observable(s) for given circuit parameters.""" - - def evaluate_observables(theta: np.ndarray) -> float | list[float]: - """Evaluate the observables for the ansatz parameters ``theta``. - - Args: - theta: The ansatz parameters. - - Returns: - The observables evaluated at the ansatz parameters. - - Raises: - AlgorithmError: If a primitive job fails. - """ - if isinstance(observables, list): - num_observables = len(observables) - obs = observables - else: - num_observables = 1 - obs = [observables] - states = [ansatz] * num_observables - parameter_values = [theta] * num_observables - - try: - estimator_job = estimator.run(states, obs, parameter_values=parameter_values) - results = estimator_job.result().values - except Exception as exc: - raise AlgorithmError("The primitive job failed!") from exc - - return results - - return evaluate_observables diff --git a/qiskit/algorithms/time_evolvers/real_time_evolver.py b/qiskit/algorithms/time_evolvers/real_time_evolver.py deleted file mode 100644 index 585da953755b..000000000000 --- a/qiskit/algorithms/time_evolvers/real_time_evolver.py +++ /dev/null @@ -1,37 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Interface for Quantum Real Time Evolution.""" - -from abc import ABC, abstractmethod - -from .time_evolution_problem import TimeEvolutionProblem -from .time_evolution_result import TimeEvolutionResult - - -class RealTimeEvolver(ABC): - """Interface for Quantum Real Time Evolution.""" - - @abstractmethod - def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult: - r"""Perform real time evolution :math:`\exp(-i t H)|\Psi\rangle`. - - Evolves an initial state :math:`|\Psi\rangle` for a time :math:`t` - under a Hamiltonian :math:`H`, as provided in the ``evolution_problem``. - - Args: - evolution_problem: The definition of the evolution problem. - - Returns: - Evolution result which includes an evolved quantum state. - """ - raise NotImplementedError() diff --git a/qiskit/algorithms/time_evolvers/time_evolution_problem.py b/qiskit/algorithms/time_evolvers/time_evolution_problem.py deleted file mode 100644 index 87159558baf5..000000000000 --- a/qiskit/algorithms/time_evolvers/time_evolution_problem.py +++ /dev/null @@ -1,114 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Time evolution problem class.""" -from __future__ import annotations - -from collections.abc import Mapping - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter, ParameterExpression -from qiskit.opflow import PauliSumOp -from ..list_or_dict import ListOrDict -from ...quantum_info import Statevector -from ...quantum_info.operators.base_operator import BaseOperator - - -class TimeEvolutionProblem: - """Time evolution problem class. - - This class is the input to time evolution algorithms and must contain information on the total - evolution time, a quantum state to be evolved and under which Hamiltonian the state is evolved. - - Attributes: - hamiltonian (BaseOperator | PauliSumOp): The Hamiltonian under which to evolve the system. - initial_state (QuantumCircuit | Statevector | None): The quantum state to be evolved for - methods like Trotterization. For variational time evolutions, where the evolution - happens in an ansatz, this argument is not required. - aux_operators (ListOrDict[BaseOperator | PauliSumOp] | None): Optional list of auxiliary - operators to be evaluated with the evolved ``initial_state`` and their expectation - values returned. - truncation_threshold (float): Defines a threshold under which values can be assumed to be 0. - Used when ``aux_operators`` is provided. - t_param (Parameter | None): Time parameter in case of a time-dependent Hamiltonian. This - free parameter must be within the ``hamiltonian``. - param_value_map (dict[Parameter, complex] | None): Maps free parameters in the problem to - values. Depending on the algorithm, it might refer to e.g. a Hamiltonian or an initial - state. - """ - - def __init__( - self, - hamiltonian: BaseOperator | PauliSumOp, - time: float, - initial_state: QuantumCircuit | Statevector | None = None, - aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None, - truncation_threshold: float = 1e-12, - t_param: Parameter | None = None, - param_value_map: Mapping[Parameter, complex] | None = None, - ): - """ - Args: - hamiltonian: The Hamiltonian under which to evolve the system. - time: Total time of evolution. - initial_state: The quantum state to be evolved for methods like Trotterization. - For variational time evolutions, where the evolution happens in an ansatz, - this argument is not required. - aux_operators: Optional list of auxiliary operators to be evaluated with the - evolved ``initial_state`` and their expectation values returned. - truncation_threshold: Defines a threshold under which values can be assumed to be 0. - Used when ``aux_operators`` is provided. - t_param: Time parameter in case of a time-dependent Hamiltonian. This - free parameter must be within the ``hamiltonian``. - param_value_map: Maps free parameters in the problem to values. Depending on the - algorithm, it might refer to e.g. a Hamiltonian or an initial state. - - Raises: - ValueError: If non-positive time of evolution is provided. - """ - - self.t_param = t_param - self.param_value_map = param_value_map - self.hamiltonian = hamiltonian - self.time = time - if isinstance(initial_state, Statevector): - circuit = QuantumCircuit(initial_state.num_qubits) - circuit.prepare_state(initial_state.data) - initial_state = circuit - self.initial_state: QuantumCircuit | None = initial_state - self.aux_operators = aux_operators - self.truncation_threshold = truncation_threshold - - @property - def time(self) -> float: - """Returns time.""" - return self._time - - @time.setter - def time(self, time: float) -> None: - """ - Sets time and validates it. - """ - self._time = time - - def validate_params(self) -> None: - """ - Checks if all parameters present in the Hamiltonian are also present in the dictionary - that maps them to values. - - Raises: - ValueError: If Hamiltonian parameters cannot be bound with data provided. - """ - if isinstance(self.hamiltonian, PauliSumOp) and isinstance( - self.hamiltonian.coeff, ParameterExpression - ): - raise ValueError("A global parametrized coefficient for PauliSumOp is not allowed.") diff --git a/qiskit/algorithms/time_evolvers/time_evolution_result.py b/qiskit/algorithms/time_evolvers/time_evolution_result.py deleted file mode 100644 index 8741367f681f..000000000000 --- a/qiskit/algorithms/time_evolvers/time_evolution_result.py +++ /dev/null @@ -1,60 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for holding time evolution result.""" -from __future__ import annotations -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.quantum_info import Statevector -from qiskit.algorithms.list_or_dict import ListOrDict -from ..algorithm_result import AlgorithmResult - - -class TimeEvolutionResult(AlgorithmResult): - """ - Class for holding time evolution result. - - Attributes: - evolved_state (QuantumCircuit|Statevector): An evolved quantum state. - aux_ops_evaluated (ListOrDict[tuple[complex, complex]] | None): Optional list of - observables for which expected values on an evolved state are calculated. These values - are in fact tuples formatted as (mean, standard deviation). - observables (ListOrDict[tuple[np.ndarray, np.ndarray]] | None): Optional list of - observables for which expected on an evolved state are calculated at each timestep. - These values are in fact lists of tuples formatted as (mean, standard deviation). - times (np.array | None): Optional list of times at which each observable has been evaluated. - """ - - def __init__( - self, - evolved_state: QuantumCircuit | Statevector, - aux_ops_evaluated: ListOrDict[tuple[complex, complex]] | None = None, - observables: ListOrDict[tuple[np.ndarray, np.ndarray]] | None = None, - times: np.ndarray | None = None, - ): - """ - Args: - evolved_state: An evolved quantum state. - aux_ops_evaluated: Optional list of observables for which expected values on an evolved - state are calculated. These values are in fact tuples formatted as (mean, standard - deviation). - observables: Optional list of observables for which expected values are calculated for - each timestep. These values are in fact tuples formatted as (mean array, standard - deviation array). - times: Optional list of times at which each observable has been evaluated. - """ - - self.evolved_state = evolved_state - self.aux_ops_evaluated = aux_ops_evaluated - self.observables = observables - self.times = times diff --git a/qiskit/algorithms/time_evolvers/trotterization/__init__.py b/qiskit/algorithms/time_evolvers/trotterization/__init__.py deleted file mode 100644 index c5e7e128728d..000000000000 --- a/qiskit/algorithms/time_evolvers/trotterization/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""This package contains Trotterization-based Quantum Real Time Evolution algorithm. -It is compliant with the new Quantum Time Evolution Framework and makes use of -:class:`qiskit.synthesis.evolution.ProductFormula` and -:class:`~qiskit.circuit.library.PauliEvolutionGate` implementations. - -Trotterization-based Quantum Real Time Evolution ------------------------------------------------- - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - TrotterQRTE -""" - -from qiskit.algorithms.time_evolvers.trotterization.trotter_qrte import TrotterQRTE - -__all__ = ["TrotterQRTE"] diff --git a/qiskit/algorithms/time_evolvers/trotterization/trotter_qrte.py b/qiskit/algorithms/time_evolvers/trotterization/trotter_qrte.py deleted file mode 100644 index cb43e297aed2..000000000000 --- a/qiskit/algorithms/time_evolvers/trotterization/trotter_qrte.py +++ /dev/null @@ -1,246 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""An algorithm to implement a Trotterization real time-evolution.""" - -from __future__ import annotations - -from qiskit import QuantumCircuit -from qiskit.algorithms.time_evolvers.time_evolution_problem import TimeEvolutionProblem -from qiskit.algorithms.time_evolvers.time_evolution_result import TimeEvolutionResult -from qiskit.algorithms.time_evolvers.real_time_evolver import RealTimeEvolver -from qiskit.algorithms.observables_evaluator import estimate_observables -from qiskit.opflow import PauliSumOp -from qiskit.circuit.library import PauliEvolutionGate -from qiskit.circuit.parametertable import ParameterView -from qiskit.primitives import BaseEstimator -from qiskit.quantum_info import Pauli, SparsePauliOp -from qiskit.synthesis import ProductFormula, LieTrotter - - -class TrotterQRTE(RealTimeEvolver): - """Quantum Real Time Evolution using Trotterization. - Type of Trotterization is defined by a ``ProductFormula`` provided. - - Examples: - - .. code-block:: python - - from qiskit.opflow import PauliSumOp - from qiskit.quantum_info import Pauli, SparsePauliOp - from qiskit import QuantumCircuit - from qiskit.algorithms import TimeEvolutionProblem - from qiskit.algorithms.time_evolvers import TrotterQRTE - from qiskit.primitives import Estimator - - operator = PauliSumOp(SparsePauliOp([Pauli("X"), Pauli("Z")])) - initial_state = QuantumCircuit(1) - time = 1 - evolution_problem = TimeEvolutionProblem(operator, time, initial_state) - # LieTrotter with 1 rep - estimator = Estimator() - trotter_qrte = TrotterQRTE(estimator=estimator) - evolved_state = trotter_qrte.evolve(evolution_problem).evolved_state - """ - - def __init__( - self, - product_formula: ProductFormula | None = None, - estimator: BaseEstimator | None = None, - num_timesteps: int = 1, - ) -> None: - """ - Args: - product_formula: A Lie-Trotter-Suzuki product formula. If ``None`` provided, the - Lie-Trotter first order product formula with a single repetition is used. ``reps`` - should be 1 to obtain a number of time-steps equal to ``num_timesteps`` and an - evaluation of :attr:`.TimeEvolutionProblem.aux_operators` at every time-step. If ``reps`` - is larger than 1, the true number of time-steps will be ``num_timesteps * reps``. - num_timesteps: The number of time-steps the full evolution time is devided into - (repetitions of ``product_formula``) - estimator: An estimator primitive used for calculating expectation values of - ``TimeEvolutionProblem.aux_operators``. - """ - - self.product_formula = product_formula - self.num_timesteps = num_timesteps - self.estimator = estimator - - @property - def product_formula(self) -> ProductFormula: - """Returns a product formula.""" - return self._product_formula - - @product_formula.setter - def product_formula(self, product_formula: ProductFormula | None): - """Sets a product formula. If ``None`` provided, sets the Lie-Trotter first order product - formula with a single repetition.""" - if product_formula is None: - product_formula = LieTrotter() - self._product_formula = product_formula - - @property - def estimator(self) -> BaseEstimator | None: - """ - Returns an estimator. - """ - return self._estimator - - @estimator.setter - def estimator(self, estimator: BaseEstimator) -> None: - """ - Sets an estimator. - """ - self._estimator = estimator - - @property - def num_timesteps(self) -> int: - """Returns the number of timesteps.""" - return self._num_timesteps - - @num_timesteps.setter - def num_timesteps(self, num_timesteps: int) -> None: - """ - Sets the number of time-steps. - - Raises: - ValueError: If num_timesteps is not positive. - """ - if num_timesteps <= 0: - raise ValueError( - f"Number of time steps must be positive integer, {num_timesteps} provided" - ) - self._num_timesteps = num_timesteps - - @classmethod - def supports_aux_operators(cls) -> bool: - """ - Whether computing the expectation value of auxiliary operators is supported. - - Returns: - ``True`` if ``aux_operators`` expectations in the ``TimeEvolutionProblem`` can be - evaluated, ``False`` otherwise. - """ - return True - - def evolve(self, evolution_problem: TimeEvolutionProblem) -> TimeEvolutionResult: - """ - Evolves a quantum state for a given time using the Trotterization method - based on a product formula provided. The result is provided in the form of a quantum - circuit. If auxiliary operators are included in the ``evolution_problem``, they are - evaluated on the ``init_state`` and on the evolved state at every step (``num_timesteps`` - times) using an estimator primitive provided. - - Args: - evolution_problem: Instance defining evolution problem. For the included Hamiltonian, - ``Pauli`` or ``PauliSumOp`` are supported by TrotterQRTE. - - Returns: - Evolution result that includes an evolved state as a quantum circuit and, optionally, - auxiliary operators evaluated for a resulting state on an estimator primitive. - - Raises: - ValueError: If ``t_param`` is not set to ``None`` in the ``TimeEvolutionProblem`` - (feature not currently supported). - ValueError: If ``aux_operators`` provided in the time evolution problem but no estimator - provided to the algorithm. - ValueError: If the ``initial_state`` is not provided in the ``TimeEvolutionProblem``. - ValueError: If an unsupported Hamiltonian type is provided. - """ - evolution_problem.validate_params() - - if evolution_problem.aux_operators is not None and self.estimator is None: - raise ValueError( - "The time evolution problem contained ``aux_operators`` but no estimator was " - "provided. The algorithm continues without calculating these quantities. " - ) - - # ensure the hamiltonian is a sparse pauli op - hamiltonian = evolution_problem.hamiltonian - if not isinstance(hamiltonian, (Pauli, PauliSumOp, SparsePauliOp)): - raise ValueError( - f"TrotterQRTE only accepts Pauli | PauliSumOp | SparsePauliOp, {type(hamiltonian)} " - "provided." - ) - if isinstance(hamiltonian, PauliSumOp): - hamiltonian = hamiltonian.primitive * hamiltonian.coeff - elif isinstance(hamiltonian, Pauli): - hamiltonian = SparsePauliOp(hamiltonian) - - t_param = evolution_problem.t_param - free_parameters = hamiltonian.parameters - if t_param is not None and free_parameters != ParameterView([t_param]): - raise ValueError( - f"Hamiltonian time parameters ({free_parameters}) do not match " - f"evolution_problem.t_param ({t_param})." - ) - - # make sure PauliEvolutionGate does not implement more than one Trotter step - dt = evolution_problem.time / self.num_timesteps - - if evolution_problem.initial_state is not None: - initial_state = evolution_problem.initial_state - else: - raise ValueError("``initial_state`` must be provided in the ``TimeEvolutionProblem``.") - - evolved_state = QuantumCircuit(initial_state.num_qubits) - evolved_state.append(initial_state, evolved_state.qubits) - - if evolution_problem.aux_operators is not None: - observables = [] - observables.append( - estimate_observables( - self.estimator, - evolved_state, - evolution_problem.aux_operators, - None, - evolution_problem.truncation_threshold, - ) - ) - else: - observables = None - - if t_param is None: - # the evolution gate - single_step_evolution_gate = PauliEvolutionGate( - hamiltonian, dt, synthesis=self.product_formula - ) - - for n in range(self.num_timesteps): - # if hamiltonian is time-dependent, bind new time-value at every step to construct - # evolution for next step - if t_param is not None: - time_value = (n + 1) * dt - bound_hamiltonian = hamiltonian.assign_parameters([time_value]) - single_step_evolution_gate = PauliEvolutionGate( - bound_hamiltonian, - dt, - synthesis=self.product_formula, - ) - evolved_state.append(single_step_evolution_gate, evolved_state.qubits) - - if evolution_problem.aux_operators is not None: - observables.append( - estimate_observables( - self.estimator, - evolved_state, - evolution_problem.aux_operators, - None, - evolution_problem.truncation_threshold, - ) - ) - - evaluated_aux_ops = None - if evolution_problem.aux_operators is not None: - evaluated_aux_ops = observables[-1] - - return TimeEvolutionResult(evolved_state, evaluated_aux_ops, observables) diff --git a/qiskit/algorithms/time_evolvers/variational/__init__.py b/qiskit/algorithms/time_evolvers/variational/__init__.py deleted file mode 100644 index ae22bca5adea..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/__init__.py +++ /dev/null @@ -1,117 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Variational Quantum Time Evolutions (:mod:`qiskit.algorithms.time_evolvers.variational`) -======================================================================================== - -Algorithms for performing Variational Quantum Time Evolution of quantum states, -which can be tailored to near-term devices. -:class:`~qiskit.algorithms.time_evolvers.variational.VarQTE` base class exposes an interface, compliant -with the Quantum Time Evolution Framework in Qiskit Terra, that is implemented by -:class:`~qiskit.algorithms.VarQRTE` and :class:`~qiskit.algorithms.VarQITE` classes for real and -imaginary time evolution respectively. The variational approach is taken according to a variational -principle chosen by a user. - -Example: - - .. code-block:: python - - import numpy as np - - from qiskit.algorithms import TimeEvolutionProblem, VarQITE - from qiskit.algorithms.time_evolvers.variational import ImaginaryMcLachlanPrinciple - from qiskit.circuit.library import EfficientSU2 - from qiskit.quantum_info import SparsePauliOp - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - ansatz = EfficientSU2(observable.num_qubits, reps=1) - init_param_values = np.zeros(len(ansatz.parameters)) - for i in range(len(ansatz.parameters)): - init_param_values[i] = np.pi / 2 - var_principle = ImaginaryMcLachlanPrinciple() - time = 1 - evolution_problem = TimeEvolutionProblem(observable, time) - var_qite = VarQITE(ansatz, var_principle, init_param_values) - evolution_result = var_qite.evolve(evolution_problem) - -.. currentmodule:: qiskit.algorithms.time_evolvers.variational - -Variational Principles ----------------------- - -With variational principles we can project time evolution of a quantum state -onto the parameters of a model, in our case a variational quantum circuit. - -They can be divided into two categories: Variational Quantum _Real_ Time Evolution, which evolves -the variational ansatz under the standard Schroediger equation and -Variational Quantum _Imaginary_ Time Evolution, which evolves under the normalized -Wick-rotated Schroedinger equation. - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - VariationalPrinciple - RealVariationalPrinciple - ImaginaryVariationalPrinciple - RealMcLachlanPrinciple - ImaginaryMcLachlanPrinciple - -ODE solvers ------------ -ODE solvers that implement the SciPy ODE Solver interface. The Forward Euler Solver is -a preferred choice in the presence of noise. One might also use solvers provided by SciPy directly, -e.g. RK45. - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - ForwardEulerSolver - -""" -from .solvers.ode.forward_euler_solver import ForwardEulerSolver -from .var_qrte import VarQRTE -from .var_qite import VarQITE - -from .var_qte import VarQTE -from .var_qte_result import VarQTEResult -from .variational_principles import ( - VariationalPrinciple, - RealVariationalPrinciple, - ImaginaryVariationalPrinciple, - ImaginaryMcLachlanPrinciple, - RealMcLachlanPrinciple, -) - -__all__ = [ - "ForwardEulerSolver", - "VarQTE", - "VarQTEResult", - "VariationalPrinciple", - "RealVariationalPrinciple", - "ImaginaryVariationalPrinciple", - "RealMcLachlanPrinciple", - "ImaginaryMcLachlanPrinciple", - "VarQITE", - "VarQRTE", -] diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/__init__.py b/qiskit/algorithms/time_evolvers/variational/solvers/__init__.py deleted file mode 100644 index b4537b41e839..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/solvers/__init__.py +++ /dev/null @@ -1,48 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Solvers (:mod:`qiskit.algorithms.time_evolvers.variational.solvers`) -==================================================================== - -This package contains the necessary classes to solve systems of equations arising in the -Variational Quantum Time Evolution. They include ordinary differential equations (ODE) which -describe ansatz parameter propagation and systems of linear equations. - - -Systems of Linear Equations Solver ----------------------------------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - VarQTELinearSolver - - -ODE Solver ----------- -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - VarQTEOdeSolver -""" - -from qiskit.algorithms.time_evolvers.variational.solvers.ode.var_qte_ode_solver import ( - VarQTEOdeSolver, -) -from qiskit.algorithms.time_evolvers.variational.solvers.var_qte_linear_solver import ( - VarQTELinearSolver, -) - -__all__ = ["VarQTELinearSolver", "VarQTEOdeSolver"] diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/abstract_ode_function.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/abstract_ode_function.py deleted file mode 100644 index b94ded552a81..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/abstract_ode_function.py +++ /dev/null @@ -1,51 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Abstract class for generating ODE functions.""" -from __future__ import annotations - -from abc import ABC, abstractmethod -from collections.abc import Mapping, Iterable - -from qiskit.circuit import Parameter - -from ..var_qte_linear_solver import VarQTELinearSolver - - -class AbstractOdeFunction(ABC): - """Abstract class for generating ODE functions.""" - - def __init__( - self, - varqte_linear_solver: VarQTELinearSolver, - param_dict: Mapping[Parameter, float], - t_param: Parameter | None = None, - ) -> None: - - self._varqte_linear_solver = varqte_linear_solver - self._param_dict = param_dict - self._t_param = t_param - - @abstractmethod - def var_qte_ode_function(self, time: float, parameter_values: Iterable) -> Iterable: - """ - Evaluates an ODE function for a given time and parameter values. It is used by an ODE - solver. - - Args: - time: Current time of evolution. - parameter_values: Current values of parameters. - - Returns: - ODE gradient arising from solving a system of linear equations. - """ - pass diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/forward_euler_solver.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/forward_euler_solver.py deleted file mode 100644 index d48ee5b6c4e1..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/forward_euler_solver.py +++ /dev/null @@ -1,72 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Forward Euler ODE solver.""" -from collections.abc import Callable, Sequence - -import numpy as np -from scipy.integrate import OdeSolver -from scipy.integrate._ivp.base import ConstantDenseOutput - - -class ForwardEulerSolver(OdeSolver): - """Forward Euler ODE solver.""" - - def __init__( - self, - function: Callable, - t0: float, - y0: Sequence, - t_bound: float, - vectorized: bool = False, - support_complex: bool = False, - num_t_steps: int = 15, - ): - """ - Forward Euler ODE solver that implements an interface from SciPy. - - Args: - function: Right-hand side of the system. The calling signature is ``fun(t, y)``. Here - ``t`` is a scalar, and there are two options for the ndarray ``y``: - It can either have shape (n,); then ``fun`` must return array_like with - shape (n,). Alternatively it can have shape (n, k); then ``fun`` - must return an array_like with shape (n, k), i.e., each column - corresponds to a single column in ``y``. The choice between the two - options is determined by `vectorized` argument (see below). The - vectorized implementation allows a faster approximation of the Jacobian - by finite differences (required for this solver). - t0: Initial time. - y0: Initial state. - t_bound: Boundary time - the integration won't continue beyond it. It also determines - the direction of the integration. - vectorized: Whether ``fun`` is implemented in a vectorized fashion. Default is False. - support_complex: Whether integration in a complex domain should be supported. - Generally determined by a derived solver class capabilities. Default is False. - num_t_steps: Number of time steps for the forward Euler method. - """ - self._y_old = None - self._step_length = (t_bound - t0) / num_t_steps - super().__init__(function, t0, y0, t_bound, vectorized, support_complex) - - def _step_impl(self): - """ - Takes an Euler step. - """ - try: - self._y_old = self.y - self.y = list(np.add(self.y, self._step_length * self.fun(self.t, self.y))) - self.t += self._step_length - return True, None - except Exception as ex: # pylint: disable=broad-except - return False, f"Unknown ODE solver error: {str(ex)}." - - def _dense_output_impl(self): - return ConstantDenseOutput(self.t_old, self.t, self._y_old) diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function.py deleted file mode 100644 index a7d8453c29b8..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function.py +++ /dev/null @@ -1,41 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for generating ODE functions based on ODE gradients.""" -from collections.abc import Iterable - -from .abstract_ode_function import AbstractOdeFunction - - -class OdeFunction(AbstractOdeFunction): - """Class for generating ODE functions based on ODE gradients.""" - - def var_qte_ode_function(self, time: float, parameter_values: Iterable) -> Iterable: - """ - Evaluates an ODE function for a given time and parameter values. It is used by an ODE - solver. - - Args: - time: Current time of evolution. - parameter_values: Current values of parameters. - - Returns: - ODE gradient arising from solving a system of linear equations. - """ - current_param_dict = dict(zip(self._param_dict.keys(), parameter_values)) - - ode_grad_res, _, _ = self._varqte_linear_solver.solve_lse( - current_param_dict, - time, - ) - - return ode_grad_res diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function_factory.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function_factory.py deleted file mode 100644 index 0d094c4b7950..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/ode_function_factory.py +++ /dev/null @@ -1,72 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Abstract class for generating ODE functions.""" -from __future__ import annotations - -from abc import ABC -from collections.abc import Mapping -from enum import Enum - -from qiskit.circuit import Parameter - -from .abstract_ode_function import AbstractOdeFunction -from .ode_function import OdeFunction - -from ..var_qte_linear_solver import VarQTELinearSolver - - -class OdeFunctionType(Enum): - """Types of ODE functions for VatQTE algorithms.""" - - # Other types may be supported in the future - STANDARD_ODE = "STANDARD_ODE" - - -class OdeFunctionFactory(ABC): - """Factory for building ODE functions.""" - - def __init__(self, ode_function_type: OdeFunctionType = OdeFunctionType.STANDARD_ODE) -> None: - """ - Args: - ode_function_type: An Enum that defines a type of an ODE function to be built. If - not provided, a default ``STANDARD_ODE`` is used. - """ - self._ode_function_type = ode_function_type - - def _build( - self, - varqte_linear_solver: VarQTELinearSolver, - param_dict: Mapping[Parameter, float], - t_param: Parameter | None = None, - ) -> AbstractOdeFunction: - """ - Initializes an ODE function specified in the class. - - Args: - varqte_linear_solver: Solver of LSE for the VarQTE algorithm. - param_dict: Dictionary which relates parameter values to the parameters in the ansatz. - t_param: Time parameter in case of a time-dependent Hamiltonian. - - Returns: - An ODE function. - - Raises: - ValueError: If unsupported ODE function provided. - - """ - if self._ode_function_type == OdeFunctionType.STANDARD_ODE: - return OdeFunction(varqte_linear_solver, param_dict, t_param) - raise ValueError( - f"Unsupported ODE function provided: {self._ode_function_type}." - f" Only {[tp.value for tp in OdeFunctionType]} are supported." - ) diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/var_qte_ode_solver.py b/qiskit/algorithms/time_evolvers/variational/solvers/ode/var_qte_ode_solver.py deleted file mode 100644 index aad1d96155ce..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/var_qte_ode_solver.py +++ /dev/null @@ -1,89 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for solving ODEs for Quantum Time Evolution.""" -from __future__ import annotations - -from collections.abc import Sequence -from functools import partial -from typing import Type - -import numpy as np -from scipy.integrate import OdeSolver, solve_ivp - -from .abstract_ode_function import AbstractOdeFunction -from .forward_euler_solver import ForwardEulerSolver - - -class VarQTEOdeSolver: - """Class for solving ODEs for Quantum Time Evolution.""" - - def __init__( - self, - init_params: Sequence[float], - ode_function: AbstractOdeFunction, - ode_solver: Type[OdeSolver] | str = ForwardEulerSolver, - num_timesteps: int | None = None, - ) -> None: - """ - Initialize ODE Solver. - - Args: - init_params: Set of initial parameters for time 0. - ode_function: Generates the ODE function. - ode_solver: ODE solver callable that implements a SciPy ``OdeSolver`` interface or a - string indicating a valid method offered by SciPy. - num_timesteps: The number of timesteps to take. If None, it is - automatically selected to achieve a timestep of approximately 0.01. Only - relevant in case of the ``ForwardEulerSolver``. - """ - self._init_params = init_params - self._ode_function = ode_function.var_qte_ode_function - self._ode_solver = ode_solver - self._num_timesteps = num_timesteps - - def run( - self, evolution_time: float - ) -> tuple[Sequence[float], Sequence[Sequence[float]], Sequence[float]]: - """ - Finds numerical solution with ODE Solver. - - Args: - evolution_time: Evolution time. - - Returns: - List of parameters found by an ODE solver for a given ODE function callable. - """ - # determine the number of timesteps and set the timestep - num_timesteps = ( - int(np.ceil(evolution_time / 0.01)) - if self._num_timesteps is None - else self._num_timesteps - ) - - if self._ode_solver == ForwardEulerSolver: - solve = partial(solve_ivp, num_t_steps=num_timesteps) - else: - solve = solve_ivp - - sol = solve( - self._ode_function, - (0, evolution_time), - self._init_params, - method=self._ode_solver, - ) - - param_vals = sol.y.T - time_points = sol.t - final_param_vals = param_vals[-1] - - return final_param_vals, param_vals, time_points diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py b/qiskit/algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py deleted file mode 100644 index ec06fba0a685..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/solvers/var_qte_linear_solver.py +++ /dev/null @@ -1,129 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for solving linear equations for Quantum Time Evolution.""" -from __future__ import annotations - -from collections.abc import Mapping, Sequence, Callable - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.quantum_info import SparsePauliOp -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ..variational_principles import VariationalPrinciple - - -class VarQTELinearSolver: - """Class for solving linear equations for Quantum Time Evolution.""" - - def __init__( - self, - var_principle: VariationalPrinciple, - hamiltonian: BaseOperator, - ansatz: QuantumCircuit, - gradient_params: Sequence[Parameter] | None = None, - t_param: Parameter | None = None, - lse_solver: Callable[[np.ndarray, np.ndarray], np.ndarray] | None = None, - imag_part_tol: float = 1e-7, - ) -> None: - """ - Args: - var_principle: Variational Principle to be used. - hamiltonian: Operator used for Variational Quantum Time Evolution. - ansatz: Quantum state in the form of a parametrized quantum circuit. - gradient_params: List of parameters with respect to which gradients should be computed. - If ``None`` given, gradients w.r.t. all parameters will be computed. - t_param: Time parameter in case of a time-dependent Hamiltonian. - lse_solver: Linear system of equations solver callable. It accepts ``A`` and ``b`` to - solve ``Ax=b`` and returns ``x``. If ``None``, the default ``np.linalg.lstsq`` - solver is used. - imag_part_tol: Allowed value of an imaginary part that can be neglected if no - imaginary part is expected. - - Raises: - TypeError: If t_param is provided and Hamiltonian is not of type SparsePauliOp. - """ - self._var_principle = var_principle - self._hamiltonian = hamiltonian - self._ansatz = ansatz - self._gradient_params = gradient_params - self._bind_params = gradient_params - self._time_param = t_param - self.lse_solver = lse_solver - self._imag_part_tol = imag_part_tol - - if self._time_param is not None and not isinstance(self._hamiltonian, SparsePauliOp): - raise TypeError( - f"A time parameter {t_param} has been specified, so a time-dependent " - f"hamiltonian is expected. The operator provided is of type {type(self._hamiltonian)}, " - f"which might not support parametrization. " - f"Please provide the parametrized hamiltonian as a SparsePauliOp." - ) - - @property - def lse_solver(self) -> Callable[[np.ndarray, np.ndarray], np.ndarray]: - """Returns an LSE solver callable.""" - return self._lse_solver - - @lse_solver.setter - def lse_solver(self, lse_solver: Callable[[np.ndarray, np.ndarray], np.ndarray] | None) -> None: - """Sets an LSE solver. Uses a ``np.linalg.lstsq`` callable if ``None`` provided.""" - if lse_solver is None: - lse_solver = lambda a, b: np.linalg.lstsq(a, b, rcond=1e-2)[0] - - self._lse_solver = lse_solver - - def solve_lse( - self, - param_dict: Mapping[Parameter, float], - time_value: float | None = None, - ) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - """ - Solve the system of linear equations underlying McLachlan's variational principle for the - calculation without error bounds. - - Args: - param_dict: Dictionary which relates parameter values to the parameters in the ansatz. - time_value: Time value that will be bound to ``t_param``. It is required if ``t_param`` - is not ``None``. - - Returns: - Solution to the LSE, A from Ax=b, b from Ax=b. - - Raises: - ValueError: If no time value is provided for time dependent hamiltonians. - - """ - param_values = list(param_dict.values()) - metric_tensor_lse_lhs = self._var_principle.metric_tensor(self._ansatz, param_values) - hamiltonian = self._hamiltonian - - if self._time_param is not None: - if time_value is not None: - hamiltonian = hamiltonian.assign_parameters([time_value]) - else: - raise ValueError( - "Providing a time_value is required for time-dependent hamiltonians, " - f"but got time_value = {time_value}. " - "Please provide a time_value to the solve_lse method." - ) - - evolution_grad_lse_rhs = self._var_principle.evolution_gradient( - hamiltonian, self._ansatz, param_values, self._gradient_params - ) - - x = self._lse_solver(metric_tensor_lse_lhs, evolution_grad_lse_rhs) - - return np.real(x), metric_tensor_lse_lhs, evolution_grad_lse_rhs diff --git a/qiskit/algorithms/time_evolvers/variational/var_qite.py b/qiskit/algorithms/time_evolvers/variational/var_qite.py deleted file mode 100644 index 4200389c83cf..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/var_qite.py +++ /dev/null @@ -1,120 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Variational Quantum Imaginary Time Evolution algorithm.""" -from __future__ import annotations - -from collections.abc import Mapping, Sequence -from typing import Type, Callable - -import numpy as np -from scipy.integrate import OdeSolver - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.primitives import BaseEstimator - -from .solvers.ode.forward_euler_solver import ForwardEulerSolver - -from .variational_principles import ImaginaryVariationalPrinciple, ImaginaryMcLachlanPrinciple -from .var_qte import VarQTE - -from ..imaginary_time_evolver import ImaginaryTimeEvolver - - -class VarQITE(VarQTE, ImaginaryTimeEvolver): - """Variational Quantum Imaginary Time Evolution algorithm. - - .. code-block::python - - import numpy as np - - from qiskit.algorithms import TimeEvolutionProblem, VarQITE - from qiskit.algorithms.time_evolvers.variational import ImaginaryMcLachlanPrinciple - from qiskit.circuit.library import EfficientSU2 - from qiskit.quantum_info import SparsePauliOp, Pauli - from qiskit.primitives import Estimator - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - ansatz = EfficientSU2(observable.num_qubits, reps=1) - init_param_values = np.ones(len(ansatz.parameters)) * np.pi/2 - var_principle = ImaginaryMcLachlanPrinciple() - time = 1 - - # without evaluating auxiliary operators - evolution_problem = TimeEvolutionProblem(observable, time) - var_qite = VarQITE(ansatz, init_param_values, var_principle) - evolution_result = var_qite.evolve(evolution_problem) - - # evaluating auxiliary operators - aux_ops = [Pauli("XX"), Pauli("YZ")] - evolution_problem = TimeEvolutionProblem(observable, time, aux_operators=aux_ops) - var_qite = VarQITE(ansatz, init_param_values, var_principle, Estimator()) - evolution_result = var_qite.evolve(evolution_problem) - """ - - def __init__( - self, - ansatz: QuantumCircuit, - initial_parameters: Mapping[Parameter, float] | Sequence[float], - variational_principle: ImaginaryVariationalPrinciple | None = None, - estimator: BaseEstimator | None = None, - ode_solver: Type[OdeSolver] | str = ForwardEulerSolver, - lse_solver: Callable[[np.ndarray, np.ndarray], np.ndarray] | None = None, - num_timesteps: int | None = None, - imag_part_tol: float = 1e-7, - num_instability_tol: float = 1e-7, - ) -> None: - r""" - Args: - ansatz: Ansatz to be used for variational time evolution. - initial_parameters: Initial parameter values for the ansatz. - variational_principle: Variational Principle to be used. Defaults to - ``ImaginaryMcLachlanPrinciple``. - estimator: An estimator primitive used for calculating expectation values of - TimeEvolutionProblem.aux_operators. - ode_solver: ODE solver callable that implements a SciPy ``OdeSolver`` interface or a - string indicating a valid method offered by SciPy. - lse_solver: Linear system of equations solver callable. It accepts ``A`` and ``b`` to - solve ``Ax=b`` and returns ``x``. If ``None``, the default ``np.linalg.lstsq`` - solver is used. - num_timesteps: The number of timesteps to take. If ``None``, it is - automatically selected to achieve a timestep of approximately 0.01. Only - relevant in case of the ``ForwardEulerSolver``. - imag_part_tol: Allowed value of an imaginary part that can be neglected if no - imaginary part is expected. - num_instability_tol: The amount of negative value that is allowed to be - rounded up to 0 for quantities that are expected to be non-negative. - """ - if variational_principle is None: - variational_principle = ImaginaryMcLachlanPrinciple() - super().__init__( - ansatz, - initial_parameters, - variational_principle, - estimator, - ode_solver, - lse_solver=lse_solver, - num_timesteps=num_timesteps, - imag_part_tol=imag_part_tol, - num_instability_tol=num_instability_tol, - ) diff --git a/qiskit/algorithms/time_evolvers/variational/var_qrte.py b/qiskit/algorithms/time_evolvers/variational/var_qrte.py deleted file mode 100644 index f8305f643cb5..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/var_qrte.py +++ /dev/null @@ -1,122 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Variational Quantum Real Time Evolution algorithm.""" -from __future__ import annotations - -from collections.abc import Mapping, Sequence -from typing import Type, Callable - -import numpy as np -from scipy.integrate import OdeSolver - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.primitives import BaseEstimator - -from .solvers.ode.forward_euler_solver import ForwardEulerSolver - -from .variational_principles import RealVariationalPrinciple, RealMcLachlanPrinciple -from .var_qte import VarQTE - -from ..real_time_evolver import RealTimeEvolver - - -class VarQRTE(VarQTE, RealTimeEvolver): - """Variational Quantum Real Time Evolution algorithm. - - .. code-block::python - - import numpy as np - - from qiskit.algorithms import TimeEvolutionProblem, VarQRTE - from qiskit.circuit.library import EfficientSU2 - from qiskit.algorithms.time_evolvers.variational import RealMcLachlanPrinciple - from qiskit.quantum_info import SparsePauliOp - from qiskit.quantum_info import SparsePauliOp, Pauli - from qiskit.primitives import Estimator - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - ansatz = EfficientSU2(observable.num_qubits, reps=1) - init_param_values = np.ones(len(ansatz.parameters)) * np.pi/2 - var_principle = RealMcLachlanPrinciple() - time = 1 - - # without evaluating auxiliary operators - evolution_problem = TimeEvolutionProblem(observable, time) - var_qrte = VarQRTE(ansatz, init_param_values, var_principle) - evolution_result = var_qrte.evolve(evolution_problem) - - # evaluating auxiliary operators - aux_ops = [Pauli("XX"), Pauli("YZ")] - evolution_problem = TimeEvolutionProblem(observable, time, aux_operators=aux_ops) - var_qrte = VarQRTE(ansatz, init_param_values, var_principle, Estimator()) - evolution_result = var_qrte.evolve(evolution_problem) - """ - - def __init__( - self, - ansatz: QuantumCircuit, - initial_parameters: Mapping[Parameter, float] | Sequence[float], - variational_principle: RealVariationalPrinciple | None = None, - estimator: BaseEstimator | None = None, - ode_solver: Type[OdeSolver] | str = ForwardEulerSolver, - lse_solver: Callable[[np.ndarray, np.ndarray], np.ndarray] | None = None, - num_timesteps: int | None = None, - imag_part_tol: float = 1e-7, - num_instability_tol: float = 1e-7, - ) -> None: - r""" - Args: - ansatz: Ansatz to be used for variational time evolution. - initial_parameters: Initial parameter values for an ansatz. - variational_principle: Variational Principle to be used. Defaults to - ``RealMcLachlanPrinciple``. - estimator: An estimator primitive used for calculating expectation values of - TimeEvolutionProblem.aux_operators. - ode_solver: ODE solver callable that implements a SciPy ``OdeSolver`` interface or a - string indicating a valid method offered by SciPy. - lse_solver: Linear system of equations solver callable. It accepts ``A`` and ``b`` to - solve ``Ax=b`` and returns ``x``. If ``None``, the default ``np.linalg.lstsq`` - solver is used. - num_timesteps: The number of timesteps to take. If ``None``, it is - automatically selected to achieve a timestep of approximately 0.01. Only - relevant in case of the ``ForwardEulerSolver``. - imag_part_tol: Allowed value of an imaginary part that can be neglected if no - imaginary part is expected. - num_instability_tol: The amount of negative value that is allowed to be - rounded up to 0 for quantities that are expected to be - non-negative. - """ - if variational_principle is None: - variational_principle = RealMcLachlanPrinciple() - super().__init__( - ansatz, - initial_parameters, - variational_principle, - estimator, - ode_solver, - lse_solver=lse_solver, - num_timesteps=num_timesteps, - imag_part_tol=imag_part_tol, - num_instability_tol=num_instability_tol, - ) diff --git a/qiskit/algorithms/time_evolvers/variational/var_qte.py b/qiskit/algorithms/time_evolvers/variational/var_qte.py deleted file mode 100644 index f0d33a6b8ae5..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/var_qte.py +++ /dev/null @@ -1,290 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Variational Quantum Time Evolution Interface""" -from __future__ import annotations - -from abc import ABC -from collections.abc import Mapping, Callable, Sequence -from typing import Type - -import numpy as np -from scipy.integrate import OdeSolver - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from .solvers.ode.forward_euler_solver import ForwardEulerSolver -from .solvers.ode.ode_function_factory import OdeFunctionFactory -from .solvers.ode.var_qte_ode_solver import VarQTEOdeSolver -from .solvers.var_qte_linear_solver import VarQTELinearSolver - -from .variational_principles.variational_principle import VariationalPrinciple -from .var_qte_result import VarQTEResult - -from ..time_evolution_problem import TimeEvolutionProblem - -from ...observables_evaluator import estimate_observables - - -class VarQTE(ABC): - """Variational Quantum Time Evolution. - - Algorithms that use variational principles to compute a time evolution for a given - Hermitian operator (Hamiltonian) and a quantum state prepared by a parameterized quantum - circuit. - - Attributes: - ansatz (QuantumCircuit): Ansatz to be used for variational time evolution. - initial_parameters (Mapping[Parameter, float] | Sequence[float]): Initial - parameter values for an ansatz. - variational_principle (VariationalPrinciple): Variational Principle to be used. - estimator (BaseEstimator): An estimator primitive used for calculating expectation - values of ``TimeEvolutionProblem.aux_operators``. - ode_solver(Type[OdeSolver] | str): ODE solver callable that implements a SciPy - ``OdeSolver`` interface or a string indicating a valid method offered by SciPy. - lse_solver (Callable[[np.ndarray, np.ndarray], np.ndarray] | None): Linear system - of equations solver callable. It accepts ``A`` and ``b`` to solve ``Ax=b`` - and returns ``x``. - num_timesteps (int | None): The number of timesteps to take. If None, it is - automatically selected to achieve a timestep of approximately 0.01. Only - relevant in case of the ``ForwardEulerSolver``. - imag_part_tol (float): Allowed value of an imaginary part that can be neglected if no - imaginary part is expected. - num_instability_tol (float): The amount of negative value that is allowed to be - rounded up to 0 for quantities that are expected to be - non-negative. - References: - - [1] Benjamin, Simon C. et al. (2019). - Theory of variational quantum simulation. ``_ - """ - - def __init__( - self, - ansatz: QuantumCircuit, - initial_parameters: Mapping[Parameter, float] | Sequence[float], - variational_principle: VariationalPrinciple, - estimator: BaseEstimator, - ode_solver: Type[OdeSolver] | str = ForwardEulerSolver, - lse_solver: Callable[[np.ndarray, np.ndarray], np.ndarray] | None = None, - num_timesteps: int | None = None, - imag_part_tol: float = 1e-7, - num_instability_tol: float = 1e-7, - ) -> None: - r""" - Args: - ansatz: Ansatz to be used for variational time evolution. - initial_parameters: Initial parameter values for an ansatz. - variational_principle: Variational Principle to be used. - estimator: An estimator primitive used for calculating expectation values of - TimeEvolutionProblem.aux_operators. - ode_solver: ODE solver callable that implements a SciPy ``OdeSolver`` interface or a - string indicating a valid method offered by SciPy. - lse_solver: Linear system of equations solver callable. It accepts ``A`` and ``b`` to - solve ``Ax=b`` and returns ``x``. - num_timesteps: The number of timesteps to take. If None, it is - automatically selected to achieve a timestep of approximately 0.01. Only - relevant in case of the ``ForwardEulerSolver``. - imag_part_tol: Allowed value of an imaginary part that can be neglected if no - imaginary part is expected. - num_instability_tol: The amount of negative value that is allowed to be - rounded up to 0 for quantities that are expected to be - non-negative. - """ - super().__init__() - self.ansatz = ansatz - self.initial_parameters = initial_parameters - self.variational_principle = variational_principle - self.estimator = estimator - self.num_timesteps = num_timesteps - self.lse_solver = lse_solver - self.ode_solver = ode_solver - self.imag_part_tol = imag_part_tol - self.num_instability_tol = num_instability_tol - # OdeFunction abstraction kept for potential extensions - unclear at the moment; - # currently hidden from the user - self._ode_function_factory = OdeFunctionFactory() - - def evolve(self, evolution_problem: TimeEvolutionProblem) -> VarQTEResult: - """Apply Variational Quantum Time Evolution to the given operator. - - Args: - evolution_problem: Instance defining an evolution problem. - Returns: - Result of the evolution which includes a quantum circuit with bound parameters as an - evolved state and, if provided, observables evaluated on the evolved state. - - Raises: - ValueError: If ``initial_state`` is included in the ``evolution_problem``. - """ - self._validate_aux_ops(evolution_problem) - - if evolution_problem.initial_state is not None: - raise ValueError( - "An initial_state was provided to the TimeEvolutionProblem but this is not " - "supported by VarQTE. Please remove this state from the problem definition " - "and set VarQTE.initial_parameters with the corresponding initial parameter " - "values instead." - ) - - init_state_param_dict = self._create_init_state_param_dict( - self.initial_parameters, self.ansatz.parameters - ) - - # unwrap PauliSumOp (in the future this will be deprecated) - if isinstance(evolution_problem.hamiltonian, PauliSumOp): - hamiltonian = ( - evolution_problem.hamiltonian.primitive * evolution_problem.hamiltonian.coeff - ) - else: - hamiltonian = evolution_problem.hamiltonian - - evolved_state, param_values, time_points = self._evolve( - init_state_param_dict, - hamiltonian, - evolution_problem.time, - evolution_problem.t_param, - ) - - observables = [] - if evolution_problem.aux_operators is not None: - for values in param_values: - # cannot batch evaluation because estimate_observables - # only accepts single circuits - evol_state = self.ansatz.assign_parameters( - dict(zip(init_state_param_dict.keys(), values)) - ) - observable = estimate_observables( - self.estimator, - evol_state, - evolution_problem.aux_operators, - ) - observables.append(observable) - - # TODO: deprecate returning evaluated_aux_ops. - # As these are the observables for the last time step. - evaluated_aux_ops = observables[-1] if len(observables) > 0 else None - - return VarQTEResult( - evolved_state, evaluated_aux_ops, observables, time_points, param_values - ) - - def _evolve( - self, - init_state_param_dict: Mapping[Parameter, float], - hamiltonian: BaseOperator, - time: float, - t_param: Parameter | None = None, - ) -> tuple[QuantumCircuit | None, Sequence[Sequence[float]], Sequence[float]]: - r""" - Helper method for performing time evolution. Works both for imaginary and real case. - - Args: - init_state_param_dict: Parameter dictionary with initial values for a given - parametrized state/ansatz. - hamiltonian: Operator used for Variational Quantum Time Evolution (VarQTE). - time: Total time of evolution. - t_param: Time parameter in case of a time-dependent Hamiltonian. - - Returns: - Result of the evolution which is a quantum circuit with bound parameters as an - evolved state. - """ - - init_state_parameters = list(init_state_param_dict.keys()) - init_state_parameter_values = list(init_state_param_dict.values()) - - linear_solver = VarQTELinearSolver( - self.variational_principle, - hamiltonian, - self.ansatz, - init_state_parameters, - t_param, - self.lse_solver, - self.imag_part_tol, - ) - - # Convert the operator that holds the Hamiltonian and ansatz into a NaturalGradient operator - ode_function = self._ode_function_factory._build( - linear_solver, init_state_param_dict, t_param - ) - - ode_solver = VarQTEOdeSolver( - init_state_parameter_values, ode_function, self.ode_solver, self.num_timesteps - ) - final_param_values, param_values, time_points = ode_solver.run(time) - param_dict_from_ode = dict(zip(init_state_parameters, final_param_values)) - - return self.ansatz.assign_parameters(param_dict_from_ode), param_values, time_points - - @staticmethod - def _create_init_state_param_dict( - param_values: Mapping[Parameter, float] | Sequence[float], - init_state_parameters: Sequence[Parameter], - ) -> Mapping[Parameter, float]: - r""" - If ``param_values`` is a dictionary, it looks for parameters present in an initial state - (an ansatz) in a ``param_values``. Based on that, it creates a new dictionary containing - only parameters present in an initial state and their respective values. - If ``param_values`` is a list of values, it creates a new dictionary containing - parameters present in an initial state and their respective values. - - Args: - param_values: Dictionary which relates parameter values to the parameters or a list of - values. - init_state_parameters: Parameters present in a quantum state. - - Returns: - Dictionary that maps parameters of an initial state to some values. - - Raises: - ValueError: If the dictionary with parameter values provided does not include all - parameters present in the initial state or if the list of values provided is not the - same length as the list of parameters. - TypeError: If an unsupported type of ``param_values`` provided. - """ - if isinstance(param_values, Mapping): - init_state_parameter_values: Sequence[float] = [] - for param in init_state_parameters: - if param in param_values.keys(): - init_state_parameter_values.append(param_values[param]) - else: - raise ValueError( - f"The dictionary with parameter values provided does not " - f"include all parameters present in the initial state." - f"Parameters present in the state: {init_state_parameters}, " - f"parameters in the dictionary: " - f"{list(param_values.keys())}." - ) - elif isinstance(param_values, (Sequence, np.ndarray)): - if len(init_state_parameters) != len(param_values): - raise ValueError( - f"Initial state has {len(init_state_parameters)} parameters and the" - f" list of values has {len(param_values)} elements. They should be" - f" equal in length." - ) - init_state_parameter_values = param_values - else: - raise TypeError(f"Unsupported type of param_values provided: {type(param_values)}.") - - init_state_param_dict = dict(zip(init_state_parameters, init_state_parameter_values)) - return init_state_param_dict - - def _validate_aux_ops(self, evolution_problem: TimeEvolutionProblem) -> None: - if evolution_problem.aux_operators is not None and self.estimator is None: - raise ValueError( - "aux_operators were provided for evaluations but no ``estimator`` was provided." - ) diff --git a/qiskit/algorithms/time_evolvers/variational/var_qte_result.py b/qiskit/algorithms/time_evolvers/variational/var_qte_result.py deleted file mode 100644 index 3efb5e7c1789..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/var_qte_result.py +++ /dev/null @@ -1,56 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Result object for varQTE.""" -from __future__ import annotations - -import numpy as np - -from qiskit.circuit import QuantumCircuit - -from ..time_evolution_result import TimeEvolutionResult - -from ...list_or_dict import ListOrDict - - -class VarQTEResult(TimeEvolutionResult): - """The result object for the variational quantum time evolution algorithms. - - Attributes: - parameter_values (np.array | None): Optional list of parameter values obtained after - each evolution step. - """ - - def __init__( - self, - evolved_state: QuantumCircuit, - aux_ops_evaluated: ListOrDict[tuple[complex, complex]] | None = None, - observables: ListOrDict[tuple[np.ndarray, np.ndarray]] | None = None, - times: np.ndarray | None = None, - parameter_values: np.ndarray | None = None, - ): - """ - Args: - evolved_state: An evolved quantum state. - aux_ops_evaluated: Optional list of observables for which expected values on an evolved - state are calculated. These values are in fact tuples formatted as (mean, standard - deviation). - observables: Optional list of observables for which expected on an evolved state are - calculated at each timestep. - These values are in fact lists of tuples formatted as (mean, standard deviation). - times: Optional list of times at which each observable has been evaluated. - parameter_values: Optional list of parameter values obtained after each evolution step. - - """ - - super().__init__(evolved_state, aux_ops_evaluated, observables, times) - self.parameter_values = parameter_values diff --git a/qiskit/algorithms/time_evolvers/variational/variational_principles/__init__.py b/qiskit/algorithms/time_evolvers/variational/variational_principles/__init__.py deleted file mode 100644 index be04c03d7bcf..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/variational_principles/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Variational Principles""" - -from .variational_principle import VariationalPrinciple -from .imaginary_mc_lachlan_principle import ImaginaryMcLachlanPrinciple -from .imaginary_variational_principle import ImaginaryVariationalPrinciple -from .real_mc_lachlan_principle import RealMcLachlanPrinciple -from .real_variational_principle import RealVariationalPrinciple - -__all__ = [ - "VariationalPrinciple", - "ImaginaryMcLachlanPrinciple", - "ImaginaryVariationalPrinciple", - "RealMcLachlanPrinciple", - "RealVariationalPrinciple", -] diff --git a/qiskit/algorithms/time_evolvers/variational/variational_principles/imaginary_mc_lachlan_principle.py b/qiskit/algorithms/time_evolvers/variational/variational_principles/imaginary_mc_lachlan_principle.py deleted file mode 100644 index 09e1e03d3473..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/variational_principles/imaginary_mc_lachlan_principle.py +++ /dev/null @@ -1,128 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for an Imaginary McLachlan's Variational Principle.""" -from __future__ import annotations - -import warnings - -from collections.abc import Sequence - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.primitives import Estimator -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from .imaginary_variational_principle import ImaginaryVariationalPrinciple - -from ....exceptions import AlgorithmError -from ....gradients import ( - BaseEstimatorGradient, - BaseQGT, - DerivativeType, - LinCombQGT, - LinCombEstimatorGradient, -) - - -class ImaginaryMcLachlanPrinciple(ImaginaryVariationalPrinciple): - """Class for an Imaginary McLachlan's Variational Principle. It aims to minimize the distance - between both sides of the Wick-rotated Schrödinger equation with a quantum state given as a - parametrized trial state. The principle leads to a system of linear equations handled by a - linear solver. The imaginary variant means that we consider imaginary time dynamics. - """ - - def __init__( - self, - qgt: BaseQGT | None = None, - gradient: BaseEstimatorGradient | None = None, - ) -> None: - """ - Args: - qgt: Instance of a the GQT class used to compute the QFI. - If ``None`` provided, ``LinCombQGT`` is used. - gradient: Instance of a class used to compute the state gradient. - If ``None`` provided, ``LinCombEstimatorGradient`` is used. - - Raises: - AlgorithmError: If the gradient instance does not contain an estimator. - """ - - self._validate_grad_settings(gradient) - - if gradient is not None: - try: - estimator = gradient._estimator - except Exception as exc: - raise AlgorithmError( - "The provided gradient instance does not contain an estimator primitive." - ) from exc - else: - estimator = Estimator() - gradient = LinCombEstimatorGradient(estimator) - - if qgt is None: - qgt = LinCombQGT(estimator) - - super().__init__(qgt, gradient) - - def evolution_gradient( - self, - hamiltonian: BaseOperator, - ansatz: QuantumCircuit, - param_values: Sequence[float], - gradient_params: Sequence[Parameter] | None = None, - ) -> np.ndarray: - """ - Calculates an evolution gradient according to the rules of this variational principle. - - Args: - hamiltonian: Operator used for Variational Quantum Time Evolution. - ansatz: Quantum state in the form of a parametrized quantum circuit. - param_values: Values of parameters to be bound. - gradient_params: List of parameters with respect to which gradients should be computed. - If ``None`` given, gradients w.r.t. all parameters will be computed. - - Returns: - An evolution gradient. - - Raises: - AlgorithmError: If a gradient job fails. - """ - - try: - evolution_grad_lse_rhs = ( - self.gradient.run([ansatz], [hamiltonian], [param_values], [gradient_params]) - .result() - .gradients[0] - ) - - except Exception as exc: - raise AlgorithmError("The gradient primitive job failed!") from exc - - return -0.5 * evolution_grad_lse_rhs - - @staticmethod - def _validate_grad_settings(gradient): - if ( - gradient is not None - and hasattr(gradient, "_derivative_type") - and gradient._derivative_type != DerivativeType.REAL - ): - warnings.warn( - "A gradient instance with a setting for calculating imaginary part of " - "the gradient was provided. This variational principle requires the" - "real part. The setting to real was changed automatically." - ) - gradient._derivative_type = DerivativeType.REAL diff --git a/qiskit/algorithms/time_evolvers/variational/variational_principles/imaginary_variational_principle.py b/qiskit/algorithms/time_evolvers/variational/variational_principles/imaginary_variational_principle.py deleted file mode 100644 index 1255e52b7c65..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/variational_principles/imaginary_variational_principle.py +++ /dev/null @@ -1,22 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Abstract class for an Imaginary Variational Principle.""" - -from abc import ABC - -from .variational_principle import VariationalPrinciple - - -class ImaginaryVariationalPrinciple(VariationalPrinciple, ABC): - """Abstract class for an Imaginary Variational Principle. The imaginary variant - means that we consider imaginary time dynamics.""" diff --git a/qiskit/algorithms/time_evolvers/variational/variational_principles/real_mc_lachlan_principle.py b/qiskit/algorithms/time_evolvers/variational/variational_principles/real_mc_lachlan_principle.py deleted file mode 100644 index d7a946b8ab70..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/variational_principles/real_mc_lachlan_principle.py +++ /dev/null @@ -1,166 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for a Real McLachlan's Variational Principle.""" -from __future__ import annotations - -import warnings - -from collections.abc import Sequence - -import numpy as np -from numpy import real - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.primitives import Estimator -from qiskit.quantum_info import SparsePauliOp -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from .real_variational_principle import RealVariationalPrinciple - -from ....exceptions import AlgorithmError -from ....gradients import ( - BaseEstimatorGradient, - BaseQGT, - DerivativeType, - LinCombQGT, - LinCombEstimatorGradient, -) - - -class RealMcLachlanPrinciple(RealVariationalPrinciple): - """Class for a Real McLachlan's Variational Principle. It aims to minimize the distance - between both sides of the Schrödinger equation with a quantum state given as a parametrized - trial state. The principle leads to a system of linear equations handled by a linear solver. - The real variant means that we consider real time dynamics. - """ - - def __init__( - self, - qgt: BaseQGT | None = None, - gradient: BaseEstimatorGradient | None = None, - ) -> None: - """ - Args: - qgt: Instance of a the GQT class used to compute the QFI. - If ``None`` provided, ``LinCombQGT`` is used. - gradient: Instance of a class used to compute the state gradient. - If ``None`` provided, ``LinCombEstimatorGradient`` is used. - - Raises: - AlgorithmError: If the gradient instance does not contain an estimator. - """ - self._validate_grad_settings(gradient) - - if gradient is not None: - try: - estimator = gradient._estimator - except Exception as exc: - raise AlgorithmError( - "The provided gradient instance does not contain an estimator primitive." - ) from exc - else: - estimator = Estimator() - gradient = LinCombEstimatorGradient(estimator, derivative_type=DerivativeType.IMAG) - - if qgt is None: - qgt = LinCombQGT(estimator) - - super().__init__(qgt, gradient) - - def evolution_gradient( - self, - hamiltonian: BaseOperator, - ansatz: QuantumCircuit, - param_values: Sequence[float], - gradient_params: Sequence[Parameter] | None = None, - ) -> np.ndarray: - """ - Calculates an evolution gradient according to the rules of this variational principle. - - Args: - hamiltonian: Operator used for Variational Quantum Time Evolution. - ansatz: Quantum state in the form of a parametrized quantum circuit. - param_values: Values of parameters to be bound. - gradient_params: List of parameters with respect to which gradients should be computed. - If ``None`` given, gradients w.r.t. all parameters will be computed. - - Returns: - An evolution gradient. - - Raises: - AlgorithmError: If a gradient job fails. - """ - - try: - estimator_job = self.gradient._estimator.run([ansatz], [hamiltonian], [param_values]) - energy = estimator_job.result().values[0] - except Exception as exc: - raise AlgorithmError("The primitive job failed!") from exc - - modified_hamiltonian = self._construct_modified_hamiltonian(hamiltonian, real(energy)) - - try: - evolution_grad = ( - 0.5 - * self.gradient.run( - [ansatz], - [modified_hamiltonian], - parameters=[gradient_params], - parameter_values=[param_values], - ) - .result() - .gradients[0] - ) - except Exception as exc: - raise AlgorithmError("The gradient primitive job failed!") from exc - - # The BaseEstimatorGradient class returns the gradient of the opposite sign than we expect - # here (i.e. with a minus sign), hence the correction that cancels it to recover the - # real McLachlan's principle equations that do not have a minus sign. - evolution_grad = (-1) * evolution_grad - return evolution_grad - - @staticmethod - def _construct_modified_hamiltonian(hamiltonian: BaseOperator, energy: float) -> BaseOperator: - """ - Modifies a Hamiltonian according to the rules of this variational principle. - - Args: - hamiltonian: Operator used for Variational Quantum Time Evolution. - energy: The energy correction value. - - Returns: - A modified Hamiltonian. - """ - energy_term = SparsePauliOp.from_list( - hamiltonian.to_list() + [("I" * hamiltonian.num_qubits, -energy)] - ) - return energy_term - - @staticmethod - def _validate_grad_settings(gradient): - - if gradient is not None: - if not hasattr(gradient, "_derivative_type"): - raise ValueError( - "The gradient instance provided does not support calculating imaginary part. " - "Please choose a different gradient class." - ) - if gradient._derivative_type != DerivativeType.IMAG: - warnings.warn( - "A gradient instance with a setting for calculating real part of the" - "gradient was provided. This variational principle requires the" - "imaginary part. The setting to imaginary was changed automatically." - ) - gradient._derivative_type = DerivativeType.IMAG diff --git a/qiskit/algorithms/time_evolvers/variational/variational_principles/real_variational_principle.py b/qiskit/algorithms/time_evolvers/variational/variational_principles/real_variational_principle.py deleted file mode 100644 index a93c50675e3e..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/variational_principles/real_variational_principle.py +++ /dev/null @@ -1,22 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for a Real Variational Principle.""" - -from abc import ABC - -from .variational_principle import VariationalPrinciple - - -class RealVariationalPrinciple(VariationalPrinciple, ABC): - """Class for a Real Variational Principle. The real variant - means that we consider real time dynamics.""" diff --git a/qiskit/algorithms/time_evolvers/variational/variational_principles/variational_principle.py b/qiskit/algorithms/time_evolvers/variational/variational_principles/variational_principle.py deleted file mode 100644 index be16849155c4..000000000000 --- a/qiskit/algorithms/time_evolvers/variational/variational_principles/variational_principle.py +++ /dev/null @@ -1,98 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Class for a Variational Principle.""" -from __future__ import annotations - -from abc import ABC, abstractmethod -from collections.abc import Sequence - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.quantum_info.operators.base_operator import BaseOperator - -from ....exceptions import AlgorithmError -from ....gradients import BaseEstimatorGradient, BaseQGT, DerivativeType - - -class VariationalPrinciple(ABC): - """A Variational Principle class. It determines the time propagation of parameters in a - quantum state provided as a parametrized quantum circuit (ansatz). - - Attributes: - qgt (BaseQGT): Instance of a class used to compute the GQT. - gradient (BaseEstimatorGradient): Instance of a class used to compute the - state gradient. - """ - - def __init__( - self, - qgt: BaseQGT, - gradient: BaseEstimatorGradient, - ) -> None: - """ - Args: - qgt: Instance of a class used to compute the GQT. - gradient: Instance of a class used to compute the state gradient. - """ - self.qgt = qgt - self.gradient = gradient - - def metric_tensor( - self, ansatz: QuantumCircuit, param_values: Sequence[float] - ) -> Sequence[float]: - """ - Calculates a metric tensor according to the rules of this variational principle. - - Args: - ansatz: Quantum state in the form of a parametrized quantum circuit. - param_values: Values of parameters to be bound. - - Returns: - Metric tensor. - - Raises: - AlgorithmError: If a QFI job fails. - """ - - self.qgt.derivative_type = DerivativeType.REAL - try: - metric_tensor = self.qgt.run([ansatz], [param_values], [None]).result().qgts[0] - except Exception as exc: - - raise AlgorithmError("The QFI primitive job failed!") from exc - return metric_tensor - - @abstractmethod - def evolution_gradient( - self, - hamiltonian: BaseOperator, - ansatz: QuantumCircuit, - param_values: Sequence[float], - gradient_params: Sequence[Parameter] | None = None, - ) -> np.ndarray: - """ - Calculates an evolution gradient according to the rules of this variational principle. - - Args: - hamiltonian: Operator used for Variational Quantum Time Evolution. - ansatz: Quantum state in the form of a parametrized quantum circuit. - param_values: Values of parameters to be bound. - gradient_params: List of parameters with respect to which gradients should be computed. - If ``None`` given, gradients w.r.t. all parameters will be computed. - - Returns: - An evolution gradient. - """ - pass diff --git a/qiskit/algorithms/utils/__init__.py b/qiskit/algorithms/utils/__init__.py deleted file mode 100644 index 2b49396270c7..000000000000 --- a/qiskit/algorithms/utils/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Common Qiskit algorithms utility functions.""" - -from .validate_initial_point import validate_initial_point -from .validate_bounds import validate_bounds - -__all__ = [ - "validate_initial_point", - "validate_bounds", -] diff --git a/qiskit/algorithms/utils/set_batching.py b/qiskit/algorithms/utils/set_batching.py deleted file mode 100644 index 225f50a6fed8..000000000000 --- a/qiskit/algorithms/utils/set_batching.py +++ /dev/null @@ -1,27 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Set default batch sizes for the optimizers.""" - -from qiskit.algorithms.optimizers import Optimizer, SPSA - - -def _set_default_batchsize(optimizer: Optimizer) -> bool: - """Set the default batchsize, if None is set and return whether it was updated or not.""" - if isinstance(optimizer, SPSA): - updated = optimizer._max_evals_grouped is None - if updated: - optimizer.set_max_evals_grouped(50) - else: # we only set a batchsize for SPSA - updated = False - - return updated diff --git a/qiskit/algorithms/utils/validate_bounds.py b/qiskit/algorithms/utils/validate_bounds.py deleted file mode 100644 index 747e68f78a52..000000000000 --- a/qiskit/algorithms/utils/validate_bounds.py +++ /dev/null @@ -1,44 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Validate parameter bounds.""" - -from __future__ import annotations - -from qiskit.circuit import QuantumCircuit - - -def validate_bounds(circuit: QuantumCircuit) -> list[tuple[float | None, float | None]]: - """ - Validate the bounds provided by a quantum circuit against its number of parameters. - If no bounds are obtained, return ``None`` for all lower and upper bounds. - - Args: - circuit: A parameterized quantum circuit. - - Returns: - A list of tuples (lower_bound, upper_bound)). - - Raises: - ValueError: If the number of bounds does not the match the number of circuit parameters. - """ - if hasattr(circuit, "parameter_bounds") and circuit.parameter_bounds is not None: - bounds = circuit.parameter_bounds - if len(bounds) != circuit.num_parameters: - raise ValueError( - f"The number of bounds ({len(bounds)}) does not match the number of " - f"parameters in the circuit ({circuit.num_parameters})." - ) - else: - bounds = [(None, None)] * circuit.num_parameters - - return bounds diff --git a/qiskit/algorithms/utils/validate_initial_point.py b/qiskit/algorithms/utils/validate_initial_point.py deleted file mode 100644 index 56a7654a16e4..000000000000 --- a/qiskit/algorithms/utils/validate_initial_point.py +++ /dev/null @@ -1,72 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Validate an initial point.""" - -from __future__ import annotations - -import warnings -from collections.abc import Sequence - -import numpy as np - -from qiskit.circuit import QuantumCircuit -from qiskit.utils import algorithm_globals - - -def validate_initial_point( - point: Sequence[float] | None, circuit: QuantumCircuit -) -> Sequence[float]: - r""" - Validate a choice of initial point against a choice of circuit. If no point is provided, a - random point will be generated within certain parameter bounds. It will first look to the - circuit for these bounds. If the circuit does not specify bounds, bounds of :math:`-2\pi`, - :math:`2\pi` will be used. - - Args: - point: An initial point. - circuit: A parameterized quantum circuit. - - Returns: - A validated initial point. - - Raises: - ValueError: If the dimension of the initial point does not match the number of circuit - parameters. - """ - expected_size = circuit.num_parameters - - if point is None: - # get bounds if circuit has them set, otherwise use [-2pi, 2pi] for each parameter - bounds = getattr(circuit, "parameter_bounds", None) - if bounds is None: - bounds = [(-2 * np.pi, 2 * np.pi)] * expected_size - - # replace all Nones by [-2pi, 2pi] - lower_bounds = [] - upper_bounds = [] - for lower, upper in bounds: - lower_bounds.append(lower if lower is not None else -2 * np.pi) - upper_bounds.append(upper if upper is not None else 2 * np.pi) - - # sample from within bounds - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - point = algorithm_globals.random.uniform(lower_bounds, upper_bounds) - - elif len(point) != expected_size: - raise ValueError( - f"The dimension of the initial point ({len(point)}) does not match the " - f"number of parameters in the circuit ({expected_size})." - ) - - return point diff --git a/qiskit/algorithms/variational_algorithm.py b/qiskit/algorithms/variational_algorithm.py deleted file mode 100644 index 1b8b2ec6a164..000000000000 --- a/qiskit/algorithms/variational_algorithm.py +++ /dev/null @@ -1,137 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Variational Algorithm Base Class. - -This class can be used an interface for working with Variation Algorithms, such as VQE, -QAOA, or QSVM, and also provides helper utilities for implementing new variational algorithms. -Writing a new variational algorithm is a simple as extending this class, implementing a cost -function for the new algorithm to pass to the optimizer, and running :meth:`find_minimum` method -of this class to carry out the optimization. Alternatively, all of the functions below can be -overridden to opt-out of this infrastructure but still meet the interface requirements. - -.. note:: - - This component has some function that is normally random. If you want to reproduce behavior - then you should set the random number generator seed in the algorithm_globals - (``qiskit.utils.algorithm_globals.random_seed = seed``). -""" - -from __future__ import annotations -from abc import ABC, abstractmethod -import numpy as np - -from qiskit.circuit import QuantumCircuit - -from .algorithm_result import AlgorithmResult -from .optimizers import OptimizerResult - - -class VariationalAlgorithm(ABC): - """The Variational Algorithm Base Class.""" - - @property - @abstractmethod - def initial_point(self) -> np.ndarray | None: - """Returns initial point.""" - pass - - @initial_point.setter - @abstractmethod - def initial_point(self, initial_point: np.ndarray | None) -> None: - """Sets initial point.""" - pass - - -class VariationalResult(AlgorithmResult): - """Variation Algorithm Result.""" - - def __init__(self) -> None: - super().__init__() - self._optimizer_evals: int | None = None - self._optimizer_time: float | None = None - self._optimal_value: float | None = None - self._optimal_point: np.ndarray | None = None - self._optimal_parameters: dict | None = None - self._optimizer_result: OptimizerResult | None = None - self._optimal_circuit: QuantumCircuit | None = None - - @property - def optimizer_evals(self) -> int | None: - """Returns number of optimizer evaluations""" - return self._optimizer_evals - - @optimizer_evals.setter - def optimizer_evals(self, value: int) -> None: - """Sets number of optimizer evaluations""" - self._optimizer_evals = value - - @property - def optimizer_time(self) -> float | None: - """Returns time taken for optimization""" - return self._optimizer_time - - @optimizer_time.setter - def optimizer_time(self, value: float) -> None: - """Sets time taken for optimization""" - self._optimizer_time = value - - @property - def optimal_value(self) -> float | None: - """Returns optimal value""" - return self._optimal_value - - @optimal_value.setter - def optimal_value(self, value: int) -> None: - """Sets optimal value""" - self._optimal_value = value - - @property - def optimal_point(self) -> np.ndarray | None: - """Returns optimal point""" - return self._optimal_point - - @optimal_point.setter - def optimal_point(self, value: np.ndarray) -> None: - """Sets optimal point""" - self._optimal_point = value - - @property - def optimal_parameters(self) -> dict | None: - """Returns the optimal parameters in a dictionary""" - return self._optimal_parameters - - @optimal_parameters.setter - def optimal_parameters(self, value: dict) -> None: - """Sets optimal parameters""" - self._optimal_parameters = value - - @property - def optimizer_result(self) -> OptimizerResult | None: - """Returns the optimizer result""" - return self._optimizer_result - - @optimizer_result.setter - def optimizer_result(self, value: OptimizerResult) -> None: - """Sets optimizer result""" - self._optimizer_result = value - - @property - def optimal_circuit(self) -> QuantumCircuit: - """The optimal circuits. Along with the optimal parameters, - these can be used to retrieve the minimum eigenstate. - """ - return self._optimal_circuit - - @optimal_circuit.setter - def optimal_circuit(self, optimal_circuit: QuantumCircuit) -> None: - self._optimal_circuit = optimal_circuit diff --git a/qiskit/assembler/assemble_schedules.py b/qiskit/assembler/assemble_schedules.py index 6ddf4c140d56..c60c28ff9a50 100644 --- a/qiskit/assembler/assemble_schedules.py +++ b/qiskit/assembler/assemble_schedules.py @@ -224,7 +224,7 @@ def _assemble_instructions( for time, instruction in sched.instructions: if isinstance(instruction, instructions.Play): - if isinstance(instruction.pulse, (library.ParametricPulse, library.SymbolicPulse)): + if isinstance(instruction.pulse, library.SymbolicPulse): is_backend_supported = True try: pulse_shape = ParametricPulseShapes.from_instance(instruction.pulse).name diff --git a/qiskit/circuit/__init__.py b/qiskit/circuit/__init__.py index 1a41c299d53e..8886c88c4136 100644 --- a/qiskit/circuit/__init__.py +++ b/qiskit/circuit/__init__.py @@ -57,194 +57,195 @@ Supplementary Information ========================= -.. dropdown:: Quantum Circuit with conditionals - :animate: fade-in-slide-down +Quantum Circuit with conditionals +--------------------------------- - When building a quantum circuit, there can be interest in applying a certain gate only - if a classical register has a specific value. This can be done with the - :meth:`InstructionSet.c_if` method. +When building a quantum circuit, there can be interest in applying a certain gate only +if a classical register has a specific value. This can be done with the +:meth:`InstructionSet.c_if` method. - In the following example, we start with a single-qubit circuit formed by only a Hadamard gate - (:class:`~.HGate`), in which we expect to get :math:`|0\\rangle` and :math:`|1\\rangle` - with equal probability. +In the following example, we start with a single-qubit circuit formed by only a Hadamard gate +(:class:`~.HGate`), in which we expect to get :math:`|0\\rangle` and :math:`|1\\rangle` +with equal probability. - .. plot:: - :include-source: +.. plot:: + :include-source: - from qiskit import BasicAer, transpile, QuantumRegister, ClassicalRegister, QuantumCircuit + from qiskit import BasicAer, transpile, QuantumRegister, ClassicalRegister, QuantumCircuit - qr = QuantumRegister(1) - cr = ClassicalRegister(1) - qc = QuantumCircuit(qr, cr) - qc.h(0) - qc.measure(0, 0) - qc.draw('mpl') + qr = QuantumRegister(1) + cr = ClassicalRegister(1) + qc = QuantumCircuit(qr, cr) + qc.h(0) + qc.measure(0, 0) + qc.draw('mpl') - .. code-block:: +.. code-block:: - backend = BasicAer.get_backend('qasm_simulator') - tqc = transpile(qc, backend) - counts = backend.run(tqc).result().get_counts() + backend = BasicAer.get_backend('qasm_simulator') + tqc = transpile(qc, backend) + counts = backend.run(tqc).result().get_counts() - print(counts) + print(counts) - .. parsed-literal:: +.. parsed-literal:: - {'0': 524, '1': 500} + {'0': 524, '1': 500} - Now, we add an :class:`~.XGate` only if the value of the :class:`~.ClassicalRegister` is 0. - That way, if the state is :math:`|0\\rangle`, it will be changed to :math:`|1\\rangle` and - if the state is :math:`|1\\rangle`, it will not be changed at all, so the final state will - always be :math:`|1\\rangle`. +Now, we add an :class:`~.XGate` only if the value of the :class:`~.ClassicalRegister` is 0. +That way, if the state is :math:`|0\\rangle`, it will be changed to :math:`|1\\rangle` and +if the state is :math:`|1\\rangle`, it will not be changed at all, so the final state will +always be :math:`|1\\rangle`. + +.. plot:: + :include-source: - .. plot:: - :include-source: + from qiskit import BasicAer, transpile, QuantumRegister, ClassicalRegister, QuantumCircuit - from qiskit import BasicAer, transpile, QuantumRegister, ClassicalRegister, QuantumCircuit + qr = QuantumRegister(1) + cr = ClassicalRegister(1) + qc = QuantumCircuit(qr, cr) + qc.h(0) + qc.measure(0, 0) - qr = QuantumRegister(1) - cr = ClassicalRegister(1) - qc = QuantumCircuit(qr, cr) - qc.h(0) - qc.measure(0, 0) + qc.x(0).c_if(cr, 0) + qc.measure(0, 0) - qc.x(0).c_if(cr, 0) - qc.measure(0, 0) + qc.draw('mpl') - qc.draw('mpl') +.. code-block:: - .. code-block:: + backend = BasicAer.get_backend('qasm_simulator') + tqc = transpile(qc, backend) + counts = backend.run(tqc).result().get_counts() - backend = BasicAer.get_backend('qasm_simulator') - tqc = transpile(qc, backend) - counts = backend.run(tqc).result().get_counts() + print(counts) - print(counts) +.. parsed-literal:: - .. parsed-literal:: + {'1': 1024} - {'1': 1024} -.. dropdown:: Quantum Circuit Properties - :animate: fade-in-slide-down +Quantum Circuit Properties +-------------------------- - When constructing quantum circuits, there are several properties that help quantify - the "size" of the circuits, and their ability to be run on a noisy quantum device. - Some of these, like number of qubits, are straightforward to understand, while others - like depth and number of tensor components require a bit more explanation. Here we will - explain all of these properties, and, in preparation for understanding how circuits change - when run on actual devices, highlight the conditions under which they change. +When constructing quantum circuits, there are several properties that help quantify +the "size" of the circuits, and their ability to be run on a noisy quantum device. +Some of these, like number of qubits, are straightforward to understand, while others +like depth and number of tensor components require a bit more explanation. Here we will +explain all of these properties, and, in preparation for understanding how circuits change +when run on actual devices, highlight the conditions under which they change. - Consider the following circuit: +Consider the following circuit: - .. plot:: - :include-source: +.. plot:: + :include-source: - from qiskit import QuantumCircuit - qc = QuantumCircuit(12) - for idx in range(5): - qc.h(idx) - qc.cx(idx, idx+5) + from qiskit import QuantumCircuit + qc = QuantumCircuit(12) + for idx in range(5): + qc.h(idx) + qc.cx(idx, idx+5) - qc.cx(1, 7) - qc.x(8) - qc.cx(1, 9) - qc.x(7) - qc.cx(1, 11) - qc.swap(6, 11) - qc.swap(6, 9) - qc.swap(6, 10) - qc.x(6) - qc.draw('mpl') + qc.cx(1, 7) + qc.x(8) + qc.cx(1, 9) + qc.x(7) + qc.cx(1, 11) + qc.swap(6, 11) + qc.swap(6, 9) + qc.swap(6, 10) + qc.x(6) + qc.draw('mpl') - From the plot, it is easy to see that this circuit has 12 qubits, and a collection of - Hadamard, CNOT, X, and SWAP gates. But how to quantify this programmatically? Because we - can do single-qubit gates on all the qubits simultaneously, the number of qubits in this - circuit is equal to the **width** of the circuit: +From the plot, it is easy to see that this circuit has 12 qubits, and a collection of +Hadamard, CNOT, X, and SWAP gates. But how to quantify this programmatically? Because we +can do single-qubit gates on all the qubits simultaneously, the number of qubits in this +circuit is equal to the **width** of the circuit: - .. code-block:: +.. code-block:: - qc.width() + qc.width() - .. parsed-literal:: +.. parsed-literal:: - 12 + 12 - We can also just get the number of qubits directly: +We can also just get the number of qubits directly: - .. code-block:: +.. code-block:: - qc.num_qubits + qc.num_qubits - .. parsed-literal:: +.. parsed-literal:: - 12 + 12 - .. important:: +.. important:: - For a quantum circuit composed from just qubits, the circuit width is equal - to the number of qubits. This is the definition used in quantum computing. However, - for more complicated circuits with classical registers, and classically controlled gates, - this equivalence breaks down. As such, from now on we will not refer to the number of - qubits in a quantum circuit as the width. + For a quantum circuit composed from just qubits, the circuit width is equal + to the number of qubits. This is the definition used in quantum computing. However, + for more complicated circuits with classical registers, and classically controlled gates, + this equivalence breaks down. As such, from now on we will not refer to the number of + qubits in a quantum circuit as the width. - It is also straightforward to get the number and type of the gates in a circuit using - :meth:`QuantumCircuit.count_ops`: +It is also straightforward to get the number and type of the gates in a circuit using +:meth:`QuantumCircuit.count_ops`: - .. code-block:: +.. code-block:: - qc.count_ops() + qc.count_ops() - .. parsed-literal:: +.. parsed-literal:: - OrderedDict([('cx', 8), ('h', 5), ('x', 3), ('swap', 3)]) + OrderedDict([('cx', 8), ('h', 5), ('x', 3), ('swap', 3)]) - We can also get just the raw count of operations by computing the circuits - :meth:`QuantumCircuit.size`: +We can also get just the raw count of operations by computing the circuits +:meth:`QuantumCircuit.size`: - .. code-block:: +.. code-block:: - qc.size() + qc.size() - .. parsed-literal:: +.. parsed-literal:: - 19 + 19 - A particularly important circuit property is known as the circuit **depth**. The depth - of a quantum circuit is a measure of how many "layers" of quantum gates, executed in - parallel, it takes to complete the computation defined by the circuit. Because quantum - gates take time to implement, the depth of a circuit roughly corresponds to the amount of - time it takes the quantum computer to execute the circuit. Thus, the depth of a circuit - is one important quantity used to measure if a quantum circuit can be run on a device. +A particularly important circuit property is known as the circuit **depth**. The depth +of a quantum circuit is a measure of how many "layers" of quantum gates, executed in +parallel, it takes to complete the computation defined by the circuit. Because quantum +gates take time to implement, the depth of a circuit roughly corresponds to the amount of +time it takes the quantum computer to execute the circuit. Thus, the depth of a circuit +is one important quantity used to measure if a quantum circuit can be run on a device. - The depth of a quantum circuit has a mathematical definition as the longest path in a - directed acyclic graph (DAG). However, such a definition is a bit hard to grasp, even for - experts. Fortunately, the depth of a circuit can be easily understood by anyone familiar - with playing `Tetris `_. Lets see how to compute this - graphically: +The depth of a quantum circuit has a mathematical definition as the longest path in a +directed acyclic graph (DAG). However, such a definition is a bit hard to grasp, even for +experts. Fortunately, the depth of a circuit can be easily understood by anyone familiar +with playing `Tetris `_. Lets see how to compute this +graphically: - .. image:: /source_images/depth.gif +.. image:: /source_images/depth.gif - .. raw:: html +.. raw:: html -

+

- We can verify our graphical result using :meth:`QuantumCircuit.depth`: +We can verify our graphical result using :meth:`QuantumCircuit.depth`: - .. code-block:: +.. code-block:: - qc.depth() + qc.depth() - .. parsed-literal:: +.. parsed-literal:: - 9 + 9 - .. raw:: html +.. raw:: html -
+
Quantum Circuit API =================== @@ -279,6 +280,7 @@ InstructionSet Operation EquivalenceLibrary + Store Control Flow Operations ----------------------- @@ -375,6 +377,7 @@ from .delay import Delay from .measure import Measure from .reset import Reset +from .store import Store from .parameter import Parameter from .parametervector import ParameterVector from .parameterexpression import ParameterExpression diff --git a/qiskit/circuit/bit.py b/qiskit/circuit/bit.py index 51996590cf5d..d51e82c84625 100644 --- a/qiskit/circuit/bit.py +++ b/qiskit/circuit/bit.py @@ -63,6 +63,7 @@ def __init__(self, register=None, index=None): @deprecate_func( is_property=True, since="0.17", + package_name="qiskit-terra", additional_msg=( "Instead, use :meth:`~qiskit.circuit.quantumcircuit.QuantumCircuit.find_bit` to find " "all the containing registers within a circuit and the index of the bit within the " @@ -85,6 +86,7 @@ def register(self): # pylint: disable=bad-docstring-quotes @deprecate_func( is_property=True, since="0.17", + package_name="qiskit-terra", additional_msg=( "Instead, use :meth:`~qiskit.circuit.quantumcircuit.QuantumCircuit.find_bit` to find " "all the containing registers within a circuit and the index of the bit within the " diff --git a/qiskit/circuit/classical/expr/__init__.py b/qiskit/circuit/classical/expr/__init__.py index d2cd4bc5044e..4502aa52779a 100644 --- a/qiskit/circuit/classical/expr/__init__.py +++ b/qiskit/circuit/classical/expr/__init__.py @@ -39,10 +39,11 @@ These objects are mutable and should not be reused in a different location without a copy. -The entry point from general circuit objects to the expression system is by wrapping the object -in a :class:`Var` node and associating a :class:`~.types.Type` with it. +The base for dynamic variables is the :class:`Var`, which can be either an arbitrarily typed runtime +variable, or a wrapper around a :class:`.Clbit` or :class:`.ClassicalRegister`. .. autoclass:: Var + :members: var, name Similarly, literals used in comparison (such as integers) should be lifted to :class:`Value` nodes with associated types. @@ -86,10 +87,18 @@ The functions and methods described in this section are a more user-friendly way to build the expression tree, while staying close to the internal representation. All these functions will automatically lift valid Python scalar values into corresponding :class:`Var` or :class:`Value` -objects, and will resolve any required implicit casts on your behalf. +objects, and will resolve any required implicit casts on your behalf. If you want to directly use +some scalar value as an :class:`Expr` node, you can manually :func:`lift` it yourself. .. autofunction:: lift +Typically you should create memory-owning :class:`Var` instances by using the +:meth:`.QuantumCircuit.add_var` method to declare them in some circuit context, since a +:class:`.QuantumCircuit` will not accept an :class:`Expr` that contains variables that are not +already declared in it, since it needs to know how to allocate the storage and how the variable will +be initialized. However, should you want to do this manually, you should use the low-level +:meth:`Var.new` call to safely generate a named variable for usage. + You can manually specify casts in cases where the cast is allowed in explicit form, but may be lossy (such as the cast of a higher precision :class:`~.types.Uint` to a lower precision one). @@ -151,6 +160,11 @@ suitable "key" functions to do the comparison. .. autofunction:: structurally_equivalent + +Some expressions have associated memory locations, and others may be purely temporary. +You can use :func:`is_lvalue` to determine whether an expression has an associated memory location. + +.. autofunction:: is_lvalue """ __all__ = [ @@ -163,6 +177,7 @@ "ExprVisitor", "iter_vars", "structurally_equivalent", + "is_lvalue", "lift", "cast", "bit_not", @@ -182,7 +197,7 @@ ] from .expr import Expr, Var, Value, Cast, Unary, Binary -from .visitors import ExprVisitor, iter_vars, structurally_equivalent +from .visitors import ExprVisitor, iter_vars, structurally_equivalent, is_lvalue from .constructors import ( lift, cast, diff --git a/qiskit/circuit/classical/expr/constructors.py b/qiskit/circuit/classical/expr/constructors.py index 1406a86237c5..64a19a2aee2a 100644 --- a/qiskit/circuit/classical/expr/constructors.py +++ b/qiskit/circuit/classical/expr/constructors.py @@ -35,65 +35,27 @@ "lift_legacy_condition", ] -import enum import typing from .expr import Expr, Var, Value, Unary, Binary, Cast +from ..types import CastKind, cast_kind from .. import types if typing.TYPE_CHECKING: import qiskit -class _CastKind(enum.Enum): - EQUAL = enum.auto() - """The two types are equal; no cast node is required at all.""" - IMPLICIT = enum.auto() - """The 'from' type can be cast to the 'to' type implicitly. A ``Cast(implicit=True)`` node is - the minimum required to specify this.""" - LOSSLESS = enum.auto() - """The 'from' type can be cast to the 'to' type explicitly, and the cast will be lossless. This - requires a ``Cast(implicit=False)`` node, but there's no danger from inserting one.""" - DANGEROUS = enum.auto() - """The 'from' type has a defined cast to the 'to' type, but depending on the value, it may lose - data. A user would need to manually specify casts.""" - NONE = enum.auto() - """There is no casting permitted from the 'from' type to the 'to' type.""" - - -def _uint_cast(from_: types.Uint, to_: types.Uint, /) -> _CastKind: - if from_.width == to_.width: - return _CastKind.EQUAL - if from_.width < to_.width: - return _CastKind.LOSSLESS - return _CastKind.DANGEROUS - - -_ALLOWED_CASTS = { - (types.Bool, types.Bool): lambda _a, _b, /: _CastKind.EQUAL, - (types.Bool, types.Uint): lambda _a, _b, /: _CastKind.LOSSLESS, - (types.Uint, types.Bool): lambda _a, _b, /: _CastKind.IMPLICIT, - (types.Uint, types.Uint): _uint_cast, -} - - -def _cast_kind(from_: types.Type, to_: types.Type, /) -> _CastKind: - if (coercer := _ALLOWED_CASTS.get((from_.kind, to_.kind))) is None: - return _CastKind.NONE - return coercer(from_, to_) - - def _coerce_lossless(expr: Expr, type: types.Type) -> Expr: """Coerce ``expr`` to ``type`` by inserting a suitable :class:`Cast` node, if the cast is lossless. Otherwise, raise a ``TypeError``.""" - kind = _cast_kind(expr.type, type) - if kind is _CastKind.EQUAL: + kind = cast_kind(expr.type, type) + if kind is CastKind.EQUAL: return expr - if kind is _CastKind.IMPLICIT: + if kind is CastKind.IMPLICIT: return Cast(expr, type, implicit=True) - if kind is _CastKind.LOSSLESS: + if kind is CastKind.LOSSLESS: return Cast(expr, type, implicit=False) - if kind is _CastKind.DANGEROUS: + if kind is CastKind.DANGEROUS: raise TypeError(f"cannot cast '{expr}' to '{type}' without loss of precision") raise TypeError(f"no cast is defined to take '{expr}' to '{type}'") @@ -198,7 +160,7 @@ def cast(operand: typing.Any, type: types.Type, /) -> Expr: Cast(Value(5, types.Uint(32)), types.Uint(8), implicit=False) """ operand = lift(operand) - if _cast_kind(operand.type, type) is _CastKind.NONE: + if cast_kind(operand.type, type) is CastKind.NONE: raise TypeError(f"cannot cast '{operand}' to '{type}'") return Cast(operand, type) diff --git a/qiskit/circuit/classical/expr/expr.py b/qiskit/circuit/classical/expr/expr.py index b9e9aad4a2b7..c22870e51fee 100644 --- a/qiskit/circuit/classical/expr/expr.py +++ b/qiskit/circuit/classical/expr/expr.py @@ -31,6 +31,7 @@ import abc import enum import typing +import uuid from .. import types @@ -108,24 +109,92 @@ def __repr__(self): @typing.final class Var(Expr): - """A classical variable.""" - - __slots__ = ("var",) + """A classical variable. + + These variables take two forms: a new-style variable that owns its storage location and has an + associated name; and an old-style variable that wraps a :class:`.Clbit` or + :class:`.ClassicalRegister` instance that is owned by some containing circuit. In general, + construction of variables for use in programs should use :meth:`Var.new` or + :meth:`.QuantumCircuit.add_var`. + + Variables are immutable after construction, so they can be used as dictionary keys.""" + + __slots__ = ("var", "name") + + var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister | uuid.UUID + """A reference to the backing data storage of the :class:`Var` instance. When lifting + old-style :class:`.Clbit` or :class:`.ClassicalRegister` instances into a :class:`Var`, + this is exactly the :class:`.Clbit` or :class:`.ClassicalRegister`. If the variable is a + new-style classical variable (one that owns its own storage separate to the old + :class:`.Clbit`/:class:`.ClassicalRegister` model), this field will be a :class:`~uuid.UUID` + to uniquely identify it.""" + name: str | None + """The name of the variable. This is required to exist if the backing :attr:`var` attribute + is a :class:`~uuid.UUID`, i.e. if it is a new-style variable, and must be ``None`` if it is + an old-style variable.""" def __init__( - self, var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister, type: types.Type + self, + var: qiskit.circuit.Clbit | qiskit.circuit.ClassicalRegister | uuid.UUID, + type: types.Type, + *, + name: str | None = None, ): - self.type = type - self.var = var + super().__setattr__("type", type) + super().__setattr__("var", var) + super().__setattr__("name", name) + + @classmethod + def new(cls, name: str, type: types.Type) -> typing.Self: + """Generate a new named variable that owns its own backing storage.""" + return cls(uuid.uuid4(), type, name=name) + + @property + def standalone(self) -> bool: + """Whether this :class:`Var` is a standalone variable that owns its storage location. If + false, this is a wrapper :class:`Var` around a pre-existing circuit object.""" + return isinstance(self.var, uuid.UUID) def accept(self, visitor, /): return visitor.visit_var(self) + def __setattr__(self, key, value): + if hasattr(self, key): + raise AttributeError(f"'Var' object attribute '{key}' is read-only") + raise AttributeError(f"'Var' object has no attribute '{key}'") + + def __hash__(self): + return hash((self.type, self.var, self.name)) + def __eq__(self, other): - return isinstance(other, Var) and self.type == other.type and self.var == other.var + return ( + isinstance(other, Var) + and self.type == other.type + and self.var == other.var + and self.name == other.name + ) def __repr__(self): - return f"Var({self.var}, {self.type})" + if self.name is None: + return f"Var({self.var}, {self.type})" + return f"Var({self.var}, {self.type}, name='{self.name}')" + + def __getstate__(self): + return (self.var, self.type, self.name) + + def __setstate__(self, state): + var, type, name = state + super().__setattr__("type", type) + super().__setattr__("var", var) + super().__setattr__("name", name) + + def __copy__(self): + # I am immutable... + return self + + def __deepcopy__(self, memo): + # ... as are all my consituent parts. + return self @typing.final diff --git a/qiskit/circuit/classical/expr/visitors.py b/qiskit/circuit/classical/expr/visitors.py index 07ad36a8e0e4..c0c1a5894af6 100644 --- a/qiskit/circuit/classical/expr/visitors.py +++ b/qiskit/circuit/classical/expr/visitors.py @@ -215,3 +215,66 @@ def structurally_equivalent( True """ return left.accept(_StructuralEquivalenceImpl(right, left_var_key, right_var_key)) + + +class _IsLValueImpl(ExprVisitor[bool]): + __slots__ = () + + def visit_var(self, node, /): + return True + + def visit_value(self, node, /): + return False + + def visit_unary(self, node, /): + return False + + def visit_binary(self, node, /): + return False + + def visit_cast(self, node, /): + return False + + +_IS_LVALUE = _IsLValueImpl() + + +def is_lvalue(node: expr.Expr, /) -> bool: + """Return whether this expression can be used in l-value positions, that is, whether it has a + well-defined location in memory, such as one that might be writeable. + + Being an l-value is a necessary but not sufficient for this location to be writeable; it is + permissible that a larger object containing this memory location may not allow writing from + the scope that attempts to write to it. This would be an access property of the containing + program, however, and not an inherent property of the expression system. + + Examples: + Literal values are never l-values; there's no memory location associated with (for example) + the constant ``1``:: + + >>> from qiskit.circuit.classical import expr + >>> expr.is_lvalue(expr.lift(2)) + False + + :class:`~.expr.Var` nodes are always l-values, because they always have some associated + memory location:: + + >>> from qiskit.circuit.classical import types + >>> from qiskit.circuit import Clbit + >>> expr.is_lvalue(expr.Var.new("a", types.Bool())) + True + >>> expr.is_lvalue(expr.lift(Clbit())) + True + + Currently there are no unary or binary operations on variables that can produce an l-value + expression, but it is likely in the future that some sort of "indexing" operation will be + added, which could produce l-values:: + + >>> a = expr.Var.new("a", types.Uint(8)) + >>> b = expr.Var.new("b", types.Uint(8)) + >>> expr.is_lvalue(a) and expr.is_lvalue(b) + True + >>> expr.is_lvalue(expr.bit_and(a, b)) + False + """ + return node.accept(_IS_LVALUE) diff --git a/qiskit/circuit/classical/types/__init__.py b/qiskit/circuit/classical/types/__init__.py index c55c724315cc..93ab90e32166 100644 --- a/qiskit/circuit/classical/types/__init__.py +++ b/qiskit/circuit/classical/types/__init__.py @@ -15,6 +15,8 @@ Typing (:mod:`qiskit.circuit.classical.types`) ============================================== +Representation +============== The type system of the expression tree is exposed through this module. This is inherently linked to the expression system in the :mod:`~.classical.expr` module, as most expressions can only be @@ -41,11 +43,18 @@ Note that :class:`Uint` defines a family of types parametrised by their width; it is not one single type, which may be slightly different to the 'classical' programming languages you are used to. + +Working with types +================== + There are some functions on these types exposed here as well. These are mostly expected to be used only in manipulations of the expression tree; users who are building expressions using the :ref:`user-facing construction interface ` should not need to use these. +Partial ordering of types +------------------------- + The type system is equipped with a partial ordering, where :math:`a < b` is interpreted as ":math:`a` is a strict subtype of :math:`b`". Note that the partial ordering is a subset of the directed graph that describes the allowed explicit casting operations between types. The partial @@ -66,6 +75,20 @@ .. autofunction:: is_subtype .. autofunction:: is_supertype .. autofunction:: greater + + +Casting between types +--------------------- + +It is common to need to cast values of one type to another type. The casting rules for this are +embedded into the :mod:`types` module. You can query the casting kinds using :func:`cast_kind`: + +.. autofunction:: cast_kind + +The return values from this function are an enumeration explaining the types of cast that are +allowed from the left type to the right type. + +.. autoclass:: CastKind """ __all__ = [ @@ -77,7 +100,9 @@ "is_subtype", "is_supertype", "greater", + "CastKind", + "cast_kind", ] from .types import Type, Bool, Uint -from .ordering import Ordering, order, is_subtype, is_supertype, greater +from .ordering import Ordering, order, is_subtype, is_supertype, greater, CastKind, cast_kind diff --git a/qiskit/circuit/classical/types/ordering.py b/qiskit/circuit/classical/types/ordering.py index aceb9aeefbcf..b000e91cf5ed 100644 --- a/qiskit/circuit/classical/types/ordering.py +++ b/qiskit/circuit/classical/types/ordering.py @@ -20,6 +20,8 @@ "is_supertype", "order", "greater", + "CastKind", + "cast_kind", ] import enum @@ -161,3 +163,60 @@ def greater(left: Type, right: Type, /) -> Type: if order_ is Ordering.NONE: raise TypeError(f"no ordering exists between '{left}' and '{right}'") return left if order_ is Ordering.GREATER else right + + +class CastKind(enum.Enum): + """A return value indicating the type of cast that can occur from one type to another.""" + + EQUAL = enum.auto() + """The two types are equal; no cast node is required at all.""" + IMPLICIT = enum.auto() + """The 'from' type can be cast to the 'to' type implicitly. A :class:`~.expr.Cast` node with + ``implicit==True`` is the minimum required to specify this.""" + LOSSLESS = enum.auto() + """The 'from' type can be cast to the 'to' type explicitly, and the cast will be lossless. This + requires a :class:`~.expr.Cast`` node with ``implicit=False``, but there's no danger from + inserting one.""" + DANGEROUS = enum.auto() + """The 'from' type has a defined cast to the 'to' type, but depending on the value, it may lose + data. A user would need to manually specify casts.""" + NONE = enum.auto() + """There is no casting permitted from the 'from' type to the 'to' type.""" + + +def _uint_cast(from_: Uint, to_: Uint, /) -> CastKind: + if from_.width == to_.width: + return CastKind.EQUAL + if from_.width < to_.width: + return CastKind.LOSSLESS + return CastKind.DANGEROUS + + +_ALLOWED_CASTS = { + (Bool, Bool): lambda _a, _b, /: CastKind.EQUAL, + (Bool, Uint): lambda _a, _b, /: CastKind.LOSSLESS, + (Uint, Bool): lambda _a, _b, /: CastKind.IMPLICIT, + (Uint, Uint): _uint_cast, +} + + +def cast_kind(from_: Type, to_: Type, /) -> CastKind: + """Determine the sort of cast that is required to move from the left type to the right type. + + Examples: + + .. code-block:: python + + >>> from qiskit.circuit.classical import types + >>> types.cast_kind(types.Bool(), types.Bool()) + + >>> types.cast_kind(types.Uint(8), types.Bool()) + + >>> types.cast_kind(types.Bool(), types.Uint(8)) + + >>> types.cast_kind(types.Uint(16), types.Uint(8)) + + """ + if (coercer := _ALLOWED_CASTS.get((from_.kind, to_.kind))) is None: + return CastKind.NONE + return coercer(from_, to_) diff --git a/qiskit/circuit/classical/types/types.py b/qiskit/circuit/classical/types/types.py index 711f82db5fc0..04266aefd410 100644 --- a/qiskit/circuit/classical/types/types.py +++ b/qiskit/circuit/classical/types/types.py @@ -89,6 +89,9 @@ class Bool(Type, metaclass=_Singleton): def __repr__(self): return "Bool()" + def __hash__(self): + return hash(self.__class__) + def __eq__(self, other): return isinstance(other, Bool) @@ -107,5 +110,8 @@ def __init__(self, width: int): def __repr__(self): return f"Uint({self.width})" + def __hash__(self): + return hash((self.__class__, self.width)) + def __eq__(self, other): return isinstance(other, Uint) and self.width == other.width diff --git a/qiskit/circuit/classicalregister.py b/qiskit/circuit/classicalregister.py index 644705ffee34..9ae39055039c 100644 --- a/qiskit/circuit/classicalregister.py +++ b/qiskit/circuit/classicalregister.py @@ -64,6 +64,7 @@ class ClassicalRegister(Register): "provided, because the premise is wrong." ), since="0.23.0", + package_name="qiskit-terra", ) def qasm(self): """Return OPENQASM string for this register.""" diff --git a/qiskit/circuit/controlflow/_builder_utils.py b/qiskit/circuit/controlflow/_builder_utils.py index 5ba5c9612c9d..aa1e331eb41a 100644 --- a/qiskit/circuit/controlflow/_builder_utils.py +++ b/qiskit/circuit/controlflow/_builder_utils.py @@ -15,15 +15,17 @@ from __future__ import annotations import dataclasses -from typing import Iterable, Tuple, Set, Union, TypeVar +from typing import Iterable, Tuple, Set, Union, TypeVar, TYPE_CHECKING from qiskit.circuit.classical import expr, types from qiskit.circuit.exceptions import CircuitError -from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.register import Register from qiskit.circuit.classicalregister import ClassicalRegister, Clbit from qiskit.circuit.quantumregister import QuantumRegister +if TYPE_CHECKING: + from qiskit.circuit import QuantumCircuit + _ConditionT = TypeVar( "_ConditionT", bound=Union[Tuple[ClassicalRegister, int], Tuple[Clbit, int], expr.Expr] ) @@ -159,6 +161,9 @@ def _unify_circuit_resources_rebuild( # pylint: disable=invalid-name # (it's t This function will always rebuild the objects into new :class:`.QuantumCircuit` instances. """ + # pylint: disable=cyclic-import + from qiskit.circuit import QuantumCircuit + qubits, clbits = set(), set() for circuit in circuits: qubits.update(circuit.qubits) diff --git a/qiskit/circuit/controlflow/builder.py b/qiskit/circuit/controlflow/builder.py index c8ada706e5fe..edd5394449db 100644 --- a/qiskit/circuit/controlflow/builder.py +++ b/qiskit/circuit/controlflow/builder.py @@ -17,12 +17,14 @@ # having a far more complete builder of all circuits, with more classical control and creation, in # the future. +from __future__ import annotations import abc import itertools import typing -from typing import Callable, Collection, Iterable, List, FrozenSet, Tuple, Union, Optional +from typing import Collection, Iterable, List, FrozenSet, Tuple, Union, Optional, Sequence +from qiskit.circuit.classical import expr from qiskit.circuit.classicalregister import Clbit, ClassicalRegister from qiskit.circuit.exceptions import CircuitError from qiskit.circuit.instruction import Instruction @@ -33,7 +35,125 @@ from ._builder_utils import condition_resources, node_resources if typing.TYPE_CHECKING: - import qiskit # pylint: disable=cyclic-import + import qiskit + + +class CircuitScopeInterface(abc.ABC): + """An interface that circuits and builder blocks explicitly fulfill, which contains the primitive + methods of circuit construction and object validation. + + This allows core circuit methods to be applied to the currently open builder scope, and allows + the builders to hook into all places where circuit resources might be used. This allows the + builders to track the resources being used, without getting in the way of + :class:`.QuantumCircuit` doing its own thing. + """ + + __slots__ = () + + @property + @abc.abstractmethod + def instructions(self) -> Sequence[CircuitInstruction]: + """Indexable view onto the :class:`.CircuitInstruction`s backing this scope.""" + + @abc.abstractmethod + def append(self, instruction: CircuitInstruction) -> CircuitInstruction: + """Low-level 'append' primitive; this may assume that the qubits, clbits and operation are + all valid for the circuit. + + Abstraction of :meth:`.QuantumCircuit._append` (the low-level one, not the high-level). + + Args: + instruction: the resource-validated instruction context object. + + Returns: + the instruction context object actually appended. This is not required to be the same + as the object given (but typically will be). + """ + + @abc.abstractmethod + def resolve_classical_resource( + self, specifier: Clbit | ClassicalRegister | int + ) -> Clbit | ClassicalRegister: + """Resolve a single bit-like classical-resource specifier. + + A resource refers to either a classical bit or a register, where integers index into the + classical bits of the greater circuit. + + This is called whenever a classical bit or register is being used outside the standard + :class:`.Clbit` usage of instructions in :meth:`append`, such as in a legacy two-tuple + condition. + + Args: + specifier: the classical resource specifier. + + Returns: + the resolved resource. This cannot be an integer any more; an integer input is resolved + into a classical bit. + + Raises: + CircuitError: if the resource cannot be used by the scope, such as an out-of-range index + or a :class:`.Clbit` that isn't actually in the circuit. + """ + + @abc.abstractmethod + def add_uninitialized_var(self, var: expr.Var): + """Add an uninitialized variable to the circuit scope. + + The general circuit context is responsible for ensuring the variable is initialized. These + uninitialized variables are guaranteed to be standalone. + + Args: + var: the variable to add, if valid. + + Raises: + CircuitError: if the variable cannot be added, such as because it invalidly shadows or + redefines an existing name. + """ + + @abc.abstractmethod + def remove_var(self, var: expr.Var): + """Remove a variable from the locals of this scope. + + This is only called in the case that an exception occurred while initializing the variable, + and is not exposed to users. + + Args: + var: the variable to remove. It can be assumed that this was already the subject of an + :meth:`add_uninitialized_var` call. + """ + + @abc.abstractmethod + def use_var(self, var: expr.Var): + """Called for every standalone classical runtime variable being used by some circuit + instruction. + + The given variable is guaranteed to be a stand-alone variable; bit-like resource-wrapping + variables will have been filtered out and their resources given to + :meth:`resolve_classical_resource`. + + Args: + var: the variable to validate. + + Returns: + the same variable. + + Raises: + CircuitError: if the variable is not valid for this scope. + """ + + @abc.abstractmethod + def get_var(self, name: str) -> Optional[expr.Var]: + """Get the variable (if any) in scope with the given name. + + This should call up to the parent scope if in a control-flow builder scope, in case the + variable exists in an outer scope. + + Args: + name: the name of the symbol to lookup. + + Returns: + the variable if it is found, otherwise ``None``. + """ class InstructionResources(typing.NamedTuple): @@ -169,7 +289,7 @@ def repeat(self, n): raise CircuitError("Cannot repeat a placeholder instruction.") -class ControlFlowBuilderBlock: +class ControlFlowBuilderBlock(CircuitScopeInterface): """A lightweight scoped block for holding instructions within a control-flow builder context. This class is designed only to be used by :obj:`.QuantumCircuit` as an internal context for @@ -199,15 +319,17 @@ class ControlFlowBuilderBlock: """ __slots__ = ( - "instructions", + "_instructions", "qubits", "clbits", "registers", "global_phase", "_allow_jumps", - "_resource_requester", + "_parent", "_built", "_forbidden_message", + "_vars_local", + "_vars_capture", ) def __init__( @@ -215,8 +337,8 @@ def __init__( qubits: Iterable[Qubit], clbits: Iterable[Clbit], *, + parent: CircuitScopeInterface, registers: Iterable[Register] = (), - resource_requester: Callable, allow_jumps: bool = True, forbidden_message: Optional[str] = None, ): @@ -238,26 +360,22 @@ def __init__( uses *exactly* the same set of resources. We cannot verify this from within the builder interface (and it is too expensive to do when the ``for`` op is made), so we fail safe, and require the user to use the more verbose, internal form. - resource_requester: A callback function that takes in some classical resource specifier, - and returns a concrete classical resource, if this scope is allowed to access that - resource. In almost all cases, this should be a resolver from the - :obj:`.QuantumCircuit` that this scope is contained in. See - :meth:`.QuantumCircuit._resolve_classical_resource` for the normal expected input - here, and the documentation of :obj:`.InstructionSet`, which uses this same - callback. + parent: The scope interface of the containing scope. forbidden_message: If a string is given here, a :exc:`.CircuitError` will be raised on any attempts to append instructions to the scope with this message. This is used by pseudo scopes where the state machine of the builder scopes has changed into a position where no instructions should be accepted, such as when inside a ``switch`` but outside any cases. """ - self.instructions: List[CircuitInstruction] = [] + self._instructions: List[CircuitInstruction] = [] self.qubits = set(qubits) self.clbits = set(clbits) self.registers = set(registers) self.global_phase = 0.0 + self._vars_local = {} + self._vars_capture = {} self._allow_jumps = allow_jumps - self._resource_requester = resource_requester + self._parent = parent self._built = False self._forbidden_message = forbidden_message @@ -275,9 +393,11 @@ def allow_jumps(self): """ return self._allow_jumps + @property + def instructions(self): + return self._instructions + def append(self, instruction: CircuitInstruction) -> CircuitInstruction: - """Add an instruction into the scope, keeping track of the qubits and clbits that have been - used in total.""" if self._forbidden_message is not None: raise CircuitError(self._forbidden_message) @@ -293,50 +413,77 @@ def append(self, instruction: CircuitInstruction) -> CircuitInstruction: " because it is not in a loop." ) - self.instructions.append(instruction) + self._instructions.append(instruction) self.qubits.update(instruction.qubits) self.clbits.update(instruction.clbits) return instruction - def request_classical_resource(self, specifier): - """Resolve a single classical resource specifier into a concrete resource, raising an error - if the specifier is invalid, and track it as now being used in scope. - - Args: - specifier (Union[Clbit, ClassicalRegister, int]): a specifier of a classical resource - present in this circuit. An ``int`` will be resolved into a :obj:`.Clbit` using the - same conventions that measurement operations on this circuit use. - - Returns: - Union[Clbit, ClassicalRegister]: the requested resource, resolved into a concrete - instance of :obj:`.Clbit` or :obj:`.ClassicalRegister`. - - Raises: - CircuitError: if the resource is not present in this circuit, or if the integer index - passed is out-of-bounds. - """ + def resolve_classical_resource(self, specifier): if self._built: raise CircuitError("Cannot add resources after the scope has been built.") # Allow the inner resolve to propagate exceptions. - resource = self._resource_requester(specifier) + resource = self._parent.resolve_classical_resource(specifier) if isinstance(resource, Clbit): self.add_bits((resource,)) else: self.add_register(resource) return resource + def add_uninitialized_var(self, var: expr.Var): + if self._built: + raise CircuitError("Cannot add resources after the scope has been built.") + # We can shadow a name if it was declared in an outer scope, but only if we haven't already + # captured it ourselves yet. + if (previous := self._vars_local.get(var.name)) is not None: + if previous == var: + raise CircuitError(f"'{var}' is already present in the scope") + raise CircuitError(f"cannot add '{var}' as its name shadows the existing '{previous}'") + if var.name in self._vars_capture: + raise CircuitError(f"cannot add '{var}' as its name shadows the existing '{previous}'") + self._vars_local[var.name] = var + + def remove_var(self, var: expr.Var): + if self._built: + raise RuntimeError("exception handler 'remove_var' called after scope built") + self._vars_local.pop(var.name) + + def get_var(self, name: str): + if (out := self._vars_local.get(name)) is not None: + return out + return self._parent.get_var(name) + + def use_var(self, var: expr.Var): + if (local := self._vars_local.get(var.name)) is not None: + if local == var: + return + raise CircuitError(f"cannot use '{var}' which is shadowed by the local '{local}'") + if self._vars_capture.get(var.name) == var: + return + if self._parent.get_var(var.name) != var: + raise CircuitError(f"cannot close over '{var}', which is not in scope") + self._parent.use_var(var) + self._vars_capture[var.name] = var + + def iter_local_vars(self): + """Iterator over the variables currently declared in this scope.""" + return self._vars_local.values() + + def iter_captured_vars(self): + """Iterator over the variables currently captured in this scope.""" + return self._vars_capture.values() + def peek(self) -> CircuitInstruction: """Get the value of the most recent instruction tuple in this scope.""" - if not self.instructions: + if not self._instructions: raise CircuitError("This scope contains no instructions.") - return self.instructions[-1] + return self._instructions[-1] def pop(self) -> CircuitInstruction: """Get the value of the most recent instruction in this scope, and remove it from this object.""" - if not self.instructions: + if not self._instructions: raise CircuitError("This scope contains no instructions.") - return self.instructions.pop() + return self._instructions.pop() def add_bits(self, bits: Iterable[Union[Qubit, Clbit]]): """Add extra bits to this scope that are not associated with any concrete instruction yet. @@ -403,6 +550,7 @@ def build( and using the minimal set of resources necessary to support them, within the enclosing scope. """ + # pylint: disable=cyclic-import from qiskit.circuit import QuantumCircuit, SwitchCaseOp # There's actually no real problem with building a scope more than once. This flag is more @@ -420,10 +568,18 @@ def build( # We start off by only giving the QuantumCircuit the qubits we _know_ it will need, and add # more later as needed. out = QuantumCircuit( - list(self.qubits), list(self.clbits), *self.registers, global_phase=self.global_phase + list(self.qubits), + list(self.clbits), + *self.registers, + global_phase=self.global_phase, + captures=self._vars_capture.values(), ) + for var in self._vars_local.values(): + # The requisite `Store` instruction to initialise the variable will have been appended + # into the instructions. + out.add_uninitialized_var(var) - for instruction in self.instructions: + for instruction in self._instructions: if isinstance(instruction.operation, InstructionPlaceholder): operation, resources = instruction.operation.concrete_instruction( all_qubits, all_clbits @@ -482,11 +638,14 @@ def copy(self) -> "ControlFlowBuilderBlock": a semi-shallow copy of this object. """ out = type(self).__new__(type(self)) - out.instructions = self.instructions.copy() + out._instructions = self._instructions.copy() out.qubits = self.qubits.copy() out.clbits = self.clbits.copy() out.registers = self.registers.copy() out.global_phase = self.global_phase + out._vars_local = self._vars_local.copy() + out._vars_capture = self._vars_capture.copy() + out._parent = self._parent out._allow_jumps = self._allow_jumps out._forbidden_message = self._forbidden_message return out diff --git a/qiskit/circuit/controlflow/control_flow.py b/qiskit/circuit/controlflow/control_flow.py index 11ac283132f4..fefa27efa27f 100644 --- a/qiskit/circuit/controlflow/control_flow.py +++ b/qiskit/circuit/controlflow/control_flow.py @@ -13,15 +13,26 @@ "Container to encapsulate all control flow operations." from __future__ import annotations + +import typing from abc import ABC, abstractmethod -from typing import Iterable -from qiskit.circuit import QuantumCircuit, Instruction +from qiskit.circuit.instruction import Instruction +from qiskit.circuit.exceptions import CircuitError + +if typing.TYPE_CHECKING: + from qiskit.circuit import QuantumCircuit class ControlFlowOp(Instruction, ABC): """Abstract class to encapsulate all control flow operations.""" + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + for block in self.blocks: + if block.num_input_vars: + raise CircuitError("control-flow blocks cannot contain input variables") + @property @abstractmethod def blocks(self) -> tuple[QuantumCircuit, ...]: @@ -29,10 +40,9 @@ def blocks(self) -> tuple[QuantumCircuit, ...]: execution of this ControlFlowOp. May be parameterized by a loop parameter to be resolved at run time. """ - pass @abstractmethod - def replace_blocks(self, blocks: Iterable[QuantumCircuit]) -> "ControlFlowOp": + def replace_blocks(self, blocks: typing.Iterable[QuantumCircuit]) -> ControlFlowOp: """Replace blocks and return new instruction. Args: blocks: Tuple of QuantumCircuits to replace in instruction. @@ -40,4 +50,3 @@ def replace_blocks(self, blocks: Iterable[QuantumCircuit]) -> "ControlFlowOp": Returns: New ControlFlowOp with replaced blocks. """ - pass diff --git a/qiskit/circuit/controlflow/for_loop.py b/qiskit/circuit/controlflow/for_loop.py index f0e79e47ca81..f62348898cec 100644 --- a/qiskit/circuit/controlflow/for_loop.py +++ b/qiskit/circuit/controlflow/for_loop.py @@ -10,16 +10,20 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"Circuit operation representing a ``for`` loop." +"""Circuit operation representing a ``for`` loop.""" + +from __future__ import annotations import warnings -from typing import Iterable, Optional, Union +from typing import Iterable, Optional, Union, TYPE_CHECKING from qiskit.circuit.parameter import Parameter from qiskit.circuit.exceptions import CircuitError -from qiskit.circuit.quantumcircuit import QuantumCircuit from .control_flow import ControlFlowOp +if TYPE_CHECKING: + from qiskit.circuit import QuantumCircuit + class ForLoopOp(ControlFlowOp): """A circuit operation which repeatedly executes a subcircuit @@ -69,6 +73,9 @@ def params(self): @params.setter def params(self, parameters): + # pylint: disable=cyclic-import + from qiskit.circuit import QuantumCircuit + indexset, loop_parameter, body = parameters if not isinstance(loop_parameter, (Parameter, type(None))): diff --git a/qiskit/circuit/controlflow/if_else.py b/qiskit/circuit/controlflow/if_else.py index 60d18cacbbc5..c06206b15871 100644 --- a/qiskit/circuit/controlflow/if_else.py +++ b/qiskit/circuit/controlflow/if_else.py @@ -14,10 +14,10 @@ from __future__ import annotations -from typing import Optional, Union, Iterable +from typing import Optional, Union, Iterable, TYPE_CHECKING import itertools -from qiskit.circuit import ClassicalRegister, Clbit, QuantumCircuit +from qiskit.circuit.classicalregister import ClassicalRegister, Clbit from qiskit.circuit.classical import expr from qiskit.circuit.instructionset import InstructionSet from qiskit.circuit.exceptions import CircuitError @@ -31,6 +31,9 @@ condition_resources, ) +if TYPE_CHECKING: + from qiskit.circuit import QuantumCircuit + # This is just an indication of what's actually meant to be the public API. __all__ = ("IfElseOp",) @@ -82,6 +85,9 @@ def __init__( false_body: QuantumCircuit | None = None, label: str | None = None, ): + # pylint: disable=cyclic-import + from qiskit.circuit import QuantumCircuit + # Type checking generally left to @params.setter, but required here for # finding num_qubits and num_clbits. if not isinstance(true_body, QuantumCircuit): @@ -103,6 +109,9 @@ def params(self): @params.setter def params(self, parameters): + # pylint: disable=cyclic-import + from qiskit.circuit import QuantumCircuit + true_body, false_body = parameters if not isinstance(true_body, QuantumCircuit): @@ -448,7 +457,7 @@ def __enter__(self): raise CircuitError("Cannot attach an 'else' to a broadcasted 'if' block.") appended = appended_instructions[0] instruction = circuit._peek_previous_instruction_in_scope() - if appended is not instruction: + if appended.operation is not instruction.operation: raise CircuitError( "The 'if' block is not the most recent instruction in the circuit." f" Expected to find: {appended!r}, but instead found: {instruction!r}." diff --git a/qiskit/circuit/controlflow/switch_case.py b/qiskit/circuit/controlflow/switch_case.py index 0f215a9bcbb8..027f2cfbfff3 100644 --- a/qiskit/circuit/controlflow/switch_case.py +++ b/qiskit/circuit/controlflow/switch_case.py @@ -17,9 +17,9 @@ __all__ = ("SwitchCaseOp", "CASE_DEFAULT") import contextlib -from typing import Union, Iterable, Any, Tuple, Optional, List, Literal +from typing import Union, Iterable, Any, Tuple, Optional, List, Literal, TYPE_CHECKING -from qiskit.circuit import ClassicalRegister, Clbit, QuantumCircuit +from qiskit.circuit.classicalregister import ClassicalRegister, Clbit from qiskit.circuit.classical import expr, types from qiskit.circuit.exceptions import CircuitError @@ -27,6 +27,9 @@ from .control_flow import ControlFlowOp from ._builder_utils import unify_circuit_resources, partition_registers, node_resources +if TYPE_CHECKING: + from qiskit.circuit import QuantumCircuit + class _DefaultCaseType: """The type of the default-case singleton. This is used instead of just having @@ -71,6 +74,9 @@ def __init__( *, label: Optional[str] = None, ): + # pylint: disable=cyclic-import + from qiskit.circuit import QuantumCircuit + if isinstance(target, expr.Expr): if target.type.kind not in (types.Uint, types.Bool): raise CircuitError( diff --git a/qiskit/circuit/controlflow/while_loop.py b/qiskit/circuit/controlflow/while_loop.py index 98fefa3ce8c2..8bd8e8d2d067 100644 --- a/qiskit/circuit/controlflow/while_loop.py +++ b/qiskit/circuit/controlflow/while_loop.py @@ -14,12 +14,17 @@ from __future__ import annotations -from qiskit.circuit import Clbit, ClassicalRegister, QuantumCircuit +from typing import TYPE_CHECKING + +from qiskit.circuit.classicalregister import Clbit, ClassicalRegister from qiskit.circuit.classical import expr from qiskit.circuit.exceptions import CircuitError from ._builder_utils import validate_condition, condition_resources from .control_flow import ControlFlowOp +if TYPE_CHECKING: + from qiskit.circuit import QuantumCircuit + class WhileLoopOp(ControlFlowOp): """A circuit operation which repeatedly executes a subcircuit (``body``) until @@ -70,6 +75,9 @@ def params(self): @params.setter def params(self, parameters): + # pylint: disable=cyclic-import + from qiskit.circuit import QuantumCircuit + (body,) = parameters if not isinstance(body, QuantumCircuit): diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index 6b5ff71cf187..9fbe77a04f82 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -215,7 +215,7 @@ def __eq__(self, other): return True def __repr__(self) -> str: - """Generates a representation of the Intruction object instance + """Generates a representation of the Instruction object instance Returns: str: A representation of the Instruction instance with the name, number of qubits, classical bits and params( if any ) @@ -523,6 +523,7 @@ def _qasmif(self, string): "provided, because the premise is wrong." ), since="0.25.0", + package_name="qiskit-terra", ) def qasm(self): """Return a default OpenQASM string for the instruction. @@ -617,12 +618,11 @@ def repeat(self, n): @property def condition_bits(self) -> List[Clbit]: """Get Clbits in condition.""" + from qiskit.circuit.controlflow import condition_resources # pylint: disable=cyclic-import + if self.condition is None: return [] - if isinstance(self.condition[0], Clbit): - return [self.condition[0]] - else: # ClassicalRegister - return list(self.condition[0]) + return list(condition_resources(self.condition).clbits) @property def name(self): diff --git a/qiskit/circuit/instructionset.py b/qiskit/circuit/instructionset.py index 2b1a3b756de6..cf3954ba4894 100644 --- a/qiskit/circuit/instructionset.py +++ b/qiskit/circuit/instructionset.py @@ -15,6 +15,8 @@ """ from __future__ import annotations + +from collections.abc import MutableSequence from typing import Callable from qiskit.circuit.exceptions import CircuitError @@ -52,7 +54,9 @@ def __init__( # pylint: disable=bad-docstring-quotes used. It may throw an error if the resource is not valid for usage. """ - self._instructions: list[CircuitInstruction] = [] + self._instructions: list[ + CircuitInstruction | (MutableSequence[CircuitInstruction], int) + ] = [] self._requester = resource_requester def __len__(self): @@ -61,7 +65,11 @@ def __len__(self): def __getitem__(self, i): """Return instruction at index""" - return self._instructions[i] + inst = self._instructions[i] + if isinstance(inst, CircuitInstruction): + return inst + data, idx = inst + return data[idx] def add(self, instruction, qargs=None, cargs=None): """Add an instruction and its context (where it is attached).""" @@ -73,10 +81,22 @@ def add(self, instruction, qargs=None, cargs=None): instruction = CircuitInstruction(instruction, tuple(qargs), tuple(cargs)) self._instructions.append(instruction) + def _add_ref(self, data: MutableSequence[CircuitInstruction], pos: int): + """Add a reference to an instruction and its context within a mutable sequence. + Updates to the instruction set will modify the specified sequence in place.""" + self._instructions.append((data, pos)) + def inverse(self): """Invert all instructions.""" for i, instruction in enumerate(self._instructions): - self._instructions[i] = instruction.replace(operation=instruction.operation.inverse()) + if isinstance(instruction, CircuitInstruction): + self._instructions[i] = instruction.replace( + operation=instruction.operation.inverse() + ) + else: + data, idx = instruction + instruction = data[idx] + data[idx] = instruction.replace(operation=instruction.operation.inverse()) return self def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "InstructionSet": @@ -132,26 +152,40 @@ def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "Instruc if self._requester is not None: classical = self._requester(classical) for instruction in self._instructions: - instruction.operation = instruction.operation.c_if(classical, val) + if isinstance(instruction, CircuitInstruction): + updated = instruction.operation.c_if(classical, val) + if updated is not instruction.operation: + raise CircuitError( + "SingletonGate instances can only be added to InstructionSet via _add_ref" + ) + else: + data, idx = instruction + instruction = data[idx] + data[idx] = instruction.replace( + operation=instruction.operation.c_if(classical, val) + ) return self # Legacy support for properties. Added in Terra 0.21 to support the internal switch in # `QuantumCircuit.data` from the 3-tuple to `CircuitInstruction`. + def _instructions_iter(self): + return (i if isinstance(i, CircuitInstruction) else i[0][i[1]] for i in self._instructions) + @property def instructions(self): """Legacy getter for the instruction components of an instruction set. This does not support mutation.""" - return [instruction.operation for instruction in self._instructions] + return [instruction.operation for instruction in self._instructions_iter()] @property def qargs(self): """Legacy getter for the qargs components of an instruction set. This does not support mutation.""" - return [list(instruction.qubits) for instruction in self._instructions] + return [list(instruction.qubits) for instruction in self._instructions_iter()] @property def cargs(self): """Legacy getter for the cargs components of an instruction set. This does not support mutation.""" - return [list(instruction.clbits) for instruction in self._instructions] + return [list(instruction.clbits) for instruction in self._instructions_iter()] diff --git a/qiskit/circuit/library/arithmetic/integer_comparator.py b/qiskit/circuit/library/arithmetic/integer_comparator.py index 699e8c221ff3..1324d512ea6f 100644 --- a/qiskit/circuit/library/arithmetic/integer_comparator.py +++ b/qiskit/circuit/library/arithmetic/integer_comparator.py @@ -14,7 +14,6 @@ """Integer Comparator.""" from __future__ import annotations -import warnings import numpy as np from qiskit.circuit import QuantumCircuit, QuantumRegister, AncillaRegister @@ -100,16 +99,6 @@ def geq(self, geq: bool) -> None: self._invalidate() self._geq = geq - @property - def num_ancilla_qubits(self): - """Deprecated. Use num_ancillas instead.""" - warnings.warn( - "The IntegerComparator.num_ancilla_qubits property is deprecated " - "as of 0.16.0. It will be removed no earlier than 3 months after the release " - "date. You should use the num_ancillas property instead." - ) - return self.num_ancillas - @property def num_state_qubits(self) -> int: """The number of qubits encoding the state for the comparison. diff --git a/qiskit/circuit/library/blueprintcircuit.py b/qiskit/circuit/library/blueprintcircuit.py index 415ae8ecd117..4244a1f2340f 100644 --- a/qiskit/circuit/library/blueprintcircuit.py +++ b/qiskit/circuit/library/blueprintcircuit.py @@ -15,6 +15,7 @@ from __future__ import annotations from abc import ABC, abstractmethod +from qiskit._accelerate.quantum_circuit import CircuitData from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister from qiskit.circuit.parametertable import ParameterTable, ParameterView @@ -32,12 +33,13 @@ class BlueprintCircuit(QuantumCircuit, ABC): def __init__(self, *regs, name: str | None = None) -> None: """Create a new blueprint circuit.""" + self._is_initialized = False super().__init__(*regs, name=name) self._qregs: list[QuantumRegister] = [] self._cregs: list[ClassicalRegister] = [] - self._qubits = [] self._qubit_indices = {} self._is_built = False + self._is_initialized = True @abstractmethod def _check_configuration(self, raise_on_failure: bool = True) -> bool: @@ -65,7 +67,7 @@ def _build(self) -> None: def _invalidate(self) -> None: """Invalidate the current circuit build.""" - self._data = [] + self._data = CircuitData(self._data.qubits, self._data.clbits) self._parameter_table = ParameterTable() self.global_phase = 0 self._is_built = False @@ -78,13 +80,19 @@ def qregs(self): @qregs.setter def qregs(self, qregs): """Set the quantum registers associated with the circuit.""" + if not self._is_initialized: + # Workaround to ignore calls from QuantumCircuit.__init__() which + # doesn't expect 'qregs' to be an overridden property! + return self._qregs = [] - self._qubits = [] self._ancillas = [] self._qubit_indices = {} + self._data = CircuitData(clbits=self._data.clbits) + self._parameter_table = ParameterTable() + self.global_phase = 0 + self._is_built = False self.add_register(*qregs) - self._invalidate() @property def data(self): @@ -114,15 +122,10 @@ def parameters(self) -> ParameterView: self._build() return super().parameters - def qasm(self, formatted=False, filename=None, encoding=None): - if not self._is_built: - self._build() - return super().qasm(formatted, filename, encoding) - - def append(self, instruction, qargs=None, cargs=None): + def _append(self, instruction, _qargs=None, _cargs=None): if not self._is_built: self._build() - return super().append(instruction, qargs, cargs) + return super()._append(instruction, _qargs, _cargs) def compose(self, other, qubits=None, clbits=None, front=False, inplace=False, wrap=False): if not self._is_built: diff --git a/qiskit/circuit/library/evolved_operator_ansatz.py b/qiskit/circuit/library/evolved_operator_ansatz.py index a3832bfb71a6..572213089451 100644 --- a/qiskit/circuit/library/evolved_operator_ansatz.py +++ b/qiskit/circuit/library/evolved_operator_ansatz.py @@ -20,7 +20,6 @@ from qiskit.circuit.parameter import Parameter from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.exceptions import QiskitError from qiskit.quantum_info import Operator, Pauli, SparsePauliOp from qiskit.synthesis.evolution import LieTrotter @@ -44,15 +43,14 @@ def __init__( ): """ Args: - operators (BaseOperator | OperatorBase | QuantumCircuit | list | None): The operators + operators (BaseOperator | QuantumCircuit | list | None): The operators to evolve. If a circuit is passed, we assume it implements an already evolved operator and thus the circuit is not evolved again. Can be a single operator (circuit) or a list of operators (and circuits). reps: The number of times to repeat the evolved operators. evolution (EvolutionBase | EvolutionSynthesis | None): A specification of which evolution synthesis to use for the - :class:`.PauliEvolutionGate`, if the operator is from :mod:`qiskit.quantum_info` - or an opflow converter object if the operator is from :mod:`qiskit.opflow`. + :class:`.PauliEvolutionGate`. Defaults to first order Trotterization. insert_barriers: Whether to insert barriers in between each evolution. name: The name of the circuit. @@ -113,13 +111,8 @@ def evolution(self): """The evolution converter used to compute the evolution. Returns: - EvolutionBase or EvolutionSynthesis: The evolution converter used to compute the evolution. + EvolutionSynthesis: The evolution converter used to compute the evolution. """ - if self._evolution is None: - # pylint: disable=cyclic-import - from qiskit.opflow import PauliTrotterEvolution - - return PauliTrotterEvolution() return self._evolution @@ -128,8 +121,7 @@ def evolution(self, evol) -> None: """Sets the evolution converter used to compute the evolution. Args: - evol (EvolutionBase | EvolutionSynthesis): An evolution synthesis object or - opflow converter object to construct the evolution. + evol (EvolutionSynthesis): An evolution synthesis object """ self._invalidate() self._evolution = evol @@ -147,7 +139,7 @@ def operators(self): def operators(self, operators=None) -> None: """Set the operators to be evolved. - operators (Optional[Union[OperatorBase, QuantumCircuit, list]): The operators to evolve. + operators (Optional[Union[QuantumCircuit, list]]): The operators to evolve. If a circuit is passed, we assume it implements an already evolved operator and thus the circuit is not evolved again. Can be a single operator (circuit) or a list of operators (and circuits). @@ -174,21 +166,10 @@ def preferred_init_points(self): return np.zeros(self.reps * len(self.operators), dtype=float) def _evolve_operator(self, operator, time): - from qiskit.opflow import OperatorBase, EvolutionBase # pylint: disable=cyclic-import from qiskit.circuit.library.hamiltonian_gate import HamiltonianGate - if isinstance(operator, OperatorBase): - if not isinstance(self.evolution, EvolutionBase): - raise QiskitError( - "If qiskit.opflow operators are evolved the evolution must be a " - f"qiskit.opflow.EvolutionBase, not a {type(self.evolution)}." - ) - - evolved = self.evolution.convert((time * operator).exp_i()) - return evolved.reduce().to_circuit() - # if the operator is specified as matrix use exact matrix exponentiation if isinstance(operator, Operator): gate = HamiltonianGate(operator, time) @@ -254,17 +235,11 @@ def _validate_prefix(parameter_prefix, operators): def _is_pauli_identity(operator): - from qiskit.opflow import PauliOp, PauliSumOp - - if isinstance(operator, PauliSumOp): - operator = operator.to_pauli_op() if isinstance(operator, SparsePauliOp): if len(operator.paulis) == 1: operator = operator.paulis[0] # check if the single Pauli is identity below else: return False - if isinstance(operator, PauliOp): - operator = operator.primitive if isinstance(operator, Pauli): return not np.any(np.logical_or(operator.x, operator.z)) return False diff --git a/qiskit/circuit/library/generalized_gates/unitary.py b/qiskit/circuit/library/generalized_gates/unitary.py index 0f98a09c5f97..3b15e83196c6 100644 --- a/qiskit/circuit/library/generalized_gates/unitary.py +++ b/qiskit/circuit/library/generalized_gates/unitary.py @@ -111,9 +111,7 @@ def __eq__(self, other): return False if self.label != other.label: return False - # Should we match unitaries as equal if they are equal - # up to global phase? - return matrix_equal(self.params[0], other.params[0], ignore_phase=True) + return matrix_equal(self.params[0], other.params[0]) def __array__(self, dtype=None): """Return matrix for the unitary.""" diff --git a/qiskit/circuit/library/hamiltonian_gate.py b/qiskit/circuit/library/hamiltonian_gate.py index eb7e511102bb..d59faf35b0a5 100644 --- a/qiskit/circuit/library/hamiltonian_gate.py +++ b/qiskit/circuit/library/hamiltonian_gate.py @@ -130,6 +130,7 @@ def _define(self): @deprecate_func( since="0.25.0", + package_name="qiskit-terra", ) def qasm(self): """Raise an error, as QASM is not defined for the HamiltonianGate.""" diff --git a/qiskit/circuit/library/overlap.py b/qiskit/circuit/library/overlap.py index 15c39435441d..ed86d8abb9a2 100644 --- a/qiskit/circuit/library/overlap.py +++ b/qiskit/circuit/library/overlap.py @@ -15,6 +15,7 @@ from qiskit.circuit import QuantumCircuit, Gate from qiskit.circuit.parametervector import ParameterVector from qiskit.circuit.exceptions import CircuitError +from qiskit.circuit import Barrier class UnitaryOverlap(QuantumCircuit): @@ -101,7 +102,7 @@ def _check_unitary(circuit): """Check a circuit is unitary by checking if all operations are of type ``Gate``.""" for instruction in circuit.data: - if not isinstance(instruction.operation, Gate): + if not isinstance(instruction.operation, (Gate, Barrier)): raise CircuitError( ( "One or more instructions cannot be converted to" diff --git a/qiskit/circuit/library/pauli_evolution.py b/qiskit/circuit/library/pauli_evolution.py index 3fa52c00569f..88f090f29a5d 100644 --- a/qiskit/circuit/library/pauli_evolution.py +++ b/qiskit/circuit/library/pauli_evolution.py @@ -50,7 +50,10 @@ class PauliEvolutionGate(Gate): from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import PauliEvolutionGate - from qiskit.opflow import I, Z, X + from qiskit.quantum_info import SparsePauliOp + + X = SparsePauliOp("X") + Z = SparsePauliOp("Z") # build the evolution gate operator = (Z ^ Z) - 0.1 * (X ^ I) @@ -86,7 +89,7 @@ def __init__( ) -> None: """ Args: - operator (Pauli | PauliOp | SparsePauliOp | PauliSumOp | list): + operator (Pauli | SparsePauliOp | list): The operator to evolve. Can also be provided as list of non-commuting operators where the elements are sums of commuting operators. For example: ``[XY + YX, ZZ + ZI + IZ, YY]``. @@ -147,22 +150,9 @@ def validate_parameter( def _to_sparse_pauli_op(operator): - """Cast the operator to a SparsePauliOp. + """Cast the operator to a SparsePauliOp.""" - For Opflow objects, return a global coefficient that must be multiplied to the evolution time. - Since this coefficient might contain unbound parameters it cannot be absorbed into the - coefficients of the SparsePauliOp. - """ - # pylint: disable=cyclic-import - from qiskit.opflow import PauliSumOp, PauliOp - - if isinstance(operator, PauliSumOp): - sparse_pauli = operator.primitive - sparse_pauli._coeffs *= operator.coeff - elif isinstance(operator, PauliOp): - sparse_pauli = SparsePauliOp(operator.primitive) - sparse_pauli._coeffs *= operator.coeff - elif isinstance(operator, Pauli): + if isinstance(operator, Pauli): sparse_pauli = SparsePauliOp(operator) elif isinstance(operator, SparsePauliOp): sparse_pauli = operator diff --git a/qiskit/circuit/library/phase_estimation.py b/qiskit/circuit/library/phase_estimation.py index 453afb4d19e7..ab23ee2218ab 100644 --- a/qiskit/circuit/library/phase_estimation.py +++ b/qiskit/circuit/library/phase_estimation.py @@ -44,7 +44,8 @@ class PhaseEstimation(QuantumCircuit): Cambridge University Press, New York, NY, USA. [3]: Qiskit - `textbook `_ + `textbook `_ """ diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py index 8fcf53f4cfcb..29329914ed1e 100644 --- a/qiskit/circuit/library/standard_gates/x.py +++ b/qiskit/circuit/library/standard_gates/x.py @@ -590,7 +590,7 @@ def _define(self): self.definition = qc - @deprecate_func(since="0.25.0") + @deprecate_func(since="0.25.0", package_name="qiskit-terra") def qasm(self): # Gross hack to override the Qiskit name with the name this gate has in Terra's version of # 'qelib1.inc'. In general, the larger exporter mechanism should know about this to do the @@ -640,7 +640,7 @@ def __init__( _singleton_lookup_key = stdlib_singleton_key(num_ctrl_qubits=3) - # seems like open controls not hapening? + # seems like open controls not happening? def _define(self): """ gate c3x a,b,c,d diff --git a/qiskit/circuit/measure.py b/qiskit/circuit/measure.py index 1da5923e953f..548aaecd81a5 100644 --- a/qiskit/circuit/measure.py +++ b/qiskit/circuit/measure.py @@ -14,17 +14,19 @@ Quantum measurement in the computational basis. """ -from qiskit.circuit.instruction import Instruction +from qiskit.circuit.singleton import SingletonInstruction, stdlib_singleton_key from qiskit.circuit.exceptions import CircuitError -class Measure(Instruction): +class Measure(SingletonInstruction): """Quantum measurement in the computational basis.""" def __init__(self, label=None, *, duration=None, unit="dt"): """Create new measurement instruction.""" super().__init__("measure", 1, 1, [], label=label, duration=duration, unit=unit) + _singleton_lookup_key = stdlib_singleton_key() + def broadcast_arguments(self, qargs, cargs): qarg = qargs[0] carg = cargs[0] diff --git a/qiskit/circuit/parameter.py b/qiskit/circuit/parameter.py index e354a708eace..42ffad5ebf0a 100644 --- a/qiskit/circuit/parameter.py +++ b/qiskit/circuit/parameter.py @@ -17,8 +17,9 @@ from uuid import uuid4, UUID +import symengine + from qiskit.circuit.exceptions import CircuitError -from qiskit.utils import optionals as _optionals from .parameterexpression import ParameterExpression @@ -75,14 +76,7 @@ def __init__( """ self._name = name self._uuid = uuid4() if uuid is None else uuid - if not _optionals.HAS_SYMENGINE: - from sympy import Symbol - - symbol = Symbol(name) - else: - import symengine - - symbol = symengine.Symbol(name) + symbol = symengine.Symbol(name) self._symbol_expr = symbol self._parameter_keys = frozenset((self._hash_key(),)) @@ -102,11 +96,7 @@ def assign(self, parameter, value): return value # This is the `super().bind` case, where we're required to return a `ParameterExpression`, # so we need to lift the given value to a symbolic expression. - if _optionals.HAS_SYMENGINE: - from symengine import sympify - else: - from sympy import sympify - return ParameterExpression({}, sympify(value)) + return ParameterExpression({}, symengine.sympify(value)) def subs(self, parameter_map: dict, allow_unknown_parameters: bool = False): """Substitute self with the corresponding parameter in ``parameter_map``.""" diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 6b88cede34e1..237d6e9d8007 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -20,9 +20,9 @@ import operator import numpy +import symengine from qiskit.circuit.exceptions import CircuitError -from qiskit.utils import optionals as _optionals # This type is redefined at the bottom to insert the full reference to "ParameterExpression", so it # can safely be used by runtime type-checkers like Sphinx. Mypy does not need this because it @@ -69,14 +69,9 @@ def _names(self) -> dict: def conjugate(self) -> "ParameterExpression": """Return the conjugate.""" - if _optionals.HAS_SYMENGINE: - import symengine - - conjugated = ParameterExpression( - self._parameter_symbols, symengine.conjugate(self._symbol_expr) - ) - else: - conjugated = ParameterExpression(self._parameter_symbols, self._symbol_expr.conjugate()) + conjugated = ParameterExpression( + self._parameter_symbols, symengine.conjugate(self._symbol_expr) + ) return conjugated def assign(self, parameter, value: ParameterValueType) -> "ParameterExpression": @@ -185,15 +180,7 @@ def subs( new_parameter_symbols = { p: s for p, s in self._parameter_symbols.items() if p not in parameter_map } - - if _optionals.HAS_SYMENGINE: - import symengine - - symbol_type = symengine.Symbol - else: - from sympy import Symbol - - symbol_type = Symbol + symbol_type = symengine.Symbol # If new_param is an expr, we'll need to construct a matching sympy expr # but with our sympy symbols instead of theirs. @@ -306,15 +293,7 @@ def gradient(self, param) -> Union["ParameterExpression", complex]: # Compute the gradient of the parameter expression w.r.t. param key = self._parameter_symbols[param] - if _optionals.HAS_SYMENGINE: - import symengine - - expr_grad = symengine.Derivative(self._symbol_expr, key) - else: - # TODO enable nth derivative - from sympy import Derivative - - expr_grad = Derivative(self._symbol_expr, key).doit() + expr_grad = symengine.Derivative(self._symbol_expr, key) # generate the new dictionary of symbols # this needs to be done since in the derivative some symbols might disappear (e.g. @@ -362,107 +341,50 @@ def __truediv__(self, other): def __rtruediv__(self, other): return self._apply_operation(operator.truediv, other, reflected=True) + def __pow__(self, other): + return self._apply_operation(pow, other) + + def __rpow__(self, other): + return self._apply_operation(pow, other, reflected=True) + def _call(self, ufunc): return ParameterExpression(self._parameter_symbols, ufunc(self._symbol_expr)) def sin(self): """Sine of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.sin) - else: - from sympy import sin as _sin - - return self._call(_sin) + return self._call(symengine.sin) def cos(self): """Cosine of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.cos) - else: - from sympy import cos as _cos - - return self._call(_cos) + return self._call(symengine.cos) def tan(self): """Tangent of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.tan) - else: - from sympy import tan as _tan - - return self._call(_tan) + return self._call(symengine.tan) def arcsin(self): """Arcsin of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.asin) - else: - from sympy import asin as _asin - - return self._call(_asin) + return self._call(symengine.asin) def arccos(self): """Arccos of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.acos) - else: - from sympy import acos as _acos - - return self._call(_acos) + return self._call(symengine.acos) def arctan(self): """Arctan of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.atan) - else: - from sympy import atan as _atan - - return self._call(_atan) + return self._call(symengine.atan) def exp(self): """Exponential of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.exp) - else: - from sympy import exp as _exp - - return self._call(_exp) + return self._call(symengine.exp) def log(self): """Logarithm of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.log) - else: - from sympy import log as _log - - return self._call(_log) + return self._call(symengine.log) def sign(self): """Sign of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.sign) - else: - from sympy import sign as _sign - - return self._call(_sign) + return self._call(symengine.sign) def __repr__(self): return f"{self.__class__.__name__}({str(self)})" @@ -494,24 +416,21 @@ def __float__(self): "ParameterExpression with unbound parameters ({}) " "cannot be cast to a float.".format(self.parameters) ) from None - try: - # In symengine, if an expression was complex at any time, its type is likely to have - # stayed "complex" even when the imaginary part symbolically (i.e. exactly) - # cancelled out. Sympy tends to more aggressively recognise these as symbolically - # real. This second attempt at a cast is a way of unifying the behaviour to the - # more expected form for our users. - cval = complex(self) - if cval.imag == 0.0: - return cval.real - except TypeError: - pass + # In symengine, if an expression was complex at any time, its type is likely to have + # stayed "complex" even when the imaginary part symbolically (i.e. exactly) + # cancelled out. Sympy tends to more aggressively recognise these as symbolically + # real. This second attempt at a cast is a way of unifying the behaviour to the + # more expected form for our users. + cval = complex(self) + if cval.imag == 0.0: + return cval.real raise TypeError("could not cast expression to float") from exc def __int__(self): try: return int(self._symbol_expr) - # TypeError is for sympy, RuntimeError for symengine - except (TypeError, RuntimeError) as exc: + # TypeError is for backwards compatibility, RuntimeError is raised by symengine + except RuntimeError as exc: if self.parameters: raise TypeError( "ParameterExpression with unbound parameters ({}) " @@ -530,14 +449,7 @@ def __deepcopy__(self, memo=None): def __abs__(self): """Absolute of a ParameterExpression""" - if _optionals.HAS_SYMENGINE: - import symengine - - return self._call(symengine.Abs) - else: - from sympy import Abs as _abs - - return self._call(_abs) + return self._call(symengine.Abs) def abs(self): """Absolute of a ParameterExpression""" @@ -555,12 +467,9 @@ def __eq__(self, other): if isinstance(other, ParameterExpression): if self.parameters != other.parameters: return False - if _optionals.HAS_SYMENGINE: - from sympy import sympify + from sympy import sympify - return sympify(self._symbol_expr).equals(sympify(other._symbol_expr)) - else: - return self._symbol_expr.equals(other._symbol_expr) + return sympify(self._symbol_expr).equals(sympify(other._symbol_expr)) elif isinstance(other, numbers.Number): return len(self.parameters) == 0 and complex(self._symbol_expr) == other return False @@ -570,7 +479,7 @@ def is_real(self): # workaround for symengine behavior that const * (0 + 1 * I) is not real # see https://github.com/symengine/symengine.py/issues/414 - if _optionals.HAS_SYMENGINE and self._symbol_expr.is_real is None: + if self._symbol_expr.is_real is None: symbol_expr = self._symbol_expr.evalf() else: symbol_expr = self._symbol_expr @@ -581,9 +490,8 @@ def is_real(self): # but the parameter will evaluate as real. Check that if the # expression's is_real attribute returns false that we have a # non-zero imaginary - if _optionals.HAS_SYMENGINE: - if symbol_expr.imag == 0.0: - return True + if symbol_expr.imag == 0.0: + return True return False return symbol_expr.is_real diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index ceaad5ecf43a..ae8d171607c8 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -16,6 +16,7 @@ from __future__ import annotations import copy +import itertools import multiprocessing as mp import warnings import typing @@ -37,17 +38,25 @@ overload, ) import numpy as np +from qiskit._accelerate.quantum_circuit import CircuitData from qiskit.exceptions import QiskitError from qiskit.utils.multiprocessing import is_main_process from qiskit.circuit.instruction import Instruction from qiskit.circuit.gate import Gate from qiskit.circuit.parameter import Parameter from qiskit.circuit.exceptions import CircuitError -from qiskit.utils import optionals as _optionals from qiskit.utils.deprecation import deprecate_func from . import _classical_resource_map from ._utils import sort_parameters -from .classical import expr +from .controlflow import ControlFlowOp +from .controlflow.builder import CircuitScopeInterface, ControlFlowBuilderBlock +from .controlflow.break_loop import BreakLoopOp, BreakLoopPlaceholder +from .controlflow.continue_loop import ContinueLoopOp, ContinueLoopPlaceholder +from .controlflow.for_loop import ForLoopOp, ForLoopContext +from .controlflow.if_else import IfElseOp, IfContext +from .controlflow.switch_case import SwitchCaseOp, SwitchContext +from .controlflow.while_loop import WhileLoopOp, WhileLoopContext +from .classical import expr, types from .parameterexpression import ParameterExpression, ParameterValueType from .quantumregister import QuantumRegister, Qubit, AncillaRegister, AncillaQubit from .classicalregister import ClassicalRegister, Clbit @@ -59,8 +68,7 @@ from .bit import Bit from .quantumcircuitdata import QuantumCircuitData, CircuitInstruction from .delay import Delay -from .measure import Measure -from .reset import Reset +from .store import Store if typing.TYPE_CHECKING: import qiskit # pylint: disable=cyclic-import @@ -138,9 +146,27 @@ class QuantumCircuit: circuit. This gets stored as free-form data in a dict in the :attr:`~qiskit.circuit.QuantumCircuit.metadata` attribute. It will not be directly used in the circuit. + inputs: any variables to declare as ``input`` runtime variables for this circuit. These + should already be existing :class:`.expr.Var` nodes that you build from somewhere else; + if you need to create the inputs as well, use :meth:`QuantumCircuit.add_input`. The + variables given in this argument will be passed directly to :meth:`add_input`. A + circuit cannot have both ``inputs`` and ``captures``. + captures: any variables that that this circuit scope should capture from a containing scope. + The variables given here will be passed directly to :meth:`add_capture`. A circuit + cannot have both ``inputs`` and ``captures``. + declarations: any variables that this circuit should declare and initialize immediately. + You can order this input so that later declarations depend on earlier ones (including + inputs or captures). If you need to depend on values that will be computed later at + runtime, use :meth:`add_var` at an appropriate point in the circuit execution. + + This argument is intended for convenient circuit initialization when you already have a + set of created variables. The variables used here will be directly passed to + :meth:`add_var`, which you can use directly if this is the first time you are creating + the variable. Raises: CircuitError: if the circuit name, if given, is not valid. + CircuitError: if both ``inputs`` and ``captures`` are given. Examples: @@ -200,6 +226,9 @@ def __init__( name: str | None = None, global_phase: ParameterValueType = 0, metadata: dict | None = None, + inputs: Iterable[expr.Var] = (), + captures: Iterable[expr.Var] = (), + declarations: Mapping[expr.Var, expr.Expr] | Iterable[Tuple[expr.Var, expr.Expr]] = (), ): if any(not isinstance(reg, (list, QuantumRegister, ClassicalRegister)) for reg in regs): # check if inputs are integers, but also allow e.g. 2.0 @@ -229,9 +258,10 @@ def __init__( self.name = name self._increment_instances() - # Data contains a list of instructions and their contexts, - # in the order they were applied. - self._data: list[CircuitInstruction] = [] + # An explicit implementation of the circuit scope builder interface used to dispatch appends + # and the like to the relevant control-flow scope. + self._builder_api = _OuterCircuitScopeInterface(self) + self._op_start_times = None # A stack to hold the instruction sets that are being built up during for-, if- and @@ -246,8 +276,6 @@ def __init__( self.qregs: list[QuantumRegister] = [] self.cregs: list[ClassicalRegister] = [] - self._qubits: list[Qubit] = [] - self._clbits: list[Clbit] = [] # Dict mapping Qubit or Clbit instances to tuple comprised of 0) the # corresponding index in circuit.{qubits,clbits} and 1) a list of @@ -256,6 +284,10 @@ def __init__( self._qubit_indices: dict[Qubit, BitLocations] = {} self._clbit_indices: dict[Clbit, BitLocations] = {} + # Data contains a list of instructions and their contexts, + # in the order they were applied. + self._data: CircuitData = CircuitData() + self._ancillas: list[AncillaQubit] = [] self._calibrations: DefaultDict[str, dict[tuple, Any]] = defaultdict(dict) self.add_register(*regs) @@ -270,6 +302,20 @@ def __init__( self._global_phase: ParameterValueType = 0 self.global_phase = global_phase + # Add classical variables. Resolve inputs and captures first because they can't depend on + # anything, but declarations might depend on them. + self._vars_input: dict[str, expr.Var] = {} + self._vars_capture: dict[str, expr.Var] = {} + self._vars_local: dict[str, expr.Var] = {} + for input_ in inputs: + self.add_input(input_) + for capture in captures: + self.add_capture(capture) + if isinstance(declarations, Mapping): + declarations = declarations.items() + for var, initial in declarations: + self.add_var(var, initial) + self.duration = None self.unit = "dt" self.metadata = {} if metadata is None else metadata @@ -386,8 +432,11 @@ def data(self, data_input: Iterable): """ # If data_input is QuantumCircuitData(self), clearing self._data # below will also empty data_input, so make a shallow copy first. - data_input = list(data_input) - self._data = [] + if isinstance(data_input, CircuitData): + data_input = data_input.copy() + else: + data_input = list(data_input) + self._data.clear() self._parameter_table = ParameterTable() if not data_input: return @@ -501,6 +550,28 @@ def __eq__(self, other) -> bool: other, copy_operations=False ) + def __deepcopy__(self, memo=None): + # This is overridden to minimize memory pressure when we don't + # actually need to pickle (i.e. the typical deepcopy case). + # Note: + # This is done here instead of in CircuitData since PyO3 + # doesn't include a native way to recursively call + # copy.deepcopy(memo). + cls = self.__class__ + result = cls.__new__(cls) + for k in self.__dict__.keys() - {"_data"}: + setattr(result, k, copy.deepcopy(self.__dict__[k], memo)) + + # Avoids pulling self._data into a Python list + # like we would when pickling. + result._data = CircuitData( + copy.deepcopy(self._data.qubits, memo), + copy.deepcopy(self._data.clbits, memo), + (i.replace(operation=copy.deepcopy(i.operation, memo)) for i in self._data), + reserve=len(self._data), + ) + return result + @classmethod def _increment_instances(cls): cls.instances += 1 @@ -864,8 +935,6 @@ def compose( lcr_1: 0 ═══════════ lcr_1: 0 ═══════════════════════ """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.switch_case import SwitchCaseOp if inplace and front and self._control_flow_scopes: # If we're composing onto ourselves while in a stateful control-flow builder context, @@ -887,6 +956,10 @@ def compose( # has to be strictly larger. This allows composing final measurements onto unitary circuits. if isinstance(other, QuantumCircuit): if not self.clbits and other.clbits: + if dest._control_flow_scopes: + raise CircuitError( + "cannot implicitly add clbits while within a control-flow scope" + ) dest.add_bits(other.clbits) for reg in other.cregs: dest.add_register(reg) @@ -905,7 +978,7 @@ def compose( clbits = self.clbits[: other.num_clbits] if front: # Need to keep a reference to the data for use after we've emptied it. - old_data = list(dest.data) + old_data = dest._data.copy() dest.clear() dest.append(other, qubits, clbits) for instruction in old_data: @@ -948,7 +1021,7 @@ def compose( variable_mapper = _classical_resource_map.VariableMapper( dest.cregs, edge_map, dest.add_register ) - mapped_instrs: list[CircuitInstruction] = [] + mapped_instrs: CircuitData = CircuitData(dest.qubits, dest.clbits, reserve=len(other.data)) for instr in other.data: n_qargs: list[Qubit] = [edge_map[qarg] for qarg in instr.qubits] n_cargs: list[Clbit] = [edge_map[carg] for carg in instr.clbits] @@ -961,11 +1034,11 @@ def compose( if front: # adjust new instrs before original ones and update all parameters - mapped_instrs += dest.data + mapped_instrs.extend(dest._data) dest.clear() - append = dest._control_flow_scopes[-1].append if dest._control_flow_scopes else dest._append + circuit_scope = dest._current_scope() for instr in mapped_instrs: - append(instr) + circuit_scope.append(instr) for gate, cals in other.calibrations.items(): dest._calibrations[gate].update(cals) @@ -982,7 +1055,7 @@ def tensor(self, other: "QuantumCircuit", inplace: bool = False) -> Optional["Qu Remember that in the little-endian convention the leftmost operation will be at the bottom of the circuit. See also - `the docs `__ + `the docs `__ for more information. .. parsed-literal:: @@ -1072,14 +1145,14 @@ def qubits(self) -> list[Qubit]: """ Returns a list of quantum bits in the order that the registers were added. """ - return self._qubits + return self._data.qubits @property def clbits(self) -> list[Clbit]: """ Returns a list of classical bits in the order that the registers were added. """ - return self._clbits + return self._data.clbits @property def ancillas(self) -> list[AncillaQubit]: @@ -1088,6 +1161,74 @@ def ancillas(self) -> list[AncillaQubit]: """ return self._ancillas + @property + def num_vars(self) -> int: + """The number of runtime classical variables in the circuit. + + This is the length of the :meth:`iter_vars` iterable.""" + return self.num_input_vars + self.num_captured_vars + self.num_declared_vars + + @property + def num_input_vars(self) -> int: + """The number of runtime classical variables in the circuit marked as circuit inputs. + + This is the length of the :meth:`iter_input_vars` iterable. If this is non-zero, + :attr:`num_captured_vars` must be zero.""" + return len(self._vars_input) + + @property + def num_captured_vars(self) -> int: + """The number of runtime classical variables in the circuit marked as captured from an + enclosing scope. + + This is the length of the :meth:`iter_captured_vars` iterable. If this is non-zero, + :attr:`num_input_vars` must be zero.""" + return len(self._vars_capture) + + @property + def num_declared_vars(self) -> int: + """The number of runtime classical variables in the circuit that are declared by this + circuit scope, excluding inputs or captures. + + This is the length of the :meth:`iter_declared_vars` iterable.""" + return len(self._vars_local) + + def iter_vars(self) -> typing.Iterable[expr.Var]: + """Get an iterable over all runtime classical variables in scope within this circuit. + + This method will iterate over all variables in scope. For more fine-grained iterators, see + :meth:`iter_declared_vars`, :meth:`iter_input_vars` and :meth:`iter_captured_vars`.""" + if self._control_flow_scopes: + builder = self._control_flow_scopes[-1] + return itertools.chain(builder.iter_captured_vars(), builder.iter_local_vars()) + return itertools.chain( + self._vars_input.values(), self._vars_capture.values(), self._vars_local.values() + ) + + def iter_declared_vars(self) -> typing.Iterable[expr.Var]: + """Get an iterable over all runtime classical variables that are declared with automatic + storage duration in this scope. This excludes input variables (see :meth:`iter_input_vars`) + and captured variables (see :meth:`iter_captured_vars`).""" + if self._control_flow_scopes: + return self._control_flow_scopes[-1].iter_local_vars() + return self._vars_local.values() + + def iter_input_vars(self) -> typing.Iterable[expr.Var]: + """Get an iterable over all runtime classical variables that are declared as inputs to this + circuit scope. This excludes locally declared variables (see :meth:`iter_declared_vars`) + and captured variables (see :meth:`iter_captured_vars`).""" + if self._control_flow_scopes: + return () + return self._vars_input.values() + + def iter_captured_vars(self) -> typing.Iterable[expr.Var]: + """Get an iterable over all runtime classical variables that are captured by this circuit + scope from a containing scope. This excludes input variables (see :meth:`iter_input_vars`) + and locally declared variables (see :meth:`iter_declared_vars`).""" + if self._control_flow_scopes: + return self._control_flow_scopes[-1].iter_captured_vars() + return self._vars_capture.values() + def __and__(self, rhs: "QuantumCircuit") -> "QuantumCircuit": """Overload & to implement self.compose.""" return self.compose(rhs) @@ -1160,56 +1301,6 @@ def cbit_argument_conversion(self, clbit_representation: ClbitSpecifier) -> list clbit_representation, self.clbits, self._clbit_indices, Clbit ) - def _resolve_classical_resource(self, specifier): - """Resolve a single classical resource specifier into a concrete resource, raising an error - if the specifier is invalid. - - This is slightly different to :meth:`.cbit_argument_conversion`, because it should not - unwrap :obj:`.ClassicalRegister` instances into lists, and in general it should not allow - iterables or broadcasting. It is expected to be used as a callback for things like - :meth:`.InstructionSet.c_if` to check the validity of their arguments. - - Args: - specifier (Union[Clbit, ClassicalRegister, int]): a specifier of a classical resource - present in this circuit. An ``int`` will be resolved into a :obj:`.Clbit` using the - same conventions as measurement operations on this circuit use. - - Returns: - Union[Clbit, ClassicalRegister]: the resolved resource. - - Raises: - CircuitError: if the resource is not present in this circuit, or if the integer index - passed is out-of-bounds. - """ - if isinstance(specifier, Clbit): - if specifier not in self._clbit_indices: - raise CircuitError(f"Clbit {specifier} is not present in this circuit.") - return specifier - if isinstance(specifier, ClassicalRegister): - # This is linear complexity for something that should be constant, but QuantumCircuit - # does not currently keep a hashmap of registers, and requires non-trivial changes to - # how it exposes its registers publically before such a map can be safely stored so it - # doesn't miss updates. (Jake, 2021-11-10). - if specifier not in self.cregs: - raise CircuitError(f"Register {specifier} is not present in this circuit.") - return specifier - if isinstance(specifier, int): - try: - return self._clbits[specifier] - except IndexError: - raise CircuitError(f"Classical bit index {specifier} is out-of-range.") from None - raise CircuitError(f"Unknown classical resource specifier: '{specifier}'.") - - def _validate_expr(self, node: expr.Expr) -> expr.Expr: - for var in expr.iter_vars(node): - if isinstance(var.var, Clbit): - if var.var not in self._clbit_indices: - raise CircuitError(f"Clbit {var.var} is not present in this circuit.") - elif isinstance(var.var, ClassicalRegister): - if var.var not in self.cregs: - raise CircuitError(f"Register {var.var} is not present in this circuit.") - return node - def append( self, instruction: Operation | CircuitInstruction, @@ -1264,37 +1355,47 @@ def append( "Object to append must be an Operation or have a to_instruction() method." ) + circuit_scope = self._current_scope() + # Make copy of parameterized gate instances - if hasattr(operation, "params"): - is_parameter = any(isinstance(param, Parameter) for param in operation.params) + if params := getattr(operation, "params", ()): + is_parameter = False + for param in params: + is_parameter = is_parameter or isinstance(param, Parameter) + if isinstance(param, expr.Expr): + param = _validate_expr(circuit_scope, param) if is_parameter: operation = copy.deepcopy(operation) + if isinstance(operation, ControlFlowOp): + # Verify that any variable bindings are valid. Control-flow ops are already enforced + # by the class not to contain 'input' variables. + if bad_captures := { + var + for var in itertools.chain.from_iterable( + block.iter_captured_vars() for block in operation.blocks + ) + if not self.has_var(var) + }: + raise CircuitError( + f"Control-flow op attempts to capture '{bad_captures}'" + " which are not in this circuit" + ) expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []] expanded_cargs = [self.cbit_argument_conversion(carg) for carg in cargs or []] - if self._control_flow_scopes: - appender = self._control_flow_scopes[-1].append - requester = self._control_flow_scopes[-1].request_classical_resource - else: - appender = self._append - requester = self._resolve_classical_resource - instructions = InstructionSet(resource_requester=requester) - if isinstance(operation, Instruction): - for qarg, carg in operation.broadcast_arguments(expanded_qargs, expanded_cargs): - self._check_dups(qarg) - instruction = CircuitInstruction(operation, qarg, carg) - appender(instruction) - instructions.add(instruction) - else: - # For Operations that are non-Instructions, we use the Instruction's default method - for qarg, carg in Instruction.broadcast_arguments( - operation, expanded_qargs, expanded_cargs - ): - self._check_dups(qarg) - instruction = CircuitInstruction(operation, qarg, carg) - appender(instruction) - instructions.add(instruction) + instructions = InstructionSet(resource_requester=circuit_scope.resolve_classical_resource) + # For Operations that are non-Instructions, we use the Instruction's default method + broadcast_iter = ( + operation.broadcast_arguments(expanded_qargs, expanded_cargs) + if isinstance(operation, Instruction) + else Instruction.broadcast_arguments(operation, expanded_qargs, expanded_cargs) + ) + for qarg, carg in broadcast_iter: + self._check_dups(qarg) + instruction = CircuitInstruction(operation, qarg, carg) + circuit_scope.append(instruction) + instructions._add_ref(circuit_scope.instructions, len(circuit_scope.instructions) - 1) return instructions # Preferred new style. @@ -1382,6 +1483,290 @@ def _update_parameter_table(self, instruction: CircuitInstruction): # clear cache if new parameter is added self._parameters = None + @typing.overload + def get_var(self, name: str, default: T) -> Union[expr.Var, T]: + ... + + # The builtin `types` module has `EllipsisType`, but only from 3.10+! + @typing.overload + def get_var(self, name: str, default: type(...) = ...) -> expr.Var: + ... + + # We use a _literal_ `Ellipsis` as the marker value to leave `None` available as a default. + def get_var(self, name: str, default: typing.Any = ...): + """Retrieve a variable that is accessible in this circuit scope by name. + + Args: + name: the name of the variable to retrieve. + default: if given, this value will be returned if the variable is not present. If it + is not given, a :exc:`KeyError` is raised instead. + + Returns: + The corresponding variable. + + Raises: + KeyError: if no default is given, but the variable does not exist. + + Examples: + Retrieve a variable by name from a circuit:: + + from qiskit.circuit import QuantumCircuit + + # Create a circuit and create a variable in it. + qc = QuantumCircuit() + my_var = qc.add_var("my_var", False) + + # We can use 'my_var' as a variable, but let's say we've lost the Python object and + # need to retrieve it. + my_var_again = qc.get_var("my_var") + + assert my_var is my_var_again + + Get a variable from a circuit by name, returning some default if it is not present:: + + assert qc.get_var("my_var", None) is my_var + assert qc.get_var("unknown_variable", None) is None + """ + if (out := self._current_scope().get_var(name)) is not None: + return out + if default is Ellipsis: + raise KeyError(f"no variable named '{name}' is present") + return default + + def has_var(self, name_or_var: str | expr.Var, /) -> bool: + """Check whether a variable is accessible in this scope. + + Args: + name_or_var: the variable, or name of a variable to check. If this is a + :class:`.expr.Var` node, the variable must be exactly the given one for this + function to return ``True``. + + Returns: + whether a matching variable is accessible. + + See also: + :meth:`QuantumCircuit.get_var`: retrieve a named variable from a circuit. + """ + if isinstance(name_or_var, str): + return self.get_var(name_or_var, None) is not None + return self.get_var(name_or_var.name, None) == name_or_var + + def _prepare_new_var( + self, name_or_var: str | expr.Var, type_: types.Type | None, / + ) -> expr.Var: + """The common logic for preparing and validating a new :class:`~.expr.Var` for the circuit. + + The given ``type_`` can be ``None`` if the variable specifier is already a :class:`.Var`, + and must be a :class:`~.types.Type` if it is a string. The argument is ignored if the given + first argument is a :class:`.Var` already. + + Returns the validated variable, which is guaranteed to be safe to add to the circuit.""" + if isinstance(name_or_var, str): + if type_ is None: + raise CircuitError("the type must be known when creating a 'Var' from a string") + var = expr.Var.new(name_or_var, type_) + else: + var = name_or_var + if not var.standalone: + raise CircuitError( + "cannot add variables that wrap `Clbit` or `ClassicalRegister` instances." + " Use `add_bits` or `add_register` as appropriate." + ) + + # The `var` is guaranteed to have a name because we already excluded the cases where it's + # wrapping a bit/register. + if (previous := self.get_var(var.name, default=None)) is not None: + if previous == var: + raise CircuitError(f"'{var}' is already present in the circuit") + raise CircuitError(f"cannot add '{var}' as its name shadows the existing '{previous}'") + return var + + def add_var(self, name_or_var: str | expr.Var, /, initial: typing.Any) -> expr.Var: + """Add a classical variable with automatic storage and scope to this circuit. + + The variable is considered to have been "declared" at the beginning of the circuit, but it + only becomes initialized at the point of the circuit that you call this method, so it can + depend on variables defined before it. + + Args: + name_or_var: either a string of the variable name, or an existing instance of + :class:`~.expr.Var` to re-use. Variables cannot shadow names that are already in + use within the circuit. + initial: the value to initialize this variable with. If the first argument was given + as a string name, the type of the resulting variable is inferred from the initial + expression; to control this more manually, either use :meth:`.Var.new` to manually + construct a new variable with the desired type, or use :func:`.expr.cast` to cast + the initializer to the desired type. + + This must be either a :class:`~.expr.Expr` node, or a value that can be lifted to + one using :class:`.expr.lift`. + + Returns: + The created variable. If a :class:`~.expr.Var` instance was given, the exact same + object will be returned. + + Raises: + CircuitError: if the variable cannot be created due to shadowing an existing variable. + + Examples: + Define a new variable given just a name and an initializer expression:: + + from qiskit.circuit import QuantumCircuit + + qc = QuantumCircuit(2) + my_var = qc.add_var("my_var", False) + + Reuse a variable that may have been taken from a related circuit, or otherwise + constructed manually, and initialize it to some more complicated expression:: + + from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister + from qiskit.circuit.classical import expr, types + + my_var = expr.Var.new("my_var", types.Uint(8)) + + cr1 = ClassicalRegister(8, "cr1") + cr2 = ClassicalRegister(8, "cr2") + qc = QuantumCircuit(QuantumRegister(8), cr1, cr2) + + # Get some measurement results into each register. + qc.h(0) + for i in range(1, 8): + qc.cx(0, i) + qc.measure(range(8), cr1) + + qc.reset(range(8)) + qc.h(0) + for i in range(1, 8): + qc.cx(0, i) + qc.measure(range(8), cr2) + + # Now when we add the variable, it is initialized using the runtime state of the two + # classical registers we measured into above. + qc.add_var(my_var, expr.bit_and(cr1, cr2)) + """ + # Validate the initialiser first to catch cases where the variable to be declared is being + # used in the initialiser. + circuit_scope = self._current_scope() + initial = _validate_expr(circuit_scope, expr.lift(initial)) + if isinstance(name_or_var, str): + var = expr.Var.new(name_or_var, initial.type) + elif not name_or_var.standalone: + raise CircuitError( + "cannot add variables that wrap `Clbit` or `ClassicalRegister` instances." + ) + else: + var = name_or_var + circuit_scope.add_uninitialized_var(var) + try: + # Store is responsible for ensuring the type safety of the initialisation. + store = Store(var, initial) + except CircuitError: + circuit_scope.remove_var(var) + raise + circuit_scope.append(CircuitInstruction(store, (), ())) + return var + + def add_uninitialized_var(self, var: expr.Var, /): + """Add a variable with no initializer. + + In most cases, you should use :meth:`add_var` to initialize the variable. To use this + function, you must already hold a :class:`~.expr.Var` instance, as the use of the function + typically only makes sense in copying contexts. + + .. warning:: + + Qiskit makes no assertions about what an uninitialized variable will evaluate to at + runtime, and some hardware may reject this as an error. + + You should treat this function with caution, and as a low-level primitive that is useful + only in special cases of programmatically rebuilding two like circuits. + + Args: + var: the variable to add. + """ + # This function is deliberately meant to be a bit harder to find, to have a long descriptive + # name, and to be a bit less ergonomic than `add_var` (i.e. not allowing the (name, type) + # overload) to discourage people from using it when they should use `add_var`. + # + # This function exists so that there is a method to emulate `copy_empty_like`'s behaviour of + # adding uninitialised variables, which there's no obvious way around. We need to be sure + # that _some_ sort of handling of uninitialised variables is taken into account in our + # structures, so that doesn't become a huge edge case, even though we make no assertions + # about the _meaning_ if such an expression was run on hardware. + if self._control_flow_scopes: + raise CircuitError("cannot add an uninitialized variable in a control-flow scope") + if not var.standalone: + raise CircuitError("cannot add a variable wrapping a bit or register to a circuit") + self._builder_api.add_uninitialized_var(var) + + def add_capture(self, var: expr.Var): + """Add a variable to the circuit that it should capture from a scope it will be contained + within. + + This method requires a :class:`~.expr.Var` node to enforce that you've got a handle to one, + because you will need to declare the same variable using the same object into the outer + circuit. + + This is a low-level method, which is only really useful if you are manually constructing + control-flow operations. You typically will not need to call this method, assuming you + are using the builder interface for control-flow scopes (``with`` context-manager statements + for :meth:`if_test` and the other scoping constructs). The builder interface will + automatically make the inner scopes closures on your behalf by capturing any variables that + are used within them. + + Args: + var: the variable to capture from an enclosing scope. + + Raises: + CircuitError: if the variable cannot be created due to shadowing an existing variable. + """ + if self._control_flow_scopes: + # Allow manual capturing. Not sure why it'd be useful, but there's a clear expected + # behaviour here. + self._control_flow_scopes[-1].use_var(var) + return + if self._vars_input: + raise CircuitError( + "circuits with input variables cannot be enclosed, so cannot be closures" + ) + self._vars_capture[var.name] = self._prepare_new_var(var, None) + + @typing.overload + def add_input(self, name_or_var: str, type_: types.Type, /) -> expr.Var: + ... + + @typing.overload + def add_input(self, name_or_var: expr.Var, type_: None = None, /) -> expr.Var: + ... + + def add_input( # pylint: disable=missing-raises-doc + self, name_or_var: str | expr.Var, type_: types.Type | None = None, / + ) -> expr.Var: + """Register a variable as an input to the circuit. + + Args: + name_or_var: either a string name, or an existing :class:`~.expr.Var` node to use as the + input variable. + type_: if the name is given as a string, then this must be a :class:`~.types.Type` to + use for the variable. If the variable is given as an existing :class:`~.expr.Var`, + then this must not be given, and will instead be read from the object itself. + + Returns: + the variable created, or the same variable as was passed in. + + Raises: + CircuitError: if the variable cannot be created due to shadowing an existing variable. + """ + if self._control_flow_scopes: + raise CircuitError("cannot add an input variable in a control-flow scope") + if self._vars_capture: + raise CircuitError("circuits to be enclosed with captures cannot have input variables") + if isinstance(name_or_var, expr.Var) and type_ is not None: + raise ValueError("cannot give an explicit type with an existing Var") + var = self._prepare_new_var(name_or_var, type_) + self._vars_input[var.name] = var + return var + def add_register(self, *regs: Register | int | Sequence[Bit]) -> None: """Add registers.""" if not regs: @@ -1431,9 +1816,9 @@ def add_register(self, *regs: Register | int | Sequence[Bit]) -> None: if bit in self._qubit_indices: self._qubit_indices[bit].registers.append((register, idx)) else: - self._qubits.append(bit) + self._data.add_qubit(bit) self._qubit_indices[bit] = BitLocations( - len(self._qubits) - 1, [(register, idx)] + len(self._data.qubits) - 1, [(register, idx)] ) elif isinstance(register, ClassicalRegister): @@ -1443,9 +1828,9 @@ def add_register(self, *regs: Register | int | Sequence[Bit]) -> None: if bit in self._clbit_indices: self._clbit_indices[bit].registers.append((register, idx)) else: - self._clbits.append(bit) + self._data.add_clbit(bit) self._clbit_indices[bit] = BitLocations( - len(self._clbits) - 1, [(register, idx)] + len(self._data.clbits) - 1, [(register, idx)] ) elif isinstance(register, list): @@ -1463,11 +1848,11 @@ def add_bits(self, bits: Iterable[Bit]) -> None: if isinstance(bit, AncillaQubit): self._ancillas.append(bit) if isinstance(bit, Qubit): - self._qubits.append(bit) - self._qubit_indices[bit] = BitLocations(len(self._qubits) - 1, []) + self._data.add_qubit(bit) + self._qubit_indices[bit] = BitLocations(len(self._data.qubits) - 1, []) elif isinstance(bit, Clbit): - self._clbits.append(bit) - self._clbit_indices[bit] = BitLocations(len(self._clbits) - 1, []) + self._data.add_clbit(bit) + self._clbit_indices[bit] = BitLocations(len(self._data.clbits) - 1, []) else: raise CircuitError( "Expected an instance of Qubit, Clbit, or " @@ -1594,65 +1979,6 @@ def decompose( # do not copy operations, this is done in the conversion with circuit_to_dag return dag_to_circuit(dag, copy_operations=False) - def qasm( - self, - formatted: bool = False, - filename: str | None = None, - encoding: str | None = None, - ) -> str | None: - """Return OpenQASM 2.0 string. - - .. seealso:: - - :func:`.qasm2.dump` and :func:`.qasm2.dumps` - The preferred entry points to the OpenQASM 2 export capabilities. These match the - interface for other serialisers in Qiskit. - - Args: - formatted (bool): Return formatted OpenQASM 2.0 string. - filename (str): Save OpenQASM 2.0 to file with name 'filename'. - encoding (str): Optionally specify the encoding to use for the - output file if ``filename`` is specified. By default this is - set to the system's default encoding (ie whatever - ``locale.getpreferredencoding()`` returns) and can be set to - any valid codec or alias from stdlib's - `codec module `__ - - Returns: - str: If formatted=False. - - Raises: - MissingOptionalLibraryError: If pygments is not installed and ``formatted`` is - ``True``. - QASM2ExportError: If circuit has free parameters. - QASM2ExportError: If an operation that has no OpenQASM 2 representation is encountered. - """ - from qiskit import qasm2 # pylint: disable=cyclic-import - - out = qasm2.dumps(self) - if filename is not None: - with open(filename, "w+", encoding=encoding) as file: - print(out, file=file) - - if formatted: - _optionals.HAS_PYGMENTS.require_now("formatted OpenQASM 2.0 output") - - import pygments - from pygments.formatters import ( # pylint: disable=no-name-in-module - Terminal256Formatter, - ) - from qiskit.qasm.pygments import OpenQASMLexer - from qiskit.qasm.pygments import QasmTerminalStyle - - code = pygments.highlight( - out, OpenQASMLexer(), Terminal256Formatter(style=QasmTerminalStyle) - ) - print(code) - return None - # The old `QuantumCircuit.qasm()` method included a terminating new line that `qasm2.dumps` - # doesn't, so for full compatibility we add it back here. - return out + "\n" - def draw( self, output: str | None = None, @@ -2096,10 +2422,11 @@ def copy(self, name: str | None = None) -> "QuantumCircuit": } ) - cpy._data = [ + cpy._data.reserve(len(self._data)) + cpy._data.extend( instruction.replace(operation=operation_copies[id(instruction.operation)]) for instruction in self._data - ] + ) return cpy @@ -2111,6 +2438,14 @@ def copy_empty_like(self, name: str | None = None) -> "QuantumCircuit": * global phase * all the qubits and clbits, including the registers + .. warning:: + + If the circuit contains any local variable declarations (those added by the + ``declarations`` argument to the circuit constructor, or using :meth:`add_var`), they + will be **uninitialized** in the output circuit. You will need to manually add store + instructions for them (see :class:`.Store` and :meth:`.QuantumCircuit.store`) to + initialize them. + Args: name (str): Name for the copied circuit. If None, then the name stays the same. @@ -2125,14 +2460,20 @@ def copy_empty_like(self, name: str | None = None) -> "QuantumCircuit": # copy registers correctly, in copy.copy they are only copied via reference cpy.qregs = self.qregs.copy() cpy.cregs = self.cregs.copy() - cpy._qubits = self._qubits.copy() + cpy._builder_api = _OuterCircuitScopeInterface(cpy) cpy._ancillas = self._ancillas.copy() - cpy._clbits = self._clbits.copy() cpy._qubit_indices = self._qubit_indices.copy() cpy._clbit_indices = self._clbit_indices.copy() + # Note that this causes the local variables to be uninitialised, because the stores are not + # copied. This can leave the circuit in a potentially dangerous state for users if they + # don't re-add initialiser stores. + cpy._vars_local = self._vars_local.copy() + cpy._vars_input = self._vars_input.copy() + cpy._vars_capture = self._vars_capture.copy() + cpy._parameter_table = ParameterTable() - cpy._data = [] + cpy._data = CircuitData(self._data.qubits, self._data.clbits) cpy._calibrations = copy.deepcopy(self._calibrations) cpy._metadata = copy.deepcopy(self._metadata) @@ -2180,8 +2521,35 @@ def reset(self, qubit: QubitSpecifier) -> InstructionSet: Returns: qiskit.circuit.InstructionSet: handle to the added instruction. """ + from .reset import Reset + return self.append(Reset(), [qubit], []) + def store(self, lvalue: typing.Any, rvalue: typing.Any, /) -> InstructionSet: + """Store the result of the given runtime classical expression ``rvalue`` in the memory + location defined by ``lvalue``. + + Typically ``lvalue`` will be a :class:`~.expr.Var` node and ``rvalue`` will be some + :class:`~.expr.Expr` to write into it, but anything that :func:`.expr.lift` can raise to an + :class:`~.expr.Expr` is permissible in both places, and it will be called on them. + + Args: + lvalue: a valid specifier for a memory location in the circuit. This will typically be + a :class:`~.expr.Var` node, but you can also write to :class:`.Clbit` or + :class:`.ClassicalRegister` memory locations if your hardware supports it. The + memory location must already be present in the circuit. + rvalue: a runtime classical expression whose result should be written into the given + memory location. + + .. seealso:: + :class:`~.circuit.Store` + The backing :class:`~.circuit.Instruction` class that represents this operation. + + :meth:`add_var` + Create a new variable in the circuit that can be written to with this method. + """ + return self.append(Store(expr.lift(lvalue), expr.lift(rvalue)), (), ()) + def measure(self, qubit: QubitSpecifier, cbit: ClbitSpecifier) -> InstructionSet: r"""Measure a quantum bit (``qubit``) in the Z basis into a classical bit (``cbit``). @@ -2255,6 +2623,8 @@ def measure(self, qubit: QubitSpecifier, cbit: ClbitSpecifier) -> InstructionSet circuit.measure(qreg[1], creg[1]) """ + from .measure import Measure + return self.append(Measure(), [qubit], [cbit]) def measure_active(self, inplace: bool = True) -> Optional["QuantumCircuit"]: @@ -2365,13 +2735,16 @@ def remove_final_measurements(self, inplace: bool = True) -> Optional["QuantumCi # Filter only cregs/clbits still in new DAG, preserving original circuit order cregs_to_add = [creg for creg in circ.cregs if creg in kept_cregs] - clbits_to_add = [clbit for clbit in circ._clbits if clbit in kept_clbits] + clbits_to_add = [clbit for clbit in circ._data.clbits if clbit in kept_clbits] # Clear cregs and clbits circ.cregs = [] - circ._clbits = [] circ._clbit_indices = {} + # Clear instruction info + circ._data = CircuitData(qubits=circ._data.qubits, reserve=len(circ._data)) + circ._parameter_table.clear() + # We must add the clbits first to preserve the original circuit # order. This way, add_register never adds clbits and just # creates registers that point to them. @@ -2379,10 +2752,6 @@ def remove_final_measurements(self, inplace: bool = True) -> Optional["QuantumCi for creg in cregs_to_add: circ.add_register(creg) - # Clear instruction info - circ.data.clear() - circ._parameter_table.clear() - # Set circ instructions to match the new DAG for node in new_dag.topological_op_nodes(): # Get arguments for classical condition (if any) @@ -2802,10 +3171,7 @@ def _unroll_param_dict( out[parameter] = value return out - @deprecate_func( - additional_msg=("Use assign_parameters() instead"), - since="0.45.0", - ) + @deprecate_func(additional_msg=("Use assign_parameters() instead"), since="0.45.0") def bind_parameters( self, values: Union[Mapping[Parameter, float], Sequence[float]] ) -> "QuantumCircuit": @@ -2853,23 +3219,12 @@ def barrier(self, *qargs: QubitSpecifier, label=None) -> InstructionSet: """ from .barrier import Barrier - qubits: list[QubitSpecifier] = [] - - if not qargs: # None - qubits.extend(self.qubits) - - for qarg in qargs: - if isinstance(qarg, QuantumRegister): - qubits.extend([qarg[j] for j in range(qarg.size)]) - elif isinstance(qarg, list): - qubits.extend(qarg) - elif isinstance(qarg, range): - qubits.extend(list(qarg)) - elif isinstance(qarg, slice): - qubits.extend(self.qubits[qarg]) - else: - qubits.append(qarg) - + qubits = ( + # This uses a `dict` not a `set` to guarantee a deterministic order to the arguments. + list({q: None for qarg in qargs for q in self.qbit_argument_conversion(qarg)}) + if qargs + else self.qubits.copy() + ) return self.append(Barrier(len(qubits), label=label), qubits, []) def delay( @@ -2910,7 +3265,9 @@ def delay( else: qubits.append(qarg) - instructions = InstructionSet(resource_requester=self._resolve_classical_resource) + instructions = InstructionSet( + resource_requester=self._current_scope().resolve_classical_resource + ) for q in qubits: inst: tuple[ Instruction, Sequence[QubitSpecifier] | None, Sequence[ClbitSpecifier] | None @@ -4800,6 +5157,11 @@ def snapshot(self, label, snapshot_type="statevector", qubits=None, params=None) return self.append(snap, qubits) + def _current_scope(self) -> CircuitScopeInterface: + if self._control_flow_scopes: + return self._control_flow_scopes[-1] + return self._builder_api + def _push_scope( self, qubits: Iterable[Qubit] = (), @@ -4820,28 +5182,18 @@ def _push_scope( forbidden_message: If given, all attempts to add instructions to this scope will raise a :exc:`.CircuitError` with this message. """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.builder import ControlFlowBuilderBlock - - # Chain resource requests so things like registers added to inner scopes via conditions are - # requested in the outer scope as well. - if self._control_flow_scopes: - resource_requester = self._control_flow_scopes[-1].request_classical_resource - else: - resource_requester = self._resolve_classical_resource - self._control_flow_scopes.append( ControlFlowBuilderBlock( qubits, clbits, - resource_requester=resource_requester, + parent=self._current_scope(), registers=registers, allow_jumps=allow_jumps, forbidden_message=forbidden_message, ) ) - def _pop_scope(self) -> "qiskit.circuit.controlflow.builder.ControlFlowBuilderBlock": + def _pop_scope(self) -> ControlFlowBuilderBlock: """Finish a scope used in the control-flow builder interface, and return it to the caller. This should only be done by the control-flow context managers, since they naturally @@ -4911,7 +5263,7 @@ def while_loop( clbits: None, *, label: str | None, - ) -> "qiskit.circuit.controlflow.while_loop.WhileLoopContext": + ) -> WhileLoopContext: ... @typing.overload @@ -4969,13 +5321,11 @@ def while_loop(self, condition, body=None, qubits=None, clbits=None, *, label=No Raises: CircuitError: if an incorrect calling convention is used. """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.while_loop import WhileLoopOp, WhileLoopContext - + circuit_scope = self._current_scope() if isinstance(condition, expr.Expr): - condition = self._validate_expr(condition) + condition = _validate_expr(circuit_scope, condition) else: - condition = (self._resolve_classical_resource(condition[0]), condition[1]) + condition = (circuit_scope.resolve_classical_resource(condition[0]), condition[1]) if body is None: if qubits is not None or clbits is not None: @@ -5001,7 +5351,7 @@ def for_loop( clbits: None, *, label: str | None, - ) -> "qiskit.circuit.controlflow.for_loop.ForLoopContext": + ) -> ForLoopContext: ... @typing.overload @@ -5069,9 +5419,6 @@ def for_loop( Raises: CircuitError: if an incorrect calling convention is used. """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.for_loop import ForLoopOp, ForLoopContext - if body is None: if qubits is not None or clbits is not None: raise CircuitError( @@ -5094,7 +5441,7 @@ def if_test( clbits: None, *, label: str | None, - ) -> "qiskit.circuit.controlflow.if_else.IfContext": + ) -> IfContext: ... @typing.overload @@ -5177,13 +5524,11 @@ def if_test( Returns: A handle to the instruction created. """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.if_else import IfElseOp, IfContext - + circuit_scope = self._current_scope() if isinstance(condition, expr.Expr): - condition = self._validate_expr(condition) + condition = _validate_expr(circuit_scope, condition) else: - condition = (self._resolve_classical_resource(condition[0]), condition[1]) + condition = (circuit_scope.resolve_classical_resource(condition[0]), condition[1]) if true_body is None: if qubits is not None or clbits is not None: @@ -5246,13 +5591,11 @@ def if_else( Returns: A handle to the instruction created. """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.if_else import IfElseOp - + circuit_scope = self._current_scope() if isinstance(condition, expr.Expr): - condition = self._validate_expr(condition) + condition = _validate_expr(circuit_scope, condition) else: - condition = (self._resolve_classical_resource(condition[0]), condition[1]) + condition = (circuit_scope.resolve_classical_resource(condition[0]), condition[1]) return self.append(IfElseOp(condition, true_body, false_body, label), qubits, clbits) @@ -5265,7 +5608,7 @@ def switch( clbits: None, *, label: Optional[str], - ) -> "qiskit.circuit.controlflow.switch_case.SwitchContext": + ) -> SwitchContext: ... @typing.overload @@ -5331,13 +5674,12 @@ def switch(self, target, cases=None, qubits=None, clbits=None, *, label=None): Raises: CircuitError: if an incorrect calling convention is used. """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.switch_case import SwitchCaseOp, SwitchContext + circuit_scope = self._current_scope() if isinstance(target, expr.Expr): - target = self._validate_expr(target) + target = _validate_expr(circuit_scope, target) else: - target = self._resolve_classical_resource(target) + target = circuit_scope.resolve_classical_resource(target) if cases is None: if qubits is not None or clbits is not None: raise CircuitError( @@ -5371,9 +5713,6 @@ def break_loop(self) -> InstructionSet: CircuitError: if this method was called within a builder context, but not contained within a loop. """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.break_loop import BreakLoopOp, BreakLoopPlaceholder - if self._control_flow_scopes: operation = BreakLoopPlaceholder() resources = operation.placeholder_resources() @@ -5401,9 +5740,6 @@ def continue_loop(self) -> InstructionSet: CircuitError: if this method was called within a builder context, but not contained within a loop. """ - # pylint: disable=cyclic-import - from qiskit.circuit.controlflow.continue_loop import ContinueLoopOp, ContinueLoopPlaceholder - if self._control_flow_scopes: operation = ContinueLoopPlaceholder() resources = operation.placeholder_resources() @@ -5564,6 +5900,78 @@ def qubit_stop_time(self, *qubits: Union[Qubit, int]) -> float: QuantumCircuit.isometry = QuantumCircuit.iso +class _OuterCircuitScopeInterface(CircuitScopeInterface): + # This is an explicit interface-fulfilling object friend of QuantumCircuit that acts as its + # implementation of the control-flow builder scope methods. + + __slots__ = ("circuit",) + + def __init__(self, circuit: QuantumCircuit): + self.circuit = circuit + + @property + def instructions(self): + return self.circuit._data + + def append(self, instruction): + # QuantumCircuit._append is semi-public, so we just call back to it. + return self.circuit._append(instruction) + + def resolve_classical_resource(self, specifier): + # This is slightly different to cbit_argument_conversion, because it should not + # unwrap :obj:`.ClassicalRegister` instances into lists, and in general it should not allow + # iterables or broadcasting. It is expected to be used as a callback for things like + # :meth:`.InstructionSet.c_if` to check the validity of their arguments. + if isinstance(specifier, Clbit): + if specifier not in self.circuit._clbit_indices: + raise CircuitError(f"Clbit {specifier} is not present in this circuit.") + return specifier + if isinstance(specifier, ClassicalRegister): + # This is linear complexity for something that should be constant, but QuantumCircuit + # does not currently keep a hashmap of registers, and requires non-trivial changes to + # how it exposes its registers publically before such a map can be safely stored so it + # doesn't miss updates. (Jake, 2021-11-10). + if specifier not in self.circuit.cregs: + raise CircuitError(f"Register {specifier} is not present in this circuit.") + return specifier + if isinstance(specifier, int): + try: + return self.circuit._data.clbits[specifier] + except IndexError: + raise CircuitError(f"Classical bit index {specifier} is out-of-range.") from None + raise CircuitError(f"Unknown classical resource specifier: '{specifier}'.") + + def add_uninitialized_var(self, var): + var = self.circuit._prepare_new_var(var, None) + self.circuit._vars_local[var.name] = var + + def remove_var(self, var): + self.circuit._vars_local.pop(var.name) + + def get_var(self, name): + if (out := self.circuit._vars_local.get(name)) is not None: + return out + if (out := self.circuit._vars_capture.get(name)) is not None: + return out + return self.circuit._vars_input.get(name) + + def use_var(self, var): + if self.get_var(var.name) != var: + raise CircuitError(f"'{var}' is not present in this circuit") + + +def _validate_expr(circuit_scope: CircuitScopeInterface, node: expr.Expr) -> expr.Expr: + # This takes the `circuit_scope` object as an argument rather than being a circuit method and + # inferring it because we may want to call this several times, and we almost invariably already + # need the interface implementation for something else anyway. + for var in set(expr.iter_vars(node)): + if var.standalone: + circuit_scope.use_var(var) + else: + circuit_scope.resolve_classical_resource(var.var) + return node + + class _ParameterBindsDict: __slots__ = ("mapping", "allowed_keys") diff --git a/qiskit/circuit/quantumcircuitdata.py b/qiskit/circuit/quantumcircuitdata.py index e1e61873bed6..9500cdf9425d 100644 --- a/qiskit/circuit/quantumcircuitdata.py +++ b/qiskit/circuit/quantumcircuitdata.py @@ -14,131 +14,15 @@ QuantumCircuit.data while maintaining the interface of a python list.""" from collections.abc import MutableSequence -from typing import Tuple, Iterable, Optional + +import qiskit._accelerate.quantum_circuit from .exceptions import CircuitError from .instruction import Instruction from .operation import Operation -from .quantumregister import Qubit -from .classicalregister import Clbit - - -class CircuitInstruction: - """A single instruction in a :class:`.QuantumCircuit`, comprised of the :attr:`operation` and - various operands. - - .. note:: - - There is some possible confusion in the names of this class, :class:`~.circuit.Instruction`, - and :class:`~.circuit.Operation`, and this class's attribute :attr:`operation`. Our - preferred terminology is by analogy to assembly languages, where an "instruction" is made up - of an "operation" and its "operands". - - Historically, :class:`~.circuit.Instruction` came first, and originally contained the qubits - it operated on and any parameters, so it was a true "instruction". Over time, - :class:`.QuantumCircuit` became responsible for tracking qubits and clbits, and the class - became better described as an "operation". Changing the name of such a core object would be - a very unpleasant API break for users, and so we have stuck with it. - - This class was created to provide a formal "instruction" context object in - :class:`.QuantumCircuit.data`, which had long been made of ad-hoc tuples. With this, and - the advent of the :class:`~.circuit.Operation` interface for adding more complex objects to - circuits, we took the opportunity to correct the historical naming. For the time being, - this leads to an awkward case where :attr:`.CircuitInstruction.operation` is often an - :class:`~.circuit.Instruction` instance (:class:`~.circuit.Instruction` implements the - :class:`.Operation` interface), but as the :class:`.Operation` interface gains more use, - this confusion will hopefully abate. - - .. warning:: - - This is a lightweight internal class and there is minimal error checking; you must respect - the type hints when using it. It is the user's responsibility to ensure that direct - mutations of the object do not invalidate the types, nor the restrictions placed on it by - its context. Typically this will mean, for example, that :attr:`qubits` must be a sequence - of distinct items, with no duplicates. - """ - - __slots__ = ("operation", "qubits", "clbits") - - operation: Operation - """The logical operation that this instruction represents an execution of.""" - qubits: Tuple[Qubit, ...] - """A sequence of the qubits that the operation is applied to.""" - clbits: Tuple[Clbit, ...] - """A sequence of the classical bits that this operation reads from or writes to.""" - - def __init__( - self, - operation: Operation, - qubits: Iterable[Qubit] = (), - clbits: Iterable[Clbit] = (), - ): - self.operation = operation - self.qubits = tuple(qubits) - self.clbits = tuple(clbits) - - def copy(self) -> "CircuitInstruction": - """Return a shallow copy of the :class:`CircuitInstruction`.""" - return self.__class__( - operation=self.operation, - qubits=self.qubits, - clbits=self.clbits, - ) - - def replace( - self, - operation: Optional[Operation] = None, - qubits: Optional[Iterable[Qubit]] = None, - clbits: Optional[Iterable[Clbit]] = None, - ) -> "CircuitInstruction": - """Return a new :class:`CircuitInstruction` with the given fields replaced.""" - return self.__class__( - operation=self.operation if operation is None else operation, - qubits=self.qubits if qubits is None else qubits, - clbits=self.clbits if clbits is None else clbits, - ) - - def __repr__(self): - return ( - f"{type(self).__name__}(" - f"operation={self.operation!r}" - f", qubits={self.qubits!r}" - f", clbits={self.clbits!r}" - ")" - ) - - def __eq__(self, other): - if isinstance(other, type(self)): - # Ordered from fastest comparisons to slowest. - return ( - self.clbits == other.clbits - and self.qubits == other.qubits - and self.operation == other.operation - ) - if isinstance(other, tuple): - return self._legacy_format() == other - return NotImplemented - # Legacy tuple-like interface support. - # - # For a best attempt at API compatibility during the transition to using this new class, we need - # the interface to behave exactly like the old 3-tuple `(inst, qargs, cargs)` if it's treated - # like that via unpacking or similar. That means that the `parameters` field is completely - # absent, and the qubits and clbits must be converted to lists. - def _legacy_format(self): - # The qubits and clbits were generally stored as lists in the old format, and various - # places assume that they will certainly be lists. - return (self.operation, list(self.qubits), list(self.clbits)) - - def __getitem__(self, key): - return self._legacy_format()[key] - - def __iter__(self): - return iter(self._legacy_format()) - - def __len__(self): - return 3 +CircuitInstruction = qiskit._accelerate.quantum_circuit.CircuitInstruction class QuantumCircuitData(MutableSequence): @@ -192,7 +76,7 @@ def _resolve_legacy_value(self, operation, qargs, cargs) -> CircuitInstruction: return CircuitInstruction(operation, tuple(qargs), tuple(cargs)) def insert(self, index, value): - self._circuit._data.insert(index, None) + self._circuit._data.insert(index, CircuitInstruction(None, (), ())) try: self[index] = value except CircuitError: @@ -209,42 +93,46 @@ def __len__(self): return len(self._circuit._data) def __cast(self, other): - return other._circuit._data if isinstance(other, QuantumCircuitData) else other + return list(other._circuit._data) if isinstance(other, QuantumCircuitData) else other def __repr__(self): - return repr(self._circuit._data) + return repr(list(self._circuit._data)) def __lt__(self, other): - return self._circuit._data < self.__cast(other) + return list(self._circuit._data) < self.__cast(other) def __le__(self, other): - return self._circuit._data <= self.__cast(other) + return list(self._circuit._data) <= self.__cast(other) def __eq__(self, other): return self._circuit._data == self.__cast(other) def __gt__(self, other): - return self._circuit._data > self.__cast(other) + return list(self._circuit._data) > self.__cast(other) def __ge__(self, other): - return self._circuit._data >= self.__cast(other) + return list(self._circuit._data) >= self.__cast(other) def __add__(self, other): - return self._circuit._data + self.__cast(other) + return list(self._circuit._data) + self.__cast(other) def __radd__(self, other): - return self.__cast(other) + self._circuit._data + return self.__cast(other) + list(self._circuit._data) def __mul__(self, n): - return self._circuit._data * n + return list(self._circuit._data) * n def __rmul__(self, n): - return n * self._circuit._data + return n * list(self._circuit._data) def sort(self, *args, **kwargs): """In-place stable sort. Accepts arguments of list.sort.""" - self._circuit._data.sort(*args, **kwargs) + data = list(self._circuit._data) + data.sort(*args, **kwargs) + self._circuit._data.clear() + self._circuit._data.reserve(len(data)) + self._circuit._data.extend(data) def copy(self): """Returns a shallow copy of instruction list.""" - return self._circuit._data.copy() + return list(self._circuit._data) diff --git a/qiskit/circuit/quantumregister.py b/qiskit/circuit/quantumregister.py index de87f73a79a3..67fe26b1b224 100644 --- a/qiskit/circuit/quantumregister.py +++ b/qiskit/circuit/quantumregister.py @@ -65,6 +65,7 @@ class QuantumRegister(Register): "provided, because the premise is wrong." ), since="0.23.0", + package_name="qiskit-terra", ) def qasm(self): """Return OPENQASM string for this register.""" diff --git a/qiskit/circuit/reset.py b/qiskit/circuit/reset.py index 3243afad4010..183004dbf877 100644 --- a/qiskit/circuit/reset.py +++ b/qiskit/circuit/reset.py @@ -14,16 +14,18 @@ Qubit reset to computational zero. """ -from qiskit.circuit.instruction import Instruction +from qiskit.circuit.singleton import SingletonInstruction, stdlib_singleton_key -class Reset(Instruction): +class Reset(SingletonInstruction): """Qubit reset.""" def __init__(self, label=None, *, duration=None, unit="dt"): """Create new reset instruction.""" super().__init__("reset", 1, 0, [], label=label, duration=duration, unit=unit) + _singleton_lookup_key = stdlib_singleton_key() + def broadcast_arguments(self, qargs, cargs): for qarg in qargs[0]: yield [qarg], [] diff --git a/qiskit/circuit/singleton.py b/qiskit/circuit/singleton.py index 26cf5163bf0b..bd689b6be103 100644 --- a/qiskit/circuit/singleton.py +++ b/qiskit/circuit/singleton.py @@ -156,7 +156,7 @@ def _singleton_lookup_key(n=1, label=None): there will be two singleton instances instantiated. One corresponds to ``n=1`` and ``label=None``, and the other to ``n=2`` and ``label="two"``. Whenever ``MySingleton`` is constructed with -arguments consistent with one of those two cases, the relavent singleton will be returned. For +arguments consistent with one of those two cases, the relevant singleton will be returned. For example:: assert MySingleton() is MySingleton(1, label=None) diff --git a/qiskit/circuit/store.py b/qiskit/circuit/store.py new file mode 100644 index 000000000000..100fe0e629b9 --- /dev/null +++ b/qiskit/circuit/store.py @@ -0,0 +1,87 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""The 'Store' operation.""" + +from __future__ import annotations + +import typing + +from .exceptions import CircuitError +from .classical import expr, types +from .instruction import Instruction + + +def _handle_equal_types(lvalue: expr.Expr, rvalue: expr.Expr, /) -> tuple[expr.Expr, expr.Expr]: + return lvalue, rvalue + + +def _handle_implicit_cast(lvalue: expr.Expr, rvalue: expr.Expr, /) -> tuple[expr.Expr, expr.Expr]: + return lvalue, expr.Cast(rvalue, lvalue.type, implicit=True) + + +def _requires_lossless_cast(lvalue: expr.Expr, rvalue: expr.Expr, /) -> typing.NoReturn: + raise CircuitError(f"an explicit cast is required from '{rvalue.type}' to '{lvalue.type}'") + + +def _requires_dangerous_cast(lvalue: expr.Expr, rvalue: expr.Expr, /) -> typing.NoReturn: + raise CircuitError( + f"an explicit cast is required from '{rvalue.type}' to '{lvalue.type}', which may be lossy" + ) + + +def _no_cast_possible(lvalue: expr.Expr, rvalue: expr.Expr) -> typing.NoReturn: + raise CircuitError(f"no cast is possible from '{rvalue.type}' to '{lvalue.type}'") + + +_HANDLE_CAST = { + types.CastKind.EQUAL: _handle_equal_types, + types.CastKind.IMPLICIT: _handle_implicit_cast, + types.CastKind.LOSSLESS: _requires_lossless_cast, + types.CastKind.DANGEROUS: _requires_dangerous_cast, + types.CastKind.NONE: _no_cast_possible, +} + + +class Store(Instruction): + """A manual storage of some classical value to a classical memory location. + + This is a low-level primitive of the classical-expression handling (similar to how + :class:`~.circuit.Measure` is a primitive for quantum measurement), and is not safe for + subclassing. It is likely to become a special-case instruction in later versions of Qiskit + circuit and compiler internal representations.""" + + def __init__(self, lvalue: expr.Expr, rvalue: expr.Expr): + if not expr.is_lvalue(lvalue): + raise CircuitError(f"'{lvalue}' is not an l-value") + + cast_kind = types.cast_kind(rvalue.type, lvalue.type) + if (handler := _HANDLE_CAST.get(cast_kind)) is None: + raise RuntimeError(f"unhandled cast kind required: {cast_kind}") + lvalue, rvalue = handler(lvalue, rvalue) + + super().__init__("store", 0, 0, [lvalue, rvalue]) + + @property + def lvalue(self): + """Get the l-value :class:`~.expr.Expr` node that is being stored to.""" + return self.params[0] + + @property + def rvalue(self): + """Get the r-value :class:`~.expr.Expr` node that is being written into the l-value.""" + return self.params[1] + + def c_if(self, classical, val): + raise NotImplementedError( + "stores cannot be conditioned with `c_if`; use a full `if_test` context instead" + ) diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index 22dfd35be3c2..a2b3971b5944 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -28,7 +28,7 @@ from qiskit.pulse import Schedule, InstructionScheduleMap from qiskit.transpiler import Layout, CouplingMap, PropertySet from qiskit.transpiler.basepasses import BasePass -from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.exceptions import TranspilerError, CircuitTooWideForTarget from qiskit.transpiler.instruction_durations import InstructionDurations, InstructionDurationsType from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager @@ -455,7 +455,7 @@ def _check_circuits_coupling_map(circuits, cmap, backend): # If coupling_map is not None or num_qubits == 1 num_qubits = len(circuit.qubits) if max_qubits is not None and (num_qubits > max_qubits): - raise TranspilerError( + raise CircuitTooWideForTarget( f"Number of qubits ({num_qubits}) in {circuit.name} " f"is greater than maximum ({max_qubits}) in the coupling_map" ) diff --git a/qiskit/converters/__init__.py b/qiskit/converters/__init__.py index afac4cd2231f..459b739ee011 100644 --- a/qiskit/converters/__init__.py +++ b/qiskit/converters/__init__.py @@ -21,7 +21,6 @@ .. autofunction:: dag_to_circuit .. autofunction:: circuit_to_instruction .. autofunction:: circuit_to_gate -.. autofunction:: ast_to_dag .. autofunction:: dagdependency_to_circuit .. autofunction:: circuit_to_dagdependency .. autofunction:: dag_to_dagdependency @@ -32,7 +31,6 @@ from .dag_to_circuit import dag_to_circuit from .circuit_to_instruction import circuit_to_instruction from .circuit_to_gate import circuit_to_gate -from .ast_to_dag import ast_to_dag from .circuit_to_dagdependency import circuit_to_dagdependency from .dagdependency_to_circuit import dagdependency_to_circuit from .dag_to_dagdependency import dag_to_dagdependency diff --git a/qiskit/converters/ast_to_dag.py b/qiskit/converters/ast_to_dag.py deleted file mode 100644 index 4cd60f56573c..000000000000 --- a/qiskit/converters/ast_to_dag.py +++ /dev/null @@ -1,418 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -AST (abstract syntax tree) to DAG (directed acyclic graph) converter. - -Acts as an OpenQASM interpreter. -""" -from collections import OrderedDict -from qiskit.dagcircuit import DAGCircuit -from qiskit.exceptions import QiskitError - -from qiskit.circuit import QuantumRegister, ClassicalRegister, Gate, QuantumCircuit -from qiskit.qasm.node.real import Real -from qiskit.circuit.measure import Measure -from qiskit.circuit.reset import Reset -from qiskit.circuit.barrier import Barrier -from qiskit.circuit.delay import Delay -from qiskit.circuit.library import standard_gates as std - - -def ast_to_dag(ast): - """Build a ``DAGCircuit`` object from an AST ``Node`` object. - - Args: - ast (Program): a Program Node of an AST (parser's output) - - Return: - DAGCircuit: the DAG representing an OpenQASM's AST - - Raises: - QiskitError: if the AST is malformed. - - Example: - .. code-block:: - - from qiskit.converters import ast_to_dag - from qiskit import qasm, QuantumCircuit, ClassicalRegister, QuantumRegister - - q = QuantumRegister(3, 'q') - c = ClassicalRegister(3, 'c') - circ = QuantumCircuit(q, c) - circ.h(q[0]) - circ.cx(q[0], q[1]) - circ.measure(q[0], c[0]) - circ.rz(0.5, q[1]).c_if(c, 2) - qasm_str = circ.qasm() - ast = qasm.Qasm(data=qasm_str).parse() - dag = ast_to_dag(ast) - """ - dag = DAGCircuit() - AstInterpreter(dag)._process_node(ast) - - return dag - - -class AstInterpreter: - """Interprets an OpenQASM by expanding subroutines and unrolling loops.""" - - standard_extension = { - "u1": std.U1Gate, - "u2": std.U2Gate, - "u3": std.U3Gate, - "u": std.UGate, - "p": std.PhaseGate, - "x": std.XGate, - "y": std.YGate, - "z": std.ZGate, - "t": std.TGate, - "tdg": std.TdgGate, - "s": std.SGate, - "sdg": std.SdgGate, - "sx": std.SXGate, - "sxdg": std.SXdgGate, - "swap": std.SwapGate, - "rx": std.RXGate, - "rxx": std.RXXGate, - "ry": std.RYGate, - "rz": std.RZGate, - "rzz": std.RZZGate, - "id": std.IGate, - "h": std.HGate, - "cx": std.CXGate, - "cy": std.CYGate, - "cz": std.CZGate, - "ch": std.CHGate, - "crx": std.CRXGate, - "cry": std.CRYGate, - "crz": std.CRZGate, - "csx": std.CSXGate, - "cu1": std.CU1Gate, - "cp": std.CPhaseGate, - "cu": std.CUGate, - "cu3": std.CU3Gate, - "ccx": std.CCXGate, - "cswap": std.CSwapGate, - "delay": Delay, - "rccx": std.RCCXGate, - "rc3x": std.RC3XGate, - "c3x": std.C3XGate, - "c3sqrtx": std.C3SXGate, - "c4x": std.C4XGate, - } - - def __init__(self, dag): - """Initialize interpreter's data.""" - # DAG object to populate - self.dag = dag - # OPENQASM version number (ignored for now) - self.version = 0.0 - # Dict of gates names and properties - self.gates = OrderedDict() - # Keeping track of conditional gates - self.condition = None - # List of dictionaries mapping local parameter ids to expression Nodes - self.arg_stack = [{}] - # List of dictionaries mapping local bit ids to global ids (name, idx) - self.bit_stack = [{}] - - def _process_bit_id(self, node): - """Process an Id or IndexedId node as a bit or register type. - - Return a list of tuples (Register,index). - """ - reg = None - - if node.name in self.dag.qregs: - reg = self.dag.qregs[node.name] - elif node.name in self.dag.cregs: - reg = self.dag.cregs[node.name] - else: - raise QiskitError( - "expected qreg or creg name:", "line=%s" % node.line, "file=%s" % node.file - ) - - if node.type == "indexed_id": - # An indexed bit or qubit - return [reg[node.index]] - elif node.type == "id": - # A qubit or qreg or creg - if not self.bit_stack[-1]: - # Global scope - return list(reg) - else: - # local scope - if node.name in self.bit_stack[-1]: - return [self.bit_stack[-1][node.name]] - raise QiskitError( - "expected local bit name:", "line=%s" % node.line, "file=%s" % node.file - ) - return None - - def _process_custom_unitary(self, node): - """Process a custom unitary node.""" - name = node.name - if node.arguments is not None: - args = self._process_node(node.arguments) - else: - args = [] - bits = [self._process_bit_id(node_element) for node_element in node.bitlist.children] - - if name in self.gates: - self._arguments(name, bits, args) - else: - raise QiskitError( - "internal error undefined gate:", "line=%s" % node.line, "file=%s" % node.file - ) - - def _process_u(self, node): - """Process a U gate node.""" - args = self._process_node(node.arguments) - bits = [self._process_bit_id(node.bitlist)] - - self._arguments("u", bits, args) - - def _arguments(self, name, bits, args): - gargs = self.gates[name]["args"] - gbits = self.gates[name]["bits"] - - maxidx = max(map(len, bits)) - for idx in range(maxidx): - self.arg_stack.append({gargs[j]: args[j] for j in range(len(gargs))}) - # Only index into register arguments. - element = [idx * x for x in [len(bits[j]) > 1 for j in range(len(bits))]] - self.bit_stack.append({gbits[j]: bits[j][element[j]] for j in range(len(gbits))}) - self._create_dag_op( - name, - [self.arg_stack[-1][s].sym() for s in gargs], - [self.bit_stack[-1][s] for s in gbits], - ) - self.arg_stack.pop() - self.bit_stack.pop() - - def _process_gate(self, node, opaque=False): - """Process a gate node. - - If opaque is True, process the node as an opaque gate node. - """ - self.gates[node.name] = {} - de_gate = self.gates[node.name] - de_gate["print"] = True # default - de_gate["opaque"] = opaque - de_gate["n_args"] = node.n_args() - de_gate["n_bits"] = node.n_bits() - if node.n_args() > 0: - de_gate["args"] = [element.name for element in node.arguments.children] - else: - de_gate["args"] = [] - de_gate["bits"] = [c.name for c in node.bitlist.children] - if node.name in self.standard_extension: - return - if opaque: - de_gate["body"] = None - else: - de_gate["body"] = node.body - - def _process_cnot(self, node): - """Process a CNOT gate node.""" - id0 = self._process_bit_id(node.children[0]) - id1 = self._process_bit_id(node.children[1]) - if not (len(id0) == len(id1) or len(id0) == 1 or len(id1) == 1): - raise QiskitError( - "internal error: qreg size mismatch", "line=%s" % node.line, "file=%s" % node.file - ) - maxidx = max([len(id0), len(id1)]) - for idx in range(maxidx): - cx_gate = std.CXGate() - if self.condition: - cx_gate = cx_gate.c_if(*self.condition) - if len(id0) > 1 and len(id1) > 1: - self.dag.apply_operation_back(cx_gate, [id0[idx], id1[idx]], [], check=False) - elif len(id0) > 1: - self.dag.apply_operation_back(cx_gate, [id0[idx], id1[0]], [], check=False) - else: - self.dag.apply_operation_back(cx_gate, [id0[0], id1[idx]], [], check=False) - - def _process_measure(self, node): - """Process a measurement node.""" - id0 = self._process_bit_id(node.children[0]) - id1 = self._process_bit_id(node.children[1]) - if len(id0) != len(id1): - raise QiskitError( - "internal error: reg size mismatch", "line=%s" % node.line, "file=%s" % node.file - ) - for idx, idy in zip(id0, id1): - meas_gate = Measure() - if self.condition: - meas_gate = meas_gate.c_if(*self.condition) - self.dag.apply_operation_back(meas_gate, [idx], [idy], check=False) - - def _process_if(self, node): - """Process an if node.""" - creg_name = node.children[0].name - creg = self.dag.cregs[creg_name] - cval = node.children[1].value - self.condition = (creg, cval) - self._process_node(node.children[2]) - self.condition = None - - def _process_children(self, node): - """Call process_node for all children of node.""" - for kid in node.children: - self._process_node(kid) - - def _process_node(self, node): - """Carry out the action associated with a node.""" - if node.type == "program": - self._process_children(node) - - elif node.type == "qreg": - qreg = QuantumRegister(node.index, node.name) - self.dag.add_qreg(qreg) - - elif node.type == "creg": - creg = ClassicalRegister(node.index, node.name) - self.dag.add_creg(creg) - - elif node.type == "id": - raise QiskitError("internal error: _process_node on id") - - elif node.type == "int": - raise QiskitError("internal error: _process_node on int") - - elif node.type == "real": - raise QiskitError("internal error: _process_node on real") - - elif node.type == "indexed_id": - raise QiskitError("internal error: _process_node on indexed_id") - - elif node.type == "id_list": - # We process id_list nodes when they are leaves of barriers. - return [self._process_bit_id(node_children) for node_children in node.children] - - elif node.type == "primary_list": - # We should only be called for a barrier. - return [self._process_bit_id(m) for m in node.children] - - elif node.type == "gate": - self._process_gate(node) - - elif node.type == "custom_unitary": - self._process_custom_unitary(node) - - elif node.type == "universal_unitary": - self._process_u(node) - - elif node.type == "cnot": - self._process_cnot(node) - - elif node.type == "expression_list": - return node.children - - elif node.type == "binop": - raise QiskitError("internal error: _process_node on binop") - - elif node.type == "prefix": - raise QiskitError("internal error: _process_node on prefix") - - elif node.type == "measure": - self._process_measure(node) - - elif node.type == "format": - self.version = node.version() - - elif node.type == "barrier": - ids = self._process_node(node.children[0]) - qubits = [] - for qubit in ids: - for j, _ in enumerate(qubit): - qubits.append(qubit[j]) - self.dag.apply_operation_back(Barrier(len(qubits)), qubits, [], check=False) - - elif node.type == "reset": - id0 = self._process_bit_id(node.children[0]) - for i, _ in enumerate(id0): - reset = Reset() - if self.condition: - reset = reset.c_if(*self.condition) - self.dag.apply_operation_back(reset, [id0[i]], [], check=False) - - elif node.type == "if": - self._process_if(node) - - elif node.type == "opaque": - self._process_gate(node, opaque=True) - - elif node.type == "external": - raise QiskitError("internal error: _process_node on external") - - else: - raise QiskitError( - "internal error: undefined node type", - node.type, - "line=%s" % node.line, - "file=%s" % node.file, - ) - return None - - def _gate_rules_to_qiskit_circuit(self, node, params): - """From a gate definition in OpenQASM, to a QuantumCircuit format.""" - rules = [] - qreg = QuantumRegister(node["n_bits"]) - bit_args = {node["bits"][i]: q for i, q in enumerate(qreg)} - exp_args = {node["args"][i]: Real(q) for i, q in enumerate(params)} - - for child_op in node["body"].children: - qparams = [] - eparams = [] - for param_list in child_op.children[1:]: - if param_list.type == "id_list": - qparams = [bit_args[param.name] for param in param_list.children] - elif param_list.type == "expression_list": - for param in param_list.children: - eparams.append(param.sym(nested_scope=[exp_args])) - op = self._create_op(child_op.name, params=eparams) - rules.append((op, qparams, [])) - circ = QuantumCircuit(qreg) - for instr, qargs, cargs in rules: - circ._append(instr, qargs, cargs) - return circ - - def _create_dag_op(self, name, params, qargs): - """ - Create a DAG node out of a parsed AST op node. - - Args: - name (str): operation name to apply to the DAG - params (list): op parameters - qargs (list(Qubit)): qubits to attach to - - Raises: - QiskitError: if encountering a non-basis opaque gate - """ - op = self._create_op(name, params) - if self.condition: - op = op.c_if(*self.condition) - self.dag.apply_operation_back(op, qargs, [], check=False) - - def _create_op(self, name, params): - if name in self.standard_extension: - op = self.standard_extension[name](*params) - elif name in self.gates: - op = Gate(name=name, num_qubits=self.gates[name]["n_bits"], params=params) - if not self.gates[name]["opaque"]: - # call a custom gate (otherwise, opaque) - op.definition = self._gate_rules_to_qiskit_circuit(self.gates[name], params=params) - else: - raise QiskitError("unknown operation for ast node name %s" % name) - return op diff --git a/qiskit/converters/circuit_to_instruction.py b/qiskit/converters/circuit_to_instruction.py index 793362281b5e..58323e07d0f6 100644 --- a/qiskit/converters/circuit_to_instruction.py +++ b/qiskit/converters/circuit_to_instruction.py @@ -100,32 +100,29 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None qubit_map = {bit: q[idx] for idx, bit in enumerate(circuit.qubits)} clbit_map = {bit: c[idx] for idx, bit in enumerate(circuit.clbits)} - definition = [ - instruction.replace( + qc = QuantumCircuit(*regs, name=out_instruction.name) + qc._data.reserve(len(target.data)) + for instruction in target._data: + rule = instruction.replace( qubits=[qubit_map[y] for y in instruction.qubits], clbits=[clbit_map[y] for y in instruction.clbits], ) - for instruction in target.data - ] - # fix condition - for rule in definition: + # fix condition condition = getattr(rule.operation, "condition", None) if condition: reg, val = condition if isinstance(reg, Clbit): - rule.operation = rule.operation.c_if(clbit_map[reg], val) + rule = rule.replace(operation=rule.operation.c_if(clbit_map[reg], val)) elif reg.size == c.size: - rule.operation = rule.operation.c_if(c, val) + rule = rule.replace(operation=rule.operation.c_if(c, val)) else: raise QiskitError( "Cannot convert condition in circuit with " "multiple classical registers to instruction" ) + qc._append(rule) - qc = QuantumCircuit(*regs, name=out_instruction.name) - for instruction in definition: - qc._append(instruction) if circuit.global_phase: qc.global_phase = circuit.global_phase diff --git a/qiskit/dagcircuit/collect_blocks.py b/qiskit/dagcircuit/collect_blocks.py index cf9adf16fe85..b15cfee71087 100644 --- a/qiskit/dagcircuit/collect_blocks.py +++ b/qiskit/dagcircuit/collect_blocks.py @@ -346,7 +346,7 @@ def collapse_to_operation(self, blocks, collapse_fn): # Additionally, find the set of classical registers used in conditions over full registers # (in such a case, we need to add that register to the block circuit, not just its clbits). - cur_clregs = [] + cur_clregs = set() for node in block: cur_qubits.update(node.qargs) @@ -355,7 +355,7 @@ def collapse_to_operation(self, blocks, collapse_fn): if cond is not None: cur_clbits.update(condition_resources(cond).clbits) if isinstance(cond[0], ClassicalRegister): - cur_clregs.append(cond[0]) + cur_clregs.add(cond[0]) # For reproducibility, order these qubits/clbits compatibly with the global order. sorted_qubits = sorted(cur_qubits, key=lambda x: global_index_map[x]) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index bf57964fcffe..c4da14078ffb 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -23,7 +23,6 @@ from collections import OrderedDict, defaultdict, deque, namedtuple import copy import math -import warnings from typing import Dict, Generator, Any, List import numpy as np @@ -657,16 +656,8 @@ def apply_operation_back(self, op, qargs=(), cargs=(), *, check=True): DAGCircuitError: if a leaf node is connected to multiple outputs """ - if qargs is None: - _warn_none_args() - qargs = () - else: - qargs = tuple(qargs) - if cargs is None: - _warn_none_args() - cargs = () - else: - cargs = tuple(cargs) + qargs = tuple(qargs) + cargs = tuple(cargs) if self._operation_may_have_bits(op): # This is the slow path; most of the time, this won't happen. @@ -710,16 +701,8 @@ def apply_operation_front(self, op, qargs=(), cargs=(), *, check=True): Raises: DAGCircuitError: if initial nodes connected to multiple out edges """ - if qargs is None: - _warn_none_args() - qargs = () - else: - qargs = tuple(qargs) - if cargs is None: - _warn_none_args() - cargs = () - else: - cargs = tuple(cargs) + qargs = tuple(qargs) + cargs = tuple(cargs) if self._operation_may_have_bits(op): # This is the slow path; most of the time, this won't happen. @@ -821,7 +804,7 @@ def compose(self, other, qubits=None, clbits=None, front=False, inplace=True): for gate, cals in other.calibrations.items(): dag._calibrations[gate].update(cals) - # Ensure that the error raised here is a `DAGCircuitError` for backwards compatiblity. + # Ensure that the error raised here is a `DAGCircuitError` for backwards compatibility. def _reject_new_register(reg): raise DAGCircuitError(f"No register with '{reg.bits}' to map this expression onto.") @@ -1204,7 +1187,7 @@ def substitute_node_with_dag(self, node, input_dag, wires=None, propagate_condit the operation within ``node`` is propagated to each node in the ``input_dag``. If ``False``, then the ``input_dag`` is assumed to faithfully implement suitable conditional logic already. This is ignored for :class:`.ControlFlowOp`\\ s (i.e. - treated as if it is ``False``); replacements of those must already fulfil the same + treated as if it is ``False``); replacements of those must already fulfill the same conditional logic or this function would be close to useless for them. Returns: @@ -1917,10 +1900,10 @@ def filter_fn(node): isinstance(node, DAGOpNode) and len(node.qargs) == 1 and len(node.cargs) == 0 - and getattr(node.op, "condition", None) is None - and not node.op.is_parameterized() and isinstance(node.op, Gate) and hasattr(node.op, "__array__") + and getattr(node.op, "condition", None) is None + and not node.op.is_parameterized() ) return rx.collect_runs(self._multi_graph, filter_fn) @@ -2099,8 +2082,12 @@ def draw(self, scale=0.7, filename=None, style="color"): """ Draws the dag circuit. - This function needs `pydot `_, which in turn needs - `Graphviz `_ to be installed. + This function needs `Graphviz `_ to be + installed. Graphviz is not a python package and can't be pip installed + (the ``graphviz`` package on PyPI is a Python interface library for + Graphviz and does not actually install Graphviz). You can refer to + `the Graphviz documentation `__ on + how to install it. Args: scale (float): scaling factor @@ -2116,12 +2103,3 @@ def draw(self, scale=0.7, filename=None, style="color"): from qiskit.visualization.dag_visualization import dag_drawer return dag_drawer(dag=self, scale=scale, filename=filename, style=style) - - -def _warn_none_args(): - warnings.warn( - "Passing 'None' as the qubits or clbits of an operation to 'DAGCircuit' methods" - " is deprecated since Qiskit 0.45 and will be removed in Qiskit 1.0. Instead, pass '()'.", - DeprecationWarning, - stacklevel=3, - ) diff --git a/qiskit/opflow/__init__.py b/qiskit/opflow/__init__.py deleted file mode 100644 index babed3da26b5..000000000000 --- a/qiskit/opflow/__init__.py +++ /dev/null @@ -1,332 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -r""" -================================ -Operators (:mod:`qiskit.opflow`) -================================ - -.. currentmodule:: qiskit.opflow - -.. deprecated:: 0.24.0 - - The :mod:`qiskit.opflow` module is deprecated and will be removed no earlier - than 3 months after the release date. For code migration guidelines, - visit https://qisk.it/opflow_migration. - -Operators and State functions are the building blocks of Quantum Algorithms. - -A library for Quantum Algorithms & Applications is more than a collection of -algorithms wrapped in Python functions. It needs to provide tools to make writing -algorithms simple and easy. This is the layer of modules between the circuits and algorithms, -providing the language and computational primitives for QA&A research. - -We call this layer the Operator Flow. It works by unifying computation with theory -through the common language of functions and operators, in a way which preserves physical -intuition and programming freedom. In the Operator Flow, we construct functions over binary -variables, manipulate those functions with operators, and evaluate properties of these functions -with measurements. - -The Operator Flow is meant to serve as a lingua franca between the theory and implementation -of Quantum Algorithms & Applications. Meaning, the ultimate goal is that when theorists speak -their theory in the Operator Flow, they are speaking valid implementation, and when the engineers -speak their implementation in the Operator Flow, they are speaking valid physical formalism. To -be successful, it must be fast and physically formal enough for theorists to find it easier and -more natural than hacking Matlab or NumPy, and the engineers must find it straightforward enough -that they can learn it as a typical software library, and learn the physics naturally and -effortlessly as they learn the code. There can never be a point where we say "below this level -this is all hacked out, don't come down here, stay in the interface layer above." It all must -be clear and learnable. - -Before getting into the details of the code, it's important to note that three mathematical -concepts unpin the Operator Flow. We derive most of the inspiration for the code structure from -`John Watrous's formalism `__ (but do not follow it exactly), -so it may be worthwhile to review Chapters I and II, which are free online, if you feel the -concepts are not clicking. - -1. An n-qubit State function is a complex function over n binary variables, which we will -often refer to as *n-qubit binary strings*. For example, the traditional quantum "zero state" is -a 1-qubit state function, with a definition of f(0) = 1 and f(1) = 0. - -2. An n-qubit Operator is a linear function taking n-qubit state functions to n-qubit state -functions. For example, the Pauli X Operator is defined by f(Zero) = One and f(One) = Zero. -Equivalently, an Operator can be defined as a complex function over two n-qubit binary strings, -and it is sometimes convenient to picture things this way. By this definition, our Pauli X can -be defined by its typical matrix elements, f(0, 0) = 0, f(1, 0) = 1, f(0, 1) = 1, -f(1, 1) = 0. - -3. An n-qubit Measurement is a functional taking n-qubit State functions to complex values. -For example, a Pauli Z Measurement can be defined by f(Zero) = 0 and f(One) = 1. - -.. note:: - - While every effort has been made to make programming the Operator Flow similar to mathematical - notation, in some places our hands are tied by the design of Python. In particular, when using - mathematical operators such as ``+`` and ``^`` (tensor product), beware that these follow - `Python operator precedence rules - `__. For example, - ``I^X + X^I`` will actually be interpreted as ``I ^ (X+X) ^ I == 2 * I^X^I``. In these cases, - you should use extra parentheses, like ``(I ^ X) + (X ^ I)``, or use the relevant method calls. - -Below, you'll find a base class for all Operators, some convenience immutable global variables -which simplify Operator construction, and two groups of submodules: Operators and Converters. - -Operator Base Class -=================== - -The OperatorBase serves as the base class for all Operators, State functions -and measurements, and enforces the presence and consistency of methods to manipulate these -objects conveniently. - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - OperatorBase - -.. _operator_globals: - -Operator Globals -================ - -The :mod:`operator_globals` is a set of immutable Operator instances that are -convenient building blocks to reach for while working with the Operator flow. - -One qubit Pauli operators: - :attr:`X`, :attr:`Y`, :attr:`Z`, :attr:`I` - -Clifford+T, and some other common non-parameterized gates: - :attr:`CX`, :attr:`S`, :attr:`H`, :attr:`T`, :attr:`Swap`, :attr:`CZ` - -One qubit states: - :attr:`Zero`, :attr:`One`, :attr:`Plus`, :attr:`Minus` - -Submodules -========== - -Operators ---------- - -The Operators submodules include the PrimitiveOp, ListOp, and StateFn class -groups which represent the primary Operator modules. - -.. autosummary:: - :toctree: ../stubs/ - - primitive_ops - list_ops - state_fns - - -Converters ----------- - -The Converter submodules include objects which manipulate Operators, -usually recursing over an Operator structure and changing certain Operators' representation. -For example, the :class:`~.expectations.PauliExpectation` traverses an Operator structure, and -replaces all of the :class:`~.state_fns.OperatorStateFn` measurements containing non-diagonal -Pauli terms into diagonalizing circuits following by :class:`~.state_fns.OperatorStateFn` -measurement containing only diagonal Paulis. - -.. autosummary:: - :toctree: ../stubs/ - - converters - evolutions - expectations - gradients - - -Utility functions -================= - -.. autofunction:: commutator -.. autofunction:: anti_commutator -.. autofunction:: double_commutator - - -Exceptions -========== - -.. autoexception:: OpflowError -""" -import warnings - -# New Operators -from .operator_base import OperatorBase -from .primitive_ops import ( - PrimitiveOp, - PauliOp, - MatrixOp, - CircuitOp, - PauliSumOp, - TaperedPauliSumOp, - Z2Symmetries, -) -from .state_fns import ( - StateFn, - DictStateFn, - VectorStateFn, - CVaRMeasurement, - CircuitStateFn, - OperatorStateFn, - SparseVectorStateFn, -) -from .list_ops import ListOp, SummedOp, ComposedOp, TensoredOp -from .converters import ( - ConverterBase, - CircuitSampler, - PauliBasisChange, - DictToCircuitSum, - AbelianGrouper, - TwoQubitReduction, -) -from .expectations import ( - ExpectationBase, - ExpectationFactory, - PauliExpectation, - MatrixExpectation, - AerPauliExpectation, - CVaRExpectation, -) -from .evolutions import ( - EvolutionBase, - EvolutionFactory, - EvolvedOp, - PauliTrotterEvolution, - MatrixEvolution, - TrotterizationBase, - TrotterizationFactory, - Trotter, - Suzuki, - QDrift, -) -from .utils import commutator, anti_commutator, double_commutator - -# Convenience immutable instances -from .operator_globals import ( - EVAL_SIG_DIGITS, - X, - Y, - Z, - I, - CX, - S, - H, - T, - Swap, - CZ, - Zero, - One, - Plus, - Minus, -) - -# Gradients -from .gradients import ( - DerivativeBase, - GradientBase, - Gradient, - NaturalGradient, - HessianBase, - Hessian, - QFIBase, - QFI, - CircuitGradient, - CircuitQFI, -) - -# Exceptions -from .exceptions import OpflowError - -__all__ = [ - # Operators - "OperatorBase", - "PrimitiveOp", - "PauliOp", - "MatrixOp", - "CircuitOp", - "PauliSumOp", - "TaperedPauliSumOp", - "StateFn", - "DictStateFn", - "VectorStateFn", - "CircuitStateFn", - "OperatorStateFn", - "SparseVectorStateFn", - "CVaRMeasurement", - "ListOp", - "SummedOp", - "ComposedOp", - "TensoredOp", - # Converters - "ConverterBase", - "CircuitSampler", - "AbelianGrouper", - "DictToCircuitSum", - "PauliBasisChange", - "ExpectationBase", - "ExpectationFactory", - "PauliExpectation", - "MatrixExpectation", - "AerPauliExpectation", - "CVaRExpectation", - "EvolutionBase", - "EvolvedOp", - "EvolutionFactory", - "PauliTrotterEvolution", - "MatrixEvolution", - "TrotterizationBase", - "TrotterizationFactory", - "Trotter", - "Suzuki", - "QDrift", - "TwoQubitReduction", - "Z2Symmetries", - # Convenience immutable instances - "X", - "Y", - "Z", - "I", - "CX", - "S", - "H", - "T", - "Swap", - "CZ", - "Zero", - "One", - "Plus", - "Minus", - # Gradients - "DerivativeBase", - "GradientBase", - "Gradient", - "NaturalGradient", - "HessianBase", - "Hessian", - "QFIBase", - "QFI", - "OpflowError", - # utils - "commutator", - "anti_commutator", - "double_commutator", -] - -warnings.warn( - "The ``qiskit.opflow`` module is deprecated as of qiskit-terra 0.24.0. " - "It will be removed no earlier than 3 months after the release date. " - "For code migration guidelines, visit https://qisk.it/opflow_migration.", - category=DeprecationWarning, - stacklevel=2, -) diff --git a/qiskit/opflow/converters/__init__.py b/qiskit/opflow/converters/__init__.py deleted file mode 100644 index 8c2236eaec77..000000000000 --- a/qiskit/opflow/converters/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Converters (:mod:`qiskit.opflow.converters`) -============================================ - -.. currentmodule:: qiskit.opflow.converters - -.. deprecated:: 0.24.0 - - The :mod:`qiskit.opflow` module is deprecated and will be removed no earlier - than 3 months after the release date. For code migration guidelines, - visit https://qisk.it/opflow_migration. - -Converters are objects which manipulate Operators, usually traversing an Operator to -change certain sub-Operators into a desired representation. Often the converted Operator is -isomorphic or approximate to the original Operator in some way, but not always. For example, -a converter may accept :class:`~qiskit.opflow.primitive_ops.CircuitOp` and return a -:class:`~qiskit.opflow.list_ops.SummedOp` of -:class:`~qiskit.opflow.primitive_ops.PauliOp`'s representing the -circuit unitary. Converters may not have polynomial space or time scaling in their operations. -On the contrary, many converters, such as a -:class:`~qiskit.opflow.expectations.MatrixExpectation` or -:class:`~qiskit.opflow.evolutions.MatrixEvolution`, -which convert :class:`~qiskit.opflow.primitive_ops.PauliOp`'s to -:class:`~qiskit.opflow.primitive_ops.MatrixOp`'s internally, will require time or space -exponential in the number of qubits unless a clever trick is known -(such as the use of sparse matrices). - - -Note: - Not all converters are in this module, as :mod:`~qiskit.opflow.expectations` - and :mod:`~qiskit.opflow.evolutions` are also converters. - -Converter Base Class --------------------- -The converter base class simply enforces the presence of a :meth:`~ConverterBase.convert` method. - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - ConverterBase - -Converters ----------- -In addition to the base class, directory holds a few miscellaneous converters which are used -frequently around the Operator flow. - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - CircuitSampler - AbelianGrouper - DictToCircuitSum - PauliBasisChange - TwoQubitReduction -""" - -from .converter_base import ConverterBase -from .circuit_sampler import CircuitSampler -from .pauli_basis_change import PauliBasisChange -from .dict_to_circuit_sum import DictToCircuitSum -from .abelian_grouper import AbelianGrouper -from .two_qubit_reduction import TwoQubitReduction - -__all__ = [ - "ConverterBase", - "CircuitSampler", - "PauliBasisChange", - "DictToCircuitSum", - "AbelianGrouper", - "TwoQubitReduction", -] diff --git a/qiskit/opflow/converters/abelian_grouper.py b/qiskit/opflow/converters/abelian_grouper.py deleted file mode 100644 index fa1d1842a8ed..000000000000 --- a/qiskit/opflow/converters/abelian_grouper.py +++ /dev/null @@ -1,164 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""AbelianGrouper Class""" - -from collections import defaultdict -from typing import List, Tuple, Union, cast - -import numpy as np -import rustworkx as rx - -from qiskit.opflow.converters.converter_base import ConverterBase -from qiskit.opflow.evolutions.evolved_op import EvolvedOp -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.state_fns.operator_state_fn import OperatorStateFn -from qiskit.utils.deprecation import deprecate_func - - -class AbelianGrouper(ConverterBase): - """Deprecated: The AbelianGrouper converts SummedOps into a sum of Abelian sums. - - Meaning, it will traverse the Operator, and when it finds a SummedOp, it will evaluate which of - the summed sub-Operators commute with one another. It will then convert each of the groups of - commuting Operators into their own SummedOps, and return the sum-of-commuting-SummedOps. - This is particularly useful for cases where mutually commuting groups can be handled - similarly, as in the case of Pauli Expectations, where commuting Paulis have the same - diagonalizing circuit rotation, or Pauli Evolutions, where commuting Paulis can be - diagonalized together. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, traverse: bool = True) -> None: - """ - Args: - traverse: Whether to convert only the Operator passed to ``convert``, or traverse - down that Operator. - """ - super().__init__() - self._traverse = traverse - - def convert(self, operator: OperatorBase) -> OperatorBase: - """Check if operator is a SummedOp, in which case covert it into a sum of mutually - commuting sums, or if the Operator contains sub-Operators and ``traverse`` is True, - attempt to convert any sub-Operators. - - Args: - operator: The Operator to attempt to convert. - - Returns: - The converted Operator. - """ - if isinstance(operator, PauliSumOp): - return self.group_subops(operator) - - if isinstance(operator, ListOp): - if isinstance(operator, SummedOp) and all( - isinstance(op, PauliOp) for op in operator.oplist - ): - # For now, we only support graphs over Paulis. - return self.group_subops(operator) - elif self._traverse: - return operator.traverse(self.convert) - elif isinstance(operator, OperatorStateFn) and self._traverse: - return OperatorStateFn( - self.convert(operator.primitive), - is_measurement=operator.is_measurement, - coeff=operator.coeff, - ) - elif isinstance(operator, EvolvedOp) and self._traverse: - return EvolvedOp(self.convert(operator.primitive), coeff=operator.coeff) - return operator - - @classmethod - def group_subops(cls, list_op: Union[ListOp, PauliSumOp]) -> ListOp: - """Given a ListOp, attempt to group into Abelian ListOps of the same type. - - Args: - list_op: The Operator to group into Abelian groups - - Returns: - The grouped Operator. - - Raises: - OpflowError: If any of list_op's sub-ops is not ``PauliOp``. - """ - if isinstance(list_op, ListOp): - for op in list_op.oplist: - if not isinstance(op, PauliOp): - raise OpflowError( - "Cannot determine Abelian groups if any Operator in list_op is not " - f"`PauliOp`. E.g., {op} ({type(op)})" - ) - - edges = cls._anti_commutation_graph(list_op) - nodes = range(len(list_op)) - - graph = rx.PyGraph() - graph.add_nodes_from(nodes) - graph.add_edges_from_no_data(edges) - # Keys in coloring_dict are nodes, values are colors - coloring_dict = rx.graph_greedy_color(graph) - groups = defaultdict(list) - for idx, color in coloring_dict.items(): - groups[color].append(idx) - - if isinstance(list_op, PauliSumOp): - primitive = list_op.primitive - return SummedOp( - [PauliSumOp(primitive[group], grouping_type="TPB") for group in groups.values()], - coeff=list_op.coeff, - ) - - group_ops: List[ListOp] = [ - list_op.__class__([list_op[idx] for idx in group], abelian=True) - for group in groups.values() - ] - if len(group_ops) == 1: - return group_ops[0].mul(list_op.coeff) - return list_op.__class__(group_ops, coeff=list_op.coeff) - - @staticmethod - def _anti_commutation_graph(ops: Union[ListOp, PauliSumOp]) -> List[Tuple[int, int]]: - """Create edges (i, j) if i and j are not commutable. - - Note: - This method is applicable to only PauliOps. - - Args: - ops: operators - - Returns: - A list of pairs of indices of the operators that are not commutable - """ - # convert a Pauli operator into int vector where {I: 0, X: 2, Y: 3, Z: 1} - if isinstance(ops, PauliSumOp): - mat1 = np.array( - [op.primitive.paulis.z[0] + 2 * op.primitive.paulis.x[0] for op in ops], - dtype=np.int8, - ) - else: - mat1 = np.array([op.primitive.z + 2 * op.primitive.x for op in ops], dtype=np.int8) - - mat2 = mat1[:, None] - # mat3[i, j] is True if i and j are commutable with TPB - mat3 = (((mat1 * mat2) * (mat1 - mat2)) == 0).all(axis=2) - # return [(i, j) if mat3[i, j] is False and i < j] - return cast(List[Tuple[int, int]], list(zip(*np.where(np.triu(np.logical_not(mat3), k=1))))) diff --git a/qiskit/opflow/converters/circuit_sampler.py b/qiskit/opflow/converters/circuit_sampler.py deleted file mode 100644 index e8e5938617e7..000000000000 --- a/qiskit/opflow/converters/circuit_sampler.py +++ /dev/null @@ -1,468 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""CircuitSampler Class""" - - -import logging -from functools import partial -from time import time -from typing import Any, Dict, List, Optional, Tuple, Union, cast - -import numpy as np - -from qiskit import QiskitError -from qiskit.circuit import Parameter, ParameterExpression, QuantumCircuit -from qiskit.opflow.converters.converter_base import ConverterBase -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.state_fns.circuit_state_fn import CircuitStateFn -from qiskit.opflow.state_fns.dict_state_fn import DictStateFn -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.providers import Backend -from qiskit.utils.backend_utils import is_aer_provider, is_statevector_backend -from qiskit.utils.quantum_instance import QuantumInstance -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - - -class CircuitSampler(ConverterBase): - """ - Deprecated: The CircuitSampler traverses an Operator and converts any CircuitStateFns into - approximations of the state function by a DictStateFn or VectorStateFn using a quantum - backend. Note that in order to approximate the value of the CircuitStateFn, it must 1) send - state function through a depolarizing channel, which will destroy all phase information and - 2) replace the sampled frequencies with **square roots** of the frequency, rather than the raw - probability of sampling (which would be the equivalent of sampling the **square** of the - state function, per the Born rule. - - The CircuitSampler aggressively caches transpiled circuits to handle re-parameterization of - the same circuit efficiently. If you are converting multiple different Operators, - you are better off using a different CircuitSampler for each Operator to avoid cache thrashing. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - backend: Union[Backend, QuantumInstance], - statevector: Optional[bool] = None, - param_qobj: bool = False, - attach_results: bool = False, - caching: str = "last", - ) -> None: - """ - Args: - backend: The quantum backend or QuantumInstance to use to sample the circuits. - statevector: If backend is a statevector backend, whether to replace the - CircuitStateFns with DictStateFns (from the counts) or VectorStateFns (from the - statevector). ``None`` will set this argument automatically based on the backend. - attach_results: Whether to attach the data from the backend ``Results`` object for - a given ``CircuitStateFn``` to an ``execution_results`` field added the converted - ``DictStateFn`` or ``VectorStateFn``. - param_qobj: Whether to use Aer's parameterized Qobj capability to avoid re-assembling - the circuits. - caching: The caching strategy. Can be `'last'` (default) to store the last operator - that was converted, set to `'all'` to cache all processed operators. - - Raises: - ValueError: Set statevector or param_qobj True when not supported by backend. - """ - super().__init__() - - self._quantum_instance = ( - backend if isinstance(backend, QuantumInstance) else QuantumInstance(backend=backend) - ) - self._statevector = ( - statevector if statevector is not None else self.quantum_instance.is_statevector - ) - self._param_qobj = param_qobj - self._attach_results = attach_results - - self._check_quantum_instance_and_modes_consistent() - - # Object state variables - self._caching = caching - self._cached_ops: Dict[int, OperatorCache] = {} - - self._last_op: Optional[OperatorBase] = None - self._reduced_op_cache = None - self._circuit_ops_cache: Dict[int, CircuitStateFn] = {} - self._transpiled_circ_cache: Optional[List[Any]] = None - self._transpiled_circ_templates: Optional[List[Any]] = None - self._transpile_before_bind = True - - def _check_quantum_instance_and_modes_consistent(self) -> None: - """Checks whether the statevector and param_qobj settings are compatible with the - backend - - Raises: - ValueError: statevector or param_qobj are True when not supported by backend. - """ - if self._statevector and not is_statevector_backend(self.quantum_instance.backend): - raise ValueError( - "Statevector mode for circuit sampling requires statevector " - "backend, not {}.".format(self.quantum_instance.backend) - ) - - if self._param_qobj and not is_aer_provider(self.quantum_instance.backend): - raise ValueError( - "Parameterized Qobj mode requires Aer " - "backend, not {}.".format(self.quantum_instance.backend) - ) - - @property - def quantum_instance(self) -> QuantumInstance: - """Returns the quantum instance. - - Returns: - The QuantumInstance used by the CircuitSampler - """ - return self._quantum_instance - - @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, Backend]) -> None: - """Sets the QuantumInstance. - - Raises: - ValueError: statevector or param_qobj are True when not supported by backend. - """ - if isinstance(quantum_instance, Backend): - quantum_instance = QuantumInstance(quantum_instance) - self._quantum_instance = quantum_instance - self._check_quantum_instance_and_modes_consistent() - - def convert( - self, - operator: OperatorBase, - params: Optional[Dict[Parameter, Union[float, List[float], List[List[float]]]]] = None, - ) -> OperatorBase: - r""" - Converts the Operator to one in which the CircuitStateFns are replaced by - DictStateFns or VectorStateFns. Extracts the CircuitStateFns out of the Operator, - caches them, calls ``sample_circuits`` below to get their converted replacements, - and replaces the CircuitStateFns in operator with the replacement StateFns. - - Args: - operator: The Operator to convert - params: A dictionary mapping parameters to either single binding values or lists of - binding values. - - Returns: - The converted Operator with CircuitStateFns replaced by DictStateFns or VectorStateFns. - Raises: - OpflowError: if extracted circuits are empty. - """ - # check if the operator should be cached - op_id = operator.instance_id - # op_id = id(operator) - if op_id not in self._cached_ops.keys(): - # delete cache if we only want to cache one operator - if self._caching == "last": - self.clear_cache() - - # convert to circuit and reduce - operator_dicts_replaced = operator.to_circuit_op() - self._reduced_op_cache = operator_dicts_replaced.reduce() - - # extract circuits - self._circuit_ops_cache = {} - self._extract_circuitstatefns(self._reduced_op_cache) - if not self._circuit_ops_cache: - raise OpflowError( - "Circuits are empty. " - "Check that the operator is an instance of CircuitStateFn or its ListOp." - ) - self._transpiled_circ_cache = None - self._transpile_before_bind = True - else: - # load the cached circuits - self._reduced_op_cache = self._cached_ops[op_id].reduced_op_cache - self._circuit_ops_cache = self._cached_ops[op_id].circuit_ops_cache - self._transpiled_circ_cache = self._cached_ops[op_id].transpiled_circ_cache - self._transpile_before_bind = self._cached_ops[op_id].transpile_before_bind - self._transpiled_circ_templates = self._cached_ops[op_id].transpiled_circ_templates - - return_as_list = False - if params is not None and len(params.keys()) > 0: - p_0 = list(params.values())[0] - if isinstance(p_0, (list, np.ndarray)): - num_parameterizations = len(p_0) - param_bindings = [ - {param: value_list[i] for param, value_list in params.items()} # type: ignore - for i in range(num_parameterizations) - ] - return_as_list = True - else: - num_parameterizations = 1 - param_bindings = [params] - - else: - param_bindings = None - num_parameterizations = 1 - - # Don't pass circuits if we have in the cache, the sampling function knows to use the cache - circs = list(self._circuit_ops_cache.values()) if not self._transpiled_circ_cache else None - p_b = cast(List[Dict[Parameter, float]], param_bindings) - sampled_statefn_dicts = self.sample_circuits(circuit_sfns=circs, param_bindings=p_b) - - def replace_circuits_with_dicts(operator, param_index=0): - if isinstance(operator, CircuitStateFn): - return sampled_statefn_dicts[id(operator)][param_index] - elif isinstance(operator, ListOp): - return operator.traverse( - partial(replace_circuits_with_dicts, param_index=param_index) - ) - else: - return operator - - # store the operator we constructed, if it isn't stored already - if op_id not in self._cached_ops.keys(): - op_cache = OperatorCache() - op_cache.reduced_op_cache = self._reduced_op_cache - op_cache.circuit_ops_cache = self._circuit_ops_cache - op_cache.transpiled_circ_cache = self._transpiled_circ_cache - op_cache.transpile_before_bind = self._transpile_before_bind - op_cache.transpiled_circ_templates = self._transpiled_circ_templates - self._cached_ops[op_id] = op_cache - - if return_as_list: - return ListOp( - [ - replace_circuits_with_dicts(self._reduced_op_cache, param_index=i) - for i in range(num_parameterizations) - ] - ) - else: - return replace_circuits_with_dicts(self._reduced_op_cache, param_index=0) - - def clear_cache(self) -> None: - """Clear the cache of sampled operator expressions.""" - self._cached_ops = {} - - def _extract_circuitstatefns(self, operator: OperatorBase) -> None: - r""" - Recursively extract the ``CircuitStateFns`` contained in operator into the - ``_circuit_ops_cache`` field. - """ - if isinstance(operator, CircuitStateFn): - self._circuit_ops_cache[id(operator)] = operator - elif isinstance(operator, ListOp): - for op in operator.oplist: - self._extract_circuitstatefns(op) - - def sample_circuits( - self, - circuit_sfns: Optional[List[CircuitStateFn]] = None, - param_bindings: Optional[List[Dict[Parameter, float]]] = None, - ) -> Dict[int, List[StateFn]]: - r""" - Samples the CircuitStateFns and returns a dict associating their ``id()`` values to their - replacement DictStateFn or VectorStateFn. If param_bindings is provided, - the CircuitStateFns are broken into their parameterizations, and a list of StateFns is - returned in the dict for each circuit ``id()``. Note that param_bindings is provided here - in a different format than in ``convert``, and lists of parameters within the dict is not - supported, and only binding dicts which are valid to be passed into Terra can be included - in this list. - - Args: - circuit_sfns: The list of CircuitStateFns to sample. - param_bindings: The parameterizations to bind to each CircuitStateFn. - - Returns: - The dictionary mapping ids of the CircuitStateFns to their replacement StateFns. - Raises: - OpflowError: if extracted circuits are empty. - """ - if not circuit_sfns and not self._transpiled_circ_cache: - raise OpflowError("CircuitStateFn is empty and there is no cache.") - - if circuit_sfns: - self._transpiled_circ_templates = None - if self._statevector or circuit_sfns[0].from_operator: - circuits = [op_c.to_circuit(meas=False) for op_c in circuit_sfns] - else: - circuits = [op_c.to_circuit(meas=True) for op_c in circuit_sfns] - - try: - self._transpiled_circ_cache = self.quantum_instance.transpile( - circuits, pass_manager=self.quantum_instance.unbound_pass_manager - ) - except QiskitError: - logger.debug( - r"CircuitSampler failed to transpile circuits with unbound " - r"parameters. Attempting to transpile only when circuits are bound " - r"now, but this can hurt performance due to repeated transpilation." - ) - self._transpile_before_bind = False - self._transpiled_circ_cache = circuits - else: - circuit_sfns = list(self._circuit_ops_cache.values()) - - if param_bindings is not None: - if self._param_qobj: - start_time = time() - ready_circs = self._prepare_parameterized_run_config(param_bindings) - end_time = time() - logger.debug("Parameter conversion %.5f (ms)", (end_time - start_time) * 1000) - else: - start_time = time() - ready_circs = [ - circ.assign_parameters(_filter_params(circ, binding)) - for circ in self._transpiled_circ_cache - for binding in param_bindings - ] - end_time = time() - logger.debug("Parameter binding %.5f (ms)", (end_time - start_time) * 1000) - else: - ready_circs = self._transpiled_circ_cache - - # run transpiler passes on bound circuits - if self._transpile_before_bind and self.quantum_instance.bound_pass_manager is not None: - ready_circs = self.quantum_instance.transpile( - ready_circs, pass_manager=self.quantum_instance.bound_pass_manager - ) - - results = self.quantum_instance.execute( - ready_circs, had_transpiled=self._transpile_before_bind - ) - - if param_bindings is not None and self._param_qobj: - self._clean_parameterized_run_config() - - # Wipe parameterizations, if any - # self.quantum_instance._run_config.parameterizations = None - - sampled_statefn_dicts = {} - for i, op_c in enumerate(circuit_sfns): - # Taking square root because we're replacing a statevector - # representation of probabilities. - reps = len(param_bindings) if param_bindings is not None else 1 - c_statefns = [] - for j in range(reps): - circ_index = (i * reps) + j - circ_results = results.data(circ_index) - - if "expval_measurement" in circ_results: - avg = circ_results["expval_measurement"] - # Will be replaced with just avg when eval is called later - num_qubits = circuit_sfns[0].num_qubits - result_sfn = DictStateFn( - "0" * num_qubits, - coeff=avg * op_c.coeff, - is_measurement=op_c.is_measurement, - from_operator=op_c.from_operator, - ) - elif self._statevector: - result_sfn = StateFn( - op_c.coeff * results.get_statevector(circ_index), - is_measurement=op_c.is_measurement, - ) - else: - shots = self.quantum_instance._run_config.shots - result_sfn = DictStateFn( - { - b: (v / shots) ** 0.5 * op_c.coeff - for (b, v) in results.get_counts(circ_index).items() - }, - is_measurement=op_c.is_measurement, - from_operator=op_c.from_operator, - ) - if self._attach_results: - result_sfn.execution_results = circ_results - c_statefns.append(result_sfn) - sampled_statefn_dicts[id(op_c)] = c_statefns - return sampled_statefn_dicts - - def _build_aer_params( - self, - circuit: QuantumCircuit, - building_param_tables: Dict[Tuple[int, int], List[float]], - input_params: Dict[Parameter, float], - ) -> None: - def resolve_param(inst_param): - if not isinstance(inst_param, ParameterExpression): - return None - param_mappings = {} - for param in inst_param._parameter_symbols.keys(): - if param not in input_params: - raise ValueError(f"unexpected parameter: {param}") - param_mappings[param] = input_params[param] - return float(inst_param.bind(param_mappings)) - - gate_index = 0 - for instruction in circuit.data: - param_index = 0 - for inst_param in instruction.operation.params: - val = resolve_param(inst_param) - if val is not None: - param_key = (gate_index, param_index) - if param_key in building_param_tables: - building_param_tables[param_key].append(val) - else: - building_param_tables[param_key] = [val] - param_index += 1 - gate_index += 1 - - def _prepare_parameterized_run_config( - self, param_bindings: List[Dict[Parameter, float]] - ) -> List[Any]: - - self.quantum_instance._run_config.parameterizations = [] - - if self._transpiled_circ_templates is None or len(self._transpiled_circ_templates) != len( - self._transpiled_circ_cache - ): - - # temporally resolve parameters of self._transpiled_circ_cache - # They will be overridden in Aer from the next iterations - self._transpiled_circ_templates = [ - circ.assign_parameters(_filter_params(circ, param_bindings[0])) - for circ in self._transpiled_circ_cache - ] - - for circ in self._transpiled_circ_cache: - building_param_tables: Dict[Tuple[int, int], List[float]] = {} - for param_binding in param_bindings: - self._build_aer_params(circ, building_param_tables, param_binding) - param_tables = [] - for gate_and_param_indices in building_param_tables: - gate_index = gate_and_param_indices[0] - param_index = gate_and_param_indices[1] - param_tables.append( - [[gate_index, param_index], building_param_tables[(gate_index, param_index)]] - ) - self.quantum_instance._run_config.parameterizations.append(param_tables) - - return self._transpiled_circ_templates - - def _clean_parameterized_run_config(self) -> None: - self.quantum_instance._run_config.parameterizations = [] - - -def _filter_params(circuit, param_dict): - """Remove all parameters from ``param_dict`` that are not in ``circuit``.""" - return {param: value for param, value in param_dict.items() if param in circuit.parameters} - - -class OperatorCache: - """A struct to cache an operator along with the circuits in contains.""" - - reduced_op_cache = None # the reduced operator - circuit_ops_cache: Optional[Dict[int, CircuitStateFn]] = None # the extracted circuits - transpiled_circ_cache = None # the transpiled circuits - transpile_before_bind = True # whether to transpile before binding parameters in the operator - transpiled_circ_templates: Optional[List[Any]] = None # transpiled circuit templates for Aer diff --git a/qiskit/opflow/converters/converter_base.py b/qiskit/opflow/converters/converter_base.py deleted file mode 100644 index 231f3f2daad7..000000000000 --- a/qiskit/opflow/converters/converter_base.py +++ /dev/null @@ -1,51 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""ConverterBase Class""" - -from abc import ABC, abstractmethod - -from qiskit.opflow.operator_base import OperatorBase -from qiskit.utils.deprecation import deprecate_func - - -class ConverterBase(ABC): - r""" - Deprecated: Converters take an Operator and return a new Operator, generally isomorphic - in some way with the first, but with certain desired properties. For example, - a converter may accept ``CircuitOp`` and return a ``SummedOp`` of - ``PauliOps`` representing the circuit unitary. Converters may not - have polynomial space or time scaling in their operations. On the contrary, many - converters, such as a ``MatrixExpectation`` or ``MatrixEvolution``, which convert - ``PauliOps`` to ``MatrixOps`` internally, will require time or space exponential - in the number of qubits unless a clever trick is known (such as the use of sparse - matrices).""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - pass - - @abstractmethod - def convert(self, operator: OperatorBase) -> OperatorBase: - """Accept the Operator and return the converted Operator - - Args: - operator: The Operator to convert. - - Returns: - The converted Operator. - - """ - raise NotImplementedError diff --git a/qiskit/opflow/converters/dict_to_circuit_sum.py b/qiskit/opflow/converters/dict_to_circuit_sum.py deleted file mode 100644 index b52f16f185c6..000000000000 --- a/qiskit/opflow/converters/dict_to_circuit_sum.py +++ /dev/null @@ -1,68 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""DictToCircuitSum Class""" - -from qiskit.opflow.converters.converter_base import ConverterBase -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.state_fns.circuit_state_fn import CircuitStateFn -from qiskit.opflow.state_fns.dict_state_fn import DictStateFn -from qiskit.opflow.state_fns.vector_state_fn import VectorStateFn -from qiskit.utils.deprecation import deprecate_func - - -class DictToCircuitSum(ConverterBase): - r""" - Deprecated: Converts ``DictStateFns`` or ``VectorStateFns`` to equivalent ``CircuitStateFns`` - or sums thereof. The behavior of this class can be mostly replicated by calling ``to_circuit_op`` - on an Operator, but with the added control of choosing whether to convert only ``DictStateFns`` - or ``VectorStateFns``, rather than both. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, traverse: bool = True, convert_dicts: bool = True, convert_vectors: bool = True - ) -> None: - """ - Args: - traverse: Whether to recurse down into Operators with internal sub-operators for - conversion. - convert_dicts: Whether to convert VectorStateFn. - convert_vectors: Whether to convert DictStateFns. - """ - super().__init__() - self._traverse = traverse - self._convert_dicts = convert_dicts - self._convert_vectors = convert_vectors - - def convert(self, operator: OperatorBase) -> OperatorBase: - """Convert the Operator to ``CircuitStateFns``, recursively if ``traverse`` is True. - - Args: - operator: The Operator to convert - - Returns: - The converted Operator. - """ - - if isinstance(operator, DictStateFn) and self._convert_dicts: - return CircuitStateFn.from_dict(operator.primitive) - if isinstance(operator, VectorStateFn) and self._convert_vectors: - return CircuitStateFn.from_vector(operator.to_matrix(massive=True)) - elif isinstance(operator, ListOp) and "Dict" in operator.primitive_strings(): - return operator.traverse(self.convert) - else: - return operator diff --git a/qiskit/opflow/converters/pauli_basis_change.py b/qiskit/opflow/converters/pauli_basis_change.py deleted file mode 100644 index 6a39f2ef73cc..000000000000 --- a/qiskit/opflow/converters/pauli_basis_change.py +++ /dev/null @@ -1,554 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""PauliBasisChange Class""" - -from functools import partial, reduce -from typing import Callable, List, Optional, Tuple, Union, cast - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.opflow.converters.converter_base import ConverterBase -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.operator_globals import H, I, S -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.opflow.state_fns.operator_state_fn import OperatorStateFn -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.quantum_info import Pauli -from qiskit.utils.deprecation import deprecate_func - - -class PauliBasisChange(ConverterBase): - r""" - Deprecated: Converter for changing Paulis into other bases. By default, the diagonal basis - composed only of Pauli {Z, I}^n is used as the destination basis to which to convert. - Meaning, if a Pauli containing X or Y terms is passed in, which cannot be - sampled or evolved natively on some Quantum hardware, the Pauli can be replaced by a - composition of a change of basis circuit and a Pauli composed of only Z - and I terms (diagonal), which can be evolved or sampled natively on the Quantum - hardware. - - The replacement function determines how the ``PauliOps`` should be replaced by their computed - change-of-basis ``CircuitOps`` and destination ``PauliOps``. Several convenient out-of-the-box - replacement functions have been added as static methods, such as ``measurement_replacement_fn``. - - This class uses the typical basis change method found in most Quantum Computing textbooks - (such as on page 210 of Nielsen and Chuang's, "Quantum Computation and Quantum Information", - ISBN: 978-1-107-00217-3), which involves diagonalizing the single-qubit Paulis with H and S† - gates, mapping the eigenvectors of the diagonalized origin Pauli to the diagonalized - destination Pauli using CNOTS, and then de-diagonalizing any single qubit Paulis to their - non-diagonal destination values. Many other methods are possible, as well as variations on - this method, such as the placement of the CNOT chains. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - destination_basis: Optional[Union[Pauli, PauliOp]] = None, - traverse: bool = True, - replacement_fn: Optional[Callable] = None, - ) -> None: - """ - Args: - destination_basis: The Pauli into the basis of which the operators - will be converted. If None is specified, the destination basis will be the - diagonal ({I, Z}^n) basis requiring only single qubit rotations. - traverse: If true and the operator passed into convert contains sub-Operators, - such as ListOp, traverse the Operator and apply the conversion to every - applicable sub-operator within it. - replacement_fn: A function specifying what to do with the basis-change - ``CircuitOp`` and destination ``PauliOp`` when converting an Operator and - replacing converted values. By default, this will be - - 1) For StateFns (or Measurements): replacing the StateFn with - ComposedOp(StateFn(d), c) where c is the conversion circuit and d is the - destination Pauli, so the overall beginning and ending operators are - equivalent. - - 2) For non-StateFn Operators: replacing the origin p with c·d·c†, where c - is the conversion circuit and d is the destination, so the overall - beginning and ending operators are equivalent. - - """ - super().__init__() - if destination_basis is not None: - self.destination = destination_basis # type: ignore - else: - self._destination = None # type: Optional[PauliOp] - self._traverse = traverse - self._replacement_fn = replacement_fn or PauliBasisChange.operator_replacement_fn - - @property - def destination(self) -> Optional[PauliOp]: - r""" - The destination ``PauliOp``, or ``None`` if using the default destination, the diagonal - basis. - """ - return self._destination - - @destination.setter - def destination(self, dest: Union[Pauli, PauliOp]) -> None: - r""" - The destination ``PauliOp``, or ``None`` if using the default destination, the diagonal - basis. - """ - if isinstance(dest, Pauli): - dest = PauliOp(dest) - - if not isinstance(dest, PauliOp): - raise TypeError( - f"PauliBasisChange can only convert into Pauli bases, not {type(dest)}." - ) - self._destination = dest - - # TODO see whether we should make this performant by handling ListOps of Paulis later. - # pylint: disable=too-many-return-statements - def convert(self, operator: OperatorBase) -> OperatorBase: - r""" - Given a ``PauliOp``, or an Operator containing ``PauliOps`` if ``_traverse`` is True, - converts each Pauli into the basis specified by self._destination and a - basis-change-circuit, calls ``replacement_fn`` with these two Operators, and replaces - the ``PauliOps`` with the output of ``replacement_fn``. For example, for the built-in - ``operator_replacement_fn`` below, each PauliOp p will be replaced by the composition - of the basis-change Clifford ``CircuitOp`` c with the destination PauliOp d and c†, - such that p = c·d·c†, up to global phase. - - Args: - operator: The Operator to convert. - - Returns: - The converted Operator. - - """ - if ( - isinstance(operator, OperatorStateFn) - and isinstance(operator.primitive, PauliSumOp) - and operator.primitive.grouping_type == "TPB" - ): - primitive = operator.primitive.primitive.copy() - origin_x = reduce(np.logical_or, primitive.paulis.x) - origin_z = reduce(np.logical_or, primitive.paulis.z) - origin_pauli = Pauli((origin_z, origin_x)) - cob_instr_op, _ = self.get_cob_circuit(origin_pauli) - primitive.paulis.z = np.logical_or(primitive.paulis.x, primitive.paulis.z) - primitive.paulis.x = False - # The following line is because the deprecated PauliTable did not have a phase - # and did not track it, so phase=0 was always guaranteed. - # But the new PauliList may change phase. - primitive.paulis.phase = 0 - dest_pauli_sum_op = PauliSumOp(primitive, coeff=operator.coeff, grouping_type="TPB") - return self._replacement_fn(cob_instr_op, dest_pauli_sum_op) - - if ( - isinstance(operator, OperatorStateFn) - and isinstance(operator.primitive, SummedOp) - and all( - isinstance(op, PauliSumOp) and op.grouping_type == "TPB" - for op in operator.primitive.oplist - ) - ): - sf_list: List[OperatorBase] = [ - StateFn(op, is_measurement=operator.is_measurement) - for op in operator.primitive.oplist - ] - listop_of_statefns = SummedOp(oplist=sf_list, coeff=operator.coeff) - return listop_of_statefns.traverse(self.convert) - - if isinstance(operator, OperatorStateFn) and isinstance(operator.primitive, PauliSumOp): - operator = OperatorStateFn( - operator.primitive.to_pauli_op(), - coeff=operator.coeff, - is_measurement=operator.is_measurement, - ) - - if isinstance(operator, PauliSumOp): - operator = operator.to_pauli_op() - - if isinstance(operator, (Pauli, PauliOp)): - cob_instr_op, dest_pauli_op = self.get_cob_circuit(operator) - return self._replacement_fn(cob_instr_op, dest_pauli_op) - if isinstance(operator, StateFn) and "Pauli" in operator.primitive_strings(): - # If the StateFn/Meas only contains a Pauli, use it directly. - if isinstance(operator.primitive, PauliOp): - cob_instr_op, dest_pauli_op = self.get_cob_circuit(operator.primitive) - return self._replacement_fn(cob_instr_op, dest_pauli_op * operator.coeff) - # TODO make a canonical "distribute" or graph swap as method in ListOp? - elif operator.primitive.distributive: - if operator.primitive.abelian: - origin_pauli = self.get_tpb_pauli(operator.primitive) - cob_instr_op, _ = self.get_cob_circuit(origin_pauli) - diag_ops: List[OperatorBase] = [ - self.get_diagonal_pauli_op(op) for op in operator.primitive.oplist - ] - dest_pauli_op = operator.primitive.__class__( - diag_ops, coeff=operator.coeff, abelian=True - ) - return self._replacement_fn(cob_instr_op, dest_pauli_op) - else: - sf_list = [ - StateFn(op, is_measurement=operator.is_measurement) - for op in operator.primitive.oplist - ] - listop_of_statefns = operator.primitive.__class__( - oplist=sf_list, coeff=operator.coeff - ) - return listop_of_statefns.traverse(self.convert) - - elif ( - isinstance(operator, ListOp) - and self._traverse - and "Pauli" in operator.primitive_strings() - ): - # If ListOp is abelian we can find a single post-rotation circuit - # for the whole set. For now, - # assume operator can only be abelian if all elements are - # Paulis (enforced in AbelianGrouper). - if operator.abelian: - origin_pauli = self.get_tpb_pauli(operator) - cob_instr_op, _ = self.get_cob_circuit(origin_pauli) - oplist = cast(List[PauliOp], operator.oplist) - diag_ops = [self.get_diagonal_pauli_op(op) for op in oplist] - dest_list_op = operator.__class__(diag_ops, coeff=operator.coeff, abelian=True) - return self._replacement_fn(cob_instr_op, dest_list_op) - else: - return operator.traverse(self.convert) - - return operator - - @staticmethod - def measurement_replacement_fn( - cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp] - ) -> OperatorBase: - r""" - A built-in convenience replacement function which produces measurements - isomorphic to an ``OperatorStateFn`` measurement holding the origin ``PauliOp``. - - Args: - cob_instr_op: The basis-change ``CircuitOp``. - dest_pauli_op: The destination Pauli type operator. - - Returns: - The ``~StateFn @ CircuitOp`` composition equivalent to a measurement by the original - ``PauliOp``. - """ - return ComposedOp([StateFn(dest_pauli_op, is_measurement=True), cob_instr_op]) - - @staticmethod - def statefn_replacement_fn( - cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp] - ) -> OperatorBase: - r""" - A built-in convenience replacement function which produces state functions - isomorphic to an ``OperatorStateFn`` state function holding the origin ``PauliOp``. - - Args: - cob_instr_op: The basis-change ``CircuitOp``. - dest_pauli_op: The destination Pauli type operator. - - Returns: - The ``~CircuitOp @ StateFn`` composition equivalent to a state function defined by the - original ``PauliOp``. - """ - return ComposedOp([cob_instr_op.adjoint(), StateFn(dest_pauli_op)]) - - @staticmethod - def operator_replacement_fn( - cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp] - ) -> OperatorBase: - r""" - A built-in convenience replacement function which produces Operators - isomorphic to the origin ``PauliOp``. - - Args: - cob_instr_op: The basis-change ``CircuitOp``. - dest_pauli_op: The destination ``PauliOp``. - - Returns: - The ``~CircuitOp @ PauliOp @ CircuitOp`` composition isomorphic to the - original ``PauliOp``. - """ - return ComposedOp([cob_instr_op.adjoint(), dest_pauli_op, cob_instr_op]) - - def get_tpb_pauli(self, list_op: ListOp) -> Pauli: - r""" - Gets the Pauli (not ``PauliOp``!) whose diagonalizing single-qubit rotations is a - superset of the diagonalizing single-qubit rotations for each of the Paulis in - ``list_op``. TPB stands for `Tensor Product Basis`. - - Args: - list_op: the :class:`ListOp` whose TPB Pauli to return. - - Returns: - The TBP Pauli. - - """ - oplist = cast(List[PauliOp], list_op.oplist) - origin_z = reduce(np.logical_or, [p_op.primitive.z for p_op in oplist]) - origin_x = reduce(np.logical_or, [p_op.primitive.x for p_op in oplist]) - return Pauli((origin_z, origin_x)) - - def get_diagonal_pauli_op(self, pauli_op: PauliOp) -> PauliOp: - """Get the diagonal ``PualiOp`` to which ``pauli_op`` could be rotated with only - single-qubit operations. - - Args: - pauli_op: The ``PauliOp`` whose diagonal to compute. - - Returns: - The diagonal ``PauliOp``. - """ - return PauliOp( - Pauli( - ( - np.logical_or(pauli_op.primitive.z, pauli_op.primitive.x), - [False] * pauli_op.num_qubits, - ) - ), - coeff=pauli_op.coeff, - ) - - def get_diagonalizing_clifford(self, pauli: Union[Pauli, PauliOp]) -> OperatorBase: - r""" - Construct a ``CircuitOp`` with only single-qubit gates which takes the eigenvectors - of ``pauli`` to eigenvectors composed only of \|0⟩ and \|1⟩ tensor products. Equivalently, - finds the basis-change circuit to take ``pauli`` to a diagonal ``PauliOp`` composed only - of Z and I tensor products. - - Note, underlying Pauli bits are in Qiskit endianness, so we need to reverse before we - begin composing with Operator flow. - - Args: - pauli: the ``Pauli`` or ``PauliOp`` to whose diagonalizing circuit to compute. - - Returns: - The diagonalizing ``CircuitOp``. - - """ - if isinstance(pauli, PauliOp): - pauli = pauli.primitive - - tensorall = cast( - Callable[[List[PrimitiveOp]], PrimitiveOp], partial(reduce, lambda x, y: x.tensor(y)) - ) - - y_to_x_origin = tensorall( - [S if has_y else I for has_y in reversed(np.logical_and(pauli.x, pauli.z))] - ).adjoint() - x_to_z_origin = tensorall( # pylint: disable=assignment-from-no-return - [H if has_x else I for has_x in reversed(pauli.x)] - ) - return x_to_z_origin.compose(y_to_x_origin) - - def pad_paulis_to_equal_length( - self, pauli_op1: PauliOp, pauli_op2: PauliOp - ) -> Tuple[PauliOp, PauliOp]: - r""" - If ``pauli_op1`` and ``pauli_op2`` do not act over the same number of qubits, pad - identities to the end of the shorter of the two so they are of equal length. Padding is - applied to the end of the Paulis. Note that the Terra represents Paulis in big-endian - order, so this will appear as padding to the beginning of the Pauli x and z bit arrays. - - Args: - pauli_op1: A pauli_op to possibly pad. - pauli_op2: A pauli_op to possibly pad. - - Returns: - A tuple containing the padded PauliOps. - - """ - num_qubits = max(pauli_op1.num_qubits, pauli_op2.num_qubits) - pauli_1, pauli_2 = pauli_op1.primitive, pauli_op2.primitive - - # Padding to the end of the Pauli, but remember that Paulis are in reverse endianness. - if not len(pauli_1.z) == num_qubits: - missing_qubits = num_qubits - len(pauli_1.z) - pauli_1 = Pauli( - ( - ([False] * missing_qubits) + pauli_1.z.tolist(), - ([False] * missing_qubits) + pauli_1.x.tolist(), - ) - ) - if not len(pauli_2.z) == num_qubits: - missing_qubits = num_qubits - len(pauli_2.z) - pauli_2 = Pauli( - ( - ([False] * missing_qubits) + pauli_2.z.tolist(), - ([False] * missing_qubits) + pauli_2.x.tolist(), - ) - ) - - return PauliOp(pauli_1, coeff=pauli_op1.coeff), PauliOp(pauli_2, coeff=pauli_op2.coeff) - - def construct_cnot_chain(self, diag_pauli_op1: PauliOp, diag_pauli_op2: PauliOp) -> PrimitiveOp: - r""" - Construct a ``CircuitOp`` (or ``PauliOp`` if equal to the identity) which takes the - eigenvectors of ``diag_pauli_op1`` to the eigenvectors of ``diag_pauli_op2``, - assuming both are diagonal (or performing this operation on their diagonalized Paulis - implicitly if not). This works by the insight that the eigenvalue of a diagonal Pauli's - eigenvector is equal to or -1 if the parity is 1 and 1 if the parity is 0, or - 1 - (2 * parity). Therefore, using CNOTs, we can write the parity of diag_pauli_op1's - significant bits onto some qubit, and then write out that parity onto diag_pauli_op2's - significant bits. - - Args: - diag_pauli_op1: The origin ``PauliOp``. - diag_pauli_op2: The destination ``PauliOp``. - - Return: - The ``PrimitiveOp`` performs the mapping. - """ - # TODO be smarter about connectivity and actual distance between pauli and destination - # TODO be smarter in general - - pauli_1 = ( - diag_pauli_op1.primitive if isinstance(diag_pauli_op1, PauliOp) else diag_pauli_op1 - ) - pauli_2 = ( - diag_pauli_op2.primitive if isinstance(diag_pauli_op2, PauliOp) else diag_pauli_op2 - ) - origin_sig_bits = np.logical_or(pauli_1.z, pauli_1.x) - destination_sig_bits = np.logical_or(pauli_2.z, pauli_2.x) - num_qubits = max(len(pauli_1.z), len(pauli_2.z)) - - sig_equal_sig_bits = np.logical_and(origin_sig_bits, destination_sig_bits) - non_equal_sig_bits = np.logical_not(origin_sig_bits == destination_sig_bits) - # Equivalent to np.logical_xor(origin_sig_bits, destination_sig_bits) - - if not any(non_equal_sig_bits): - return I ^ num_qubits - - # I am deeply sorry for this code, but I don't know another way to do it. - sig_in_origin_only_indices = np.extract( - np.logical_and(non_equal_sig_bits, origin_sig_bits), np.arange(num_qubits) - ) - sig_in_dest_only_indices = np.extract( - np.logical_and(non_equal_sig_bits, destination_sig_bits), np.arange(num_qubits) - ) - - if len(sig_in_origin_only_indices) > 0 and len(sig_in_dest_only_indices) > 0: - origin_anchor_bit = min(sig_in_origin_only_indices) - dest_anchor_bit = min(sig_in_dest_only_indices) - else: - # Set to lowest equal bit - origin_anchor_bit = min(np.extract(sig_equal_sig_bits, np.arange(num_qubits))) - dest_anchor_bit = origin_anchor_bit - - cnots = QuantumCircuit(num_qubits) - # Step 3) Take the indices of bits which are sig_bits in - # pauli but but not in dest, and cnot them to the pauli anchor. - for i in sig_in_origin_only_indices: - if not i == origin_anchor_bit: - cnots.cx(i, origin_anchor_bit) - - # Step 4) - if not origin_anchor_bit == dest_anchor_bit: - cnots.swap(origin_anchor_bit, dest_anchor_bit) - - # Need to do this or a Terra bug sometimes flips cnots. No time to investigate. - cnots.id(0) - - # Step 6) - for i in sig_in_dest_only_indices: - if not i == dest_anchor_bit: - cnots.cx(i, dest_anchor_bit) - - return PrimitiveOp(cnots) - - def get_cob_circuit(self, origin: Union[Pauli, PauliOp]) -> Tuple[PrimitiveOp, PauliOp]: - r""" - Construct an Operator which maps the +1 and -1 eigenvectors - of the origin Pauli to the +1 and -1 eigenvectors of the destination Pauli. It does so by - - 1) converting any \|i+⟩ or \|i+⟩ eigenvector bits in the origin to - \|+⟩ and \|-⟩ with S†s, then - - 2) converting any \|+⟩ or \|+⟩ eigenvector bits in the converted origin to - \|0⟩ and \|1⟩ with Hs, then - - 3) writing the parity of the significant (Z-measured, rather than I) - bits in the origin to a single - "origin anchor bit," using cnots, which will hold the parity of these bits, - - 4) swapping the parity of the pauli anchor bit into a destination anchor bit using - a swap gate (only if they are different, if there are any bits which are significant - in both origin and dest, we set both anchors to one of these bits to avoid a swap). - - 5) writing the parity of the destination anchor bit into the other significant bits - of the destination, - - 6) converting the \|0⟩ and \|1⟩ significant eigenvector bits to \|+⟩ and \|-⟩ eigenvector - bits in the destination where the destination demands it - (e.g. pauli.x == true for a bit), using Hs 8) converting the \|+⟩ and \|-⟩ - significant eigenvector bits to \|i+⟩ and \|i-⟩ eigenvector bits in the - destination where the destination demands it - (e.g. pauli.x == true and pauli.z == true for a bit), using Ss - - Args: - origin: The ``Pauli`` or ``PauliOp`` to map. - - Returns: - A tuple of a ``PrimitiveOp`` which equals the basis change mapping and a ``PauliOp`` - which equals the destination basis. - - Raises: - TypeError: Attempting to convert from non-Pauli origin. - ValueError: Attempting to change a non-identity Pauli to an identity Pauli, or vice - versa. - - """ - - # If pauli is an PrimitiveOp, extract the Pauli - if isinstance(origin, Pauli): - origin = PauliOp(origin) - - if not isinstance(origin, PauliOp): - raise TypeError( - f"PauliBasisChange can only convert Pauli-based OpPrimitives, not {type(origin)}" - ) - - # If no destination specified, assume nearest Pauli in {Z,I}^n basis, - # the standard basis change for expectations. - destination = self.destination or self.get_diagonal_pauli_op(origin) - - # Pad origin or destination if either are not as long as the other - origin, destination = self.pad_paulis_to_equal_length(origin, destination) - - origin_sig_bits = np.logical_or(origin.primitive.x, origin.primitive.z) - destination_sig_bits = np.logical_or(destination.primitive.x, destination.primitive.z) - if not any(origin_sig_bits) or not any(destination_sig_bits): - if not (any(origin_sig_bits) or any(destination_sig_bits)): - # Both all Identity, just return Identities - return I ^ origin.num_qubits, destination - else: - # One is Identity, one is not - raise ValueError("Cannot change to or from a fully Identity Pauli.") - - # Steps 1 and 2 - cob_instruction = self.get_diagonalizing_clifford(origin) - - # Construct CNOT chain, assuming full connectivity... - Steps 3)-5) - cob_instruction = self.construct_cnot_chain(origin, destination).compose(cob_instruction) - - # Step 6 and 7 - dest_diagonlizing_clifford = self.get_diagonalizing_clifford(destination).adjoint() - cob_instruction = dest_diagonlizing_clifford.compose(cob_instruction) - - return cast(PrimitiveOp, cob_instruction), destination diff --git a/qiskit/opflow/converters/two_qubit_reduction.py b/qiskit/opflow/converters/two_qubit_reduction.py deleted file mode 100644 index 10f596f10494..000000000000 --- a/qiskit/opflow/converters/two_qubit_reduction.py +++ /dev/null @@ -1,100 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Z2 Symmetry Tapering Converter Class""" - -import logging -from typing import List, Tuple, Union, cast - -from qiskit.opflow.converters.converter_base import ConverterBase -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.tapered_pauli_sum_op import Z2Symmetries -from qiskit.quantum_info import Pauli -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - - -class TwoQubitReduction(ConverterBase): - """ - Deprecated: Two qubit reduction converter which eliminates the central and last - qubit in a list of Pauli that has diagonal operators (Z,I) at those positions. - - Chemistry specific method: - It can be used to taper two qubits in parity and binary-tree mapped - fermionic Hamiltonians when the spin orbitals are ordered in two spin - sectors, (block spin order) according to the number of particles in the system. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, num_particles: Union[int, List[int], Tuple[int, int]]): - """ - Args: - num_particles: number of particles, if it is a list, - the first number is alpha and the second number if beta. - """ - super().__init__() - if isinstance(num_particles, (tuple, list)): - num_alpha = num_particles[0] - num_beta = num_particles[1] - else: - num_alpha = num_particles // 2 - num_beta = num_particles // 2 - - par_1 = 1 if (num_alpha + num_beta) % 2 == 0 else -1 - par_2 = 1 if num_alpha % 2 == 0 else -1 - self._tapering_values = [par_2, par_1] - - def convert(self, operator: OperatorBase) -> OperatorBase: - """ - Converts the Operator to tapered one by Z2 symmetries. - - Args: - operator: the operator - Returns: - A new operator whose qubit number is reduced by 2. - """ - if not isinstance(operator, PauliSumOp): - return operator - - operator = cast(PauliSumOp, operator) - - if operator.is_zero(): - logger.info( - "Operator is empty, can not do two qubit reduction. Return the empty operator back." - ) - return PauliSumOp.from_list([("I" * (operator.num_qubits - 2), 0)]) - - num_qubits = operator.num_qubits - last_idx = num_qubits - 1 - mid_idx = num_qubits // 2 - 1 - sq_list = [mid_idx, last_idx] - - # build symmetries, sq_paulis: - symmetries, sq_paulis = [], [] - for idx in sq_list: - pauli_str = ["I"] * num_qubits - - pauli_str[idx] = "Z" - z_sym = Pauli("".join(pauli_str)[::-1]) - symmetries.append(z_sym) - - pauli_str[idx] = "X" - sq_pauli = Pauli("".join(pauli_str)[::-1]) - sq_paulis.append(sq_pauli) - - z2_symmetries = Z2Symmetries(symmetries, sq_paulis, sq_list, self._tapering_values) - return z2_symmetries.taper(operator) diff --git a/qiskit/opflow/evolutions/__init__.py b/qiskit/opflow/evolutions/__init__.py deleted file mode 100644 index 6bfeb7d1490d..000000000000 --- a/qiskit/opflow/evolutions/__init__.py +++ /dev/null @@ -1,108 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Operator Evolutions (:mod:`qiskit.opflow.evolutions`) -===================================================== - -.. currentmodule:: qiskit.opflow.evolutions - -.. deprecated:: 0.24.0 - - The :mod:`qiskit.opflow` module is deprecated and will be removed no earlier - than 3 months after the release date. For code migration guidelines, - visit https://qisk.it/opflow_migration. - -Evolutions are converters which traverse an Operator tree, replacing -any :class:`EvolvedOp` `e` with a Schrodinger equation-style evolution -:class:`~qiskit.opflow.primitive_ops.CircuitOp` -equalling or approximating the matrix exponential of -i * the Operator contained inside -(`e.primitive`). The Evolutions are essentially implementations of Hamiltonian Simulation -algorithms, including various methods for Trotterization. - -The :class:`EvolvedOp` is simply a placeholder signifying that the Operator inside it should be -converted to its exponential by the Evolution converter. All Operators -(not :mod:`~qiskit.opflow.state_fns`) have -``.exp_i()`` methods which either return the exponential of the Operator directly, -or an :class:`EvolvedOp` containing the Operator. - - -Note: - Evolutions work with parameterized Operator coefficients, so - ``my_expectation.convert((t * H).exp_i())``, where t is a scalar or Terra Parameter and H - is an Operator, will produce a :class:`~qiskit.opflow.primitive_ops.CircuitOp` - equivalent to e^iHt. - -Evolution Base Class --------------------- - -The EvolutionBase class gives an interface for algorithms to ask for Evolutions as -execution settings. For example, if an algorithm contains an Operator evolution step within it, -such as :class:`~qiskit.algorithms.QAOA`, the algorithm can give the opportunity for the user -to pass an EvolutionBase of their choice to be used in that evolution step. - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - EvolutionBase - -Evolutions ----------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - EvolutionFactory - EvolvedOp - MatrixEvolution - PauliTrotterEvolution - -Trotterizations ---------------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - TrotterizationBase - TrotterizationFactory - Trotter - Suzuki - QDrift -""" - -from .evolution_base import EvolutionBase -from .evolution_factory import EvolutionFactory -from .evolved_op import EvolvedOp -from .pauli_trotter_evolution import PauliTrotterEvolution -from .matrix_evolution import MatrixEvolution -from .trotterizations import TrotterizationBase, TrotterizationFactory, Trotter, Suzuki, QDrift - -# TODO co-diagonalization of Abelian groups in PauliTrotterEvolution -# TODO quantum signal processing/qubitization -# TODO evolve by density matrix (need to add iexp to operator_state_fn) -# TODO linear combination evolution - -__all__ = [ - "EvolutionBase", - "EvolutionFactory", - "EvolvedOp", - "PauliTrotterEvolution", - "MatrixEvolution", - "TrotterizationBase", - "TrotterizationFactory", - "Trotter", - "Suzuki", - "QDrift", -] diff --git a/qiskit/opflow/evolutions/evolution_base.py b/qiskit/opflow/evolutions/evolution_base.py deleted file mode 100644 index 1123c36c34a7..000000000000 --- a/qiskit/opflow/evolutions/evolution_base.py +++ /dev/null @@ -1,57 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""EvolutionBase Class""" - -from abc import ABC, abstractmethod - -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.converters.converter_base import ConverterBase -from qiskit.utils.deprecation import deprecate_func - - -class EvolutionBase(ConverterBase, ABC): - r""" - Deprecated: A base for Evolution converters. - Evolutions are converters which traverse an Operator tree, replacing any ``EvolvedOp`` `e` - with a Schrodinger equation-style evolution ``CircuitOp`` equalling or approximating the - matrix exponential of -i * the Operator contained inside (`e.primitive`). The Evolutions are - essentially implementations of Hamiltonian Simulation algorithms, including various methods - for Trotterization. - - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - @abstractmethod - def convert(self, operator: OperatorBase) -> OperatorBase: - """Traverse the operator, replacing any ``EvolutionOps`` with their equivalent evolution - ``CircuitOps``. - - Args: - operator: The Operator to convert. - - Returns: - The converted Operator, with ``EvolutionOps`` replaced by ``CircuitOps``. - - """ - raise NotImplementedError - - # TODO @abstractmethod - # def error_bounds(self): - # """ error bounds """ - # raise NotImplementedError diff --git a/qiskit/opflow/evolutions/evolution_factory.py b/qiskit/opflow/evolutions/evolution_factory.py deleted file mode 100644 index fffad684e816..000000000000 --- a/qiskit/opflow/evolutions/evolution_factory.py +++ /dev/null @@ -1,57 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""EvolutionFactory Class""" - -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.evolutions.evolution_base import EvolutionBase -from qiskit.opflow.evolutions.pauli_trotter_evolution import PauliTrotterEvolution -from qiskit.opflow.evolutions.matrix_evolution import MatrixEvolution -from qiskit.utils.deprecation import deprecate_func - - -class EvolutionFactory: - """Deprecated: A factory class for convenient automatic selection of an - Evolution algorithm based on the Operator to be converted. - """ - - @staticmethod - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def build(operator: OperatorBase = None) -> EvolutionBase: - r""" - A factory method for convenient automatic selection of an Evolution algorithm based on the - Operator to be converted. - - Args: - operator: the Operator being evolved - - Returns: - EvolutionBase: the ``EvolutionBase`` best suited to evolve operator. - - Raises: - ValueError: If operator is not of a composition for which we know the best Evolution - method. - - """ - primitive_strings = operator.primitive_strings() - if "Matrix" in primitive_strings: - return MatrixEvolution() - - elif "Pauli" in primitive_strings or "SparsePauliOp" in primitive_strings: - # TODO figure out what to do based on qubits and hamming weight. - return PauliTrotterEvolution() - - else: - raise ValueError("Evolutions of mixed Operators not yet supported.") diff --git a/qiskit/opflow/evolutions/evolved_op.py b/qiskit/opflow/evolutions/evolved_op.py deleted file mode 100644 index 03771aa52fa8..000000000000 --- a/qiskit/opflow/evolutions/evolved_op.py +++ /dev/null @@ -1,179 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""EvolutionOp Class""" - -from typing import List, Optional, Set, Union, cast - -import numpy as np -import scipy - -from qiskit.circuit import Instruction, ParameterExpression -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.list_ops.tensored_op import TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.matrix_op import MatrixOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.quantum_info import Statevector -from qiskit.utils.deprecation import deprecate_func - - -class EvolvedOp(PrimitiveOp): - r""" - Deprecated: Class for wrapping Operator Evolutions for compilation (``convert``) by an EvolutionBase - method later, essentially acting as a placeholder. Note that EvolvedOp is a weird case of - PrimitiveOp. It happens to be that it fits into the PrimitiveOp interface nearly perfectly, - and it essentially represents a placeholder for a PrimitiveOp later, even though it doesn't - actually hold a primitive object. We could have chosen for it to be an OperatorBase, - but would have ended up copying and pasting a lot of code from PrimitiveOp.""" - primitive: PrimitiveOp - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, primitive: OperatorBase, coeff: Union[complex, ParameterExpression] = 1.0 - ) -> None: - """ - Args: - primitive: The operator being wrapped to signify evolution later. - coeff: A coefficient multiplying the operator - """ - super().__init__(primitive, coeff=coeff) - - def primitive_strings(self) -> Set[str]: - return self.primitive.primitive_strings() - - @property - def num_qubits(self) -> int: - return self.primitive.num_qubits - - def add(self, other: OperatorBase) -> Union["EvolvedOp", SummedOp]: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over operators with different numbers of qubits, {} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - if isinstance(other, EvolvedOp) and self.primitive == other.primitive: - return EvolvedOp(self.primitive, coeff=self.coeff + other.coeff) - - if isinstance(other, SummedOp): - op_list = [cast(OperatorBase, self)] + other.oplist - return SummedOp(op_list) - - return SummedOp([self, other]) - - def adjoint(self) -> "EvolvedOp": - return EvolvedOp(self.primitive.adjoint() * -1, coeff=self.coeff.conjugate()) - - def equals(self, other: OperatorBase) -> bool: - if not isinstance(other, EvolvedOp) or not self.coeff == other.coeff: - return False - - return self.primitive == other.primitive - - def tensor(self, other: OperatorBase) -> TensoredOp: - if isinstance(other, TensoredOp): - return TensoredOp([cast(OperatorBase, self)] + other.oplist) - - return TensoredOp([self, other]) - - def _expand_dim(self, num_qubits: int) -> TensoredOp: - # pylint: disable=cyclic-import - from ..operator_globals import I - - return self.tensor(I ^ num_qubits) - - def permute(self, permutation: List[int]) -> "EvolvedOp": - return EvolvedOp(self.primitive.permute(permutation), coeff=self.coeff) - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - if front: - return other.compose(new_self) - if isinstance(other, ComposedOp): - return ComposedOp([new_self] + other.oplist) - - return ComposedOp([new_self, other]) - - def __str__(self) -> str: - prim_str = str(self.primitive) - if self.coeff == 1.0: - return f"e^(-i*{prim_str})" - else: - return f"{self.coeff} * e^(-i*{prim_str})" - - def __repr__(self) -> str: - return f"EvolvedOp({repr(self.primitive)}, coeff={self.coeff})" - - def reduce(self) -> "EvolvedOp": - return EvolvedOp(self.primitive.reduce(), coeff=self.coeff) - - def assign_parameters(self, param_dict: dict) -> Union["EvolvedOp", ListOp]: - param_value = self.coeff - if isinstance(self.coeff, ParameterExpression): - unrolled_dict = self._unroll_param_dict(param_dict) - if isinstance(unrolled_dict, list): - return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) - if self.coeff.parameters <= set(unrolled_dict.keys()): - binds = {param: unrolled_dict[param] for param in self.coeff.parameters} - param_value = float(self.coeff.bind(binds)) - return EvolvedOp(self.primitive.bind_parameters(param_dict), coeff=param_value) - - def eval( - self, front: Optional[Union[str, dict, np.ndarray, OperatorBase, Statevector]] = None - ) -> Union[OperatorBase, complex]: - return cast(Union[OperatorBase, complex], self.to_matrix_op().eval(front=front)) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - if ( - isinstance(self.primitive, ListOp) - and self.primitive.__class__.__name__ == ListOp.__name__ - ): - return np.array( - [ - op.exp_i().to_matrix(massive=massive) * self.primitive.coeff * self.coeff - for op in self.primitive.oplist - ], - dtype=complex, - ) - - prim_mat = -1.0j * self.primitive.to_matrix() - return scipy.linalg.expm(prim_mat) * self.coeff - - def to_matrix_op(self, massive: bool = False) -> Union[ListOp, MatrixOp]: - """Returns a ``MatrixOp`` equivalent to this Operator.""" - primitive = self.primitive - if isinstance(primitive, ListOp) and primitive.__class__.__name__ == ListOp.__name__: - return ListOp( - [op.exp_i().to_matrix_op() for op in primitive.oplist], - coeff=primitive.coeff * self.coeff, - ) - - prim_mat = EvolvedOp(primitive).to_matrix(massive=massive) - return MatrixOp(prim_mat, coeff=self.coeff) - - def log_i(self, massive: bool = False) -> OperatorBase: - return self.primitive * self.coeff - - def to_instruction(self, massive: bool = False) -> Instruction: - mat_op = self.to_matrix_op(massive=massive) - if not isinstance(mat_op, MatrixOp): - raise OpflowError("to_instruction is not allowed for ListOp.") - return mat_op.to_instruction() diff --git a/qiskit/opflow/evolutions/matrix_evolution.py b/qiskit/opflow/evolutions/matrix_evolution.py deleted file mode 100644 index 8c18a6a57728..000000000000 --- a/qiskit/opflow/evolutions/matrix_evolution.py +++ /dev/null @@ -1,73 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""MatrixEvolution Class""" - -import logging - -from qiskit.opflow.evolutions.evolution_base import EvolutionBase -from qiskit.opflow.evolutions.evolved_op import EvolvedOp -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.matrix_op import MatrixOp -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - - -class MatrixEvolution(EvolutionBase): - r""" - Deprecated: Performs Evolution by classical matrix exponentiation, constructing a circuit with - ``UnitaryGates`` or ``HamiltonianGates`` containing the exponentiation of the Operator. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - def convert(self, operator: OperatorBase) -> OperatorBase: - r""" - Traverse the operator, replacing ``EvolvedOps`` with ``CircuitOps`` containing - ``UnitaryGates`` or ``HamiltonianGates`` (if self.coeff is a ``ParameterExpression``) - equalling the exponentiation of -i * operator. This is done by converting the - ``EvolvedOp.primitive`` to a ``MatrixOp`` and simply calling ``.exp_i()`` on that. - - Args: - operator: The Operator to convert. - - Returns: - The converted operator. - """ - if isinstance(operator, EvolvedOp): - if not {"Matrix"} == operator.primitive_strings(): - logger.warning( - "Evolved Hamiltonian is not composed of only MatrixOps, converting " - "to Matrix representation, which can be expensive." - ) - # Setting massive=False because this conversion is implicit. User can perform this - # action on the Hamiltonian with massive=True explicitly if they so choose. - # TODO explore performance to see whether we should avoid doing this repeatedly - matrix_ham = operator.primitive.to_matrix_op(massive=False) - operator = EvolvedOp(matrix_ham, coeff=operator.coeff) - - if isinstance(operator.primitive, ListOp): - return operator.primitive.exp_i() * operator.coeff - elif isinstance(operator.primitive, (MatrixOp, PauliOp)): - return operator.primitive.exp_i() - elif isinstance(operator, ListOp): - return operator.traverse(self.convert).reduce() - - return operator diff --git a/qiskit/opflow/evolutions/pauli_trotter_evolution.py b/qiskit/opflow/evolutions/pauli_trotter_evolution.py deleted file mode 100644 index 414ffd5777ca..000000000000 --- a/qiskit/opflow/evolutions/pauli_trotter_evolution.py +++ /dev/null @@ -1,203 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""PauliTrotterEvolution Class""" - -import logging -from typing import Optional, Union, cast - -import numpy as np - -from qiskit.circuit.library import PauliEvolutionGate -from qiskit.synthesis import LieTrotter, SuzukiTrotter -from qiskit.opflow.converters.pauli_basis_change import PauliBasisChange -from qiskit.opflow.evolutions.evolution_base import EvolutionBase -from qiskit.opflow.evolutions.evolved_op import EvolvedOp -from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase -from qiskit.opflow.evolutions.trotterizations.trotterization_factory import TrotterizationFactory -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.operator_globals import I, Z -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.primitive_ops.circuit_op import CircuitOp -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.utils.deprecation import deprecate_func - -# TODO uncomment when we implement Abelian grouped evolution. -# from qiskit.opflow.converters.abelian_grouper import AbelianGrouper - -logger = logging.getLogger(__name__) - - -class PauliTrotterEvolution(EvolutionBase): - r""" - Deprecated: An Evolution algorithm replacing exponentiated sums of Paulis by changing - them each to the Z basis, rotating with an rZ, changing back, and Trotterizing. - - More specifically, we compute basis change circuits for each Pauli into a single-qubit Z, - evolve the Z by the desired evolution time with an rZ gate, and change the basis back using - the adjoint of the original basis change circuit. For sums of Paulis, the individual Pauli - evolution circuits are composed together by Trotterization scheme. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - trotter_mode: Optional[Union[str, TrotterizationBase]] = "trotter", - reps: Optional[int] = 1, - # TODO uncomment when we implement Abelian grouped evolution. - # group_paulis: Optional[bool] = False - ) -> None: - """ - Args: - trotter_mode: A string ('trotter', 'suzuki', or 'qdrift') to pass to the - TrotterizationFactory, or a TrotterizationBase, indicating how to combine - individual Pauli evolution circuits to equal the exponentiation of the Pauli sum. - reps: How many Trotterization repetitions to make, to improve the approximation - accuracy. - # TODO uncomment when we implement Abelian grouped evolution. - # group_paulis: Whether to group Pauli sums into Abelian - # sub-groups, so a single diagonalization circuit can be used for each group - # rather than each Pauli. - """ - super().__init__() - if isinstance(trotter_mode, TrotterizationBase): - self._trotter = trotter_mode - else: - self._trotter = TrotterizationFactory.build(mode=trotter_mode, reps=reps) - - # TODO uncomment when we implement Abelian grouped evolution. - # self._grouper = AbelianGrouper() if group_paulis else None - - @property - def trotter(self) -> TrotterizationBase: - """TrotterizationBase used to evolve SummedOps.""" - return self._trotter - - @trotter.setter - def trotter(self, trotter: TrotterizationBase) -> None: - """Set TrotterizationBase used to evolve SummedOps.""" - self._trotter = trotter - - def convert(self, operator: OperatorBase) -> OperatorBase: - r""" - Traverse the operator, replacing ``EvolvedOps`` with ``CircuitOps`` containing - Trotterized evolutions equalling the exponentiation of -i * operator. - - Args: - operator: The Operator to convert. - - Returns: - The converted operator. - """ - # TODO uncomment when we implement Abelian grouped evolution. - # if self._grouper: - # # Sort into commuting groups - # operator = self._grouper.convert(operator).reduce() - return self._recursive_convert(operator) - - def _get_evolution_synthesis(self): - """Return the ``EvolutionSynthesis`` corresponding to this Trotterization.""" - if self.trotter.order == 1: - return LieTrotter(reps=self.trotter.reps) - return SuzukiTrotter(reps=self.trotter.reps, order=self.trotter.order) - - def _recursive_convert(self, operator: OperatorBase) -> OperatorBase: - if isinstance(operator, EvolvedOp): - if isinstance(operator.primitive, (PauliOp, PauliSumOp)): - pauli = operator.primitive.primitive - time = operator.coeff * operator.primitive.coeff - evo = PauliEvolutionGate( - pauli, time=time, synthesis=self._get_evolution_synthesis() - ) - return CircuitOp(evo) - # operator = EvolvedOp(operator.primitive.to_pauli_op(), coeff=operator.coeff) - if not {"Pauli"} == operator.primitive_strings(): - logger.warning( - "Evolved Hamiltonian is not composed of only Paulis, converting to " - "Pauli representation, which can be expensive." - ) - # Setting massive=False because this conversion is implicit. User can perform this - # action on the Hamiltonian with massive=True explicitly if they so choose. - # TODO explore performance to see whether we should avoid doing this repeatedly - pauli_ham = operator.primitive.to_pauli_op(massive=False) - operator = EvolvedOp(pauli_ham, coeff=operator.coeff) - - if isinstance(operator.primitive, SummedOp): - # TODO uncomment when we implement Abelian grouped evolution. - # if operator.primitive.abelian: - # return self.evolution_for_abelian_paulisum(operator.primitive) - # else: - # Collect terms that are not the identity. - oplist = [ - x - for x in operator.primitive - if not isinstance(x, PauliOp) or sum(x.primitive.x + x.primitive.z) != 0 - ] - # Collect the coefficients of any identity terms, - # which become global phases when exponentiated. - identity_phases = [ - x.coeff - for x in operator.primitive - if isinstance(x, PauliOp) and sum(x.primitive.x + x.primitive.z) == 0 - ] - # Construct sum without the identity operators. - new_primitive = SummedOp(oplist, coeff=operator.primitive.coeff) - trotterized = self.trotter.convert(new_primitive) - circuit_no_identities = self._recursive_convert(trotterized) - # Set the global phase of the QuantumCircuit to account for removed identity terms. - global_phase = -sum(identity_phases) * operator.primitive.coeff - circuit_no_identities.primitive.global_phase = global_phase - return circuit_no_identities - # Covers ListOp, ComposedOp, TensoredOp - elif isinstance(operator.primitive, ListOp): - converted_ops = [self._recursive_convert(op) for op in operator.primitive.oplist] - return operator.primitive.__class__(converted_ops, coeff=operator.coeff) - elif isinstance(operator, ListOp): - return operator.traverse(self.convert).reduce() - - return operator - - def evolution_for_pauli(self, pauli_op: PauliOp) -> PrimitiveOp: - r""" - Compute evolution Operator for a single Pauli using a ``PauliBasisChange``. - - Args: - pauli_op: The ``PauliOp`` to evolve. - - Returns: - A ``PrimitiveOp``, either the evolution ``CircuitOp`` or a ``PauliOp`` equal to the - identity if pauli_op is the identity. - """ - - def replacement_fn(cob_instr_op, dest_pauli_op): - z_evolution = dest_pauli_op.exp_i() - # Remember, circuit composition order is mirrored operator composition order. - return cob_instr_op.adjoint().compose(z_evolution).compose(cob_instr_op) - - # Note: PauliBasisChange will pad destination with identities - # to produce correct CoB circuit - sig_bits = np.logical_or(pauli_op.primitive.z, pauli_op.primitive.x) - a_sig_bit = int(max(np.extract(sig_bits, np.arange(pauli_op.num_qubits)[::-1]))) - destination = (I.tensorpower(a_sig_bit)) ^ (Z * pauli_op.coeff) - cob = PauliBasisChange(destination_basis=destination, replacement_fn=replacement_fn) - return cast(PrimitiveOp, cob.convert(pauli_op)) - - # TODO implement Abelian grouped evolution. - def evolution_for_abelian_paulisum(self, op_sum: SummedOp) -> PrimitiveOp: - """Evolution for abelian pauli sum""" - raise NotImplementedError diff --git a/qiskit/opflow/evolutions/trotterizations/__init__.py b/qiskit/opflow/evolutions/trotterizations/__init__.py deleted file mode 100644 index a721bec2fee4..000000000000 --- a/qiskit/opflow/evolutions/trotterizations/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Trotterization methods - Algorithms for -approximating Exponentials of Operator Sums. -""" - -from .trotterization_base import TrotterizationBase -from .trotterization_factory import TrotterizationFactory -from .trotter import Trotter -from .suzuki import Suzuki -from .qdrift import QDrift - -__all__ = ["TrotterizationBase", "TrotterizationFactory", "Trotter", "Suzuki", "QDrift"] diff --git a/qiskit/opflow/evolutions/trotterizations/qdrift.py b/qiskit/opflow/evolutions/trotterizations/qdrift.py deleted file mode 100644 index 8a31fde01c93..000000000000 --- a/qiskit/opflow/evolutions/trotterizations/qdrift.py +++ /dev/null @@ -1,87 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -QDrift Class - -""" - -import warnings -from typing import List, Union, cast - -import numpy as np - -from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.utils import algorithm_globals -from qiskit.utils.deprecation import deprecate_func - -# pylint: disable=invalid-name - - -class QDrift(TrotterizationBase): - """Deprecated: The QDrift Trotterization method, which selects each each term in the - Trotterization randomly, with a probability proportional to its weight. Based on the work - of Earl Campbell in https://arxiv.org/abs/1811.08017. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, reps: int = 1) -> None: - r""" - Args: - reps: The number of times to repeat the Trotterization circuit. - """ - super().__init__(reps=reps) - - def convert(self, operator: OperatorBase) -> OperatorBase: - if not isinstance(operator, (SummedOp, PauliSumOp)): - raise TypeError("Trotterization converters can only convert SummedOp or PauliSumOp.") - - if not isinstance(operator.coeff, (float, int)): - raise TypeError( - "Trotterization converters can only convert operators with real coefficients." - ) - - operator_iter: Union[PauliSumOp, List[PrimitiveOp]] - - if isinstance(operator, PauliSumOp): - operator_iter = operator - coeffs = operator.primitive.coeffs - coeff = operator.coeff - else: - operator_iter = cast(List[PrimitiveOp], operator.oplist) - coeffs = [op.coeff for op in operator_iter] - coeff = operator.coeff - - # We artificially make the weights positive, TODO check approximation performance - weights = np.abs(coeffs) - lambd = np.sum(weights) - - N = 2 * (lambd**2) * (coeff**2) - factor = lambd * coeff / (N * self.reps) - # The protocol calls for the removal of the individual coefficients, - # and multiplication by a constant factor. - scaled_ops = [(op * (factor / op.coeff)).exp_i() for op in operator_iter] - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - sampled_ops = algorithm_globals.random.choice( - scaled_ops, size=(int(N * self.reps),), p=weights / lambd - ) - - return ComposedOp(sampled_ops).reduce() diff --git a/qiskit/opflow/evolutions/trotterizations/suzuki.py b/qiskit/opflow/evolutions/trotterizations/suzuki.py deleted file mode 100644 index cfe59acac5a8..000000000000 --- a/qiskit/opflow/evolutions/trotterizations/suzuki.py +++ /dev/null @@ -1,121 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Suzuki Class""" - -from typing import List, Union, cast - -from numpy import isreal - -from qiskit.circuit import ParameterExpression -from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.utils.deprecation import deprecate_func - - -class Suzuki(TrotterizationBase): - r""" - Deprecated: Suzuki Trotter expansion, composing the evolution circuits of each Operator in the sum - together by a recursive "bookends" strategy, repeating the whole composed circuit - ``reps`` times. - - Detailed in https://arxiv.org/pdf/quant-ph/0508139.pdf. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, reps: int = 1, order: int = 2) -> None: - """ - Args: - reps: The number of times to repeat the expansion circuit. - order: The order of the expansion to perform. - - """ - super().__init__(reps=reps) - self._order = order - - @property - def order(self) -> int: - """returns order""" - return self._order - - @order.setter - def order(self, order: int) -> None: - """sets order""" - self._order = order - - def convert(self, operator: OperatorBase) -> OperatorBase: - if not isinstance(operator, (SummedOp, PauliSumOp)): - raise TypeError("Trotterization converters can only convert SummedOp or PauliSumOp.") - - if isinstance(operator.coeff, (float, ParameterExpression)): - coeff = operator.coeff - else: - if isreal(operator.coeff): - coeff = operator.coeff.real - else: - raise TypeError( - "Coefficient of the operator must be float or ParameterExpression, " - f"but {operator.coeff}:{type(operator.coeff)} is given." - ) - - if isinstance(operator, PauliSumOp): - comp_list = self._recursive_expansion(operator, coeff, self.order, self.reps) - if isinstance(operator, SummedOp): - comp_list = Suzuki._recursive_expansion(operator.oplist, coeff, self.order, self.reps) - - single_rep = ComposedOp(cast(List[OperatorBase], comp_list)) - full_evo = single_rep.power(self.reps) - return full_evo.reduce() - - @staticmethod - def _recursive_expansion( - op_list: Union[List[OperatorBase], PauliSumOp], - evo_time: Union[float, ParameterExpression], - expansion_order: int, - reps: int, - ) -> List[PrimitiveOp]: - """ - Compute the list of pauli terms for a single slice of the Suzuki expansion - following the paper https://arxiv.org/pdf/quant-ph/0508139.pdf. - - Args: - op_list: The slice's weighted Pauli list for the Suzuki expansion - evo_time: The parameter lambda as defined in said paper, - adjusted for the evolution time and the number of time slices - expansion_order: The order for the Suzuki expansion. - reps: The number of times to repeat the expansion circuit. - - Returns: - The evolution list after expansion. - """ - if expansion_order == 1: - # Base first-order Trotter case - return [(op * (evo_time / reps)).exp_i() for op in op_list] # type: ignore - if expansion_order == 2: - half = Suzuki._recursive_expansion(op_list, evo_time / 2, expansion_order - 1, reps) - return list(reversed(half)) + half - else: - p_k = (4 - 4 ** (1 / (2 * expansion_order - 1))) ** -1 - side = 2 * Suzuki._recursive_expansion( - op_list, evo_time * p_k, expansion_order - 2, reps - ) - middle = Suzuki._recursive_expansion( - op_list, evo_time * (1 - 4 * p_k), expansion_order - 2, reps - ) - return side + middle + side diff --git a/qiskit/opflow/evolutions/trotterizations/trotter.py b/qiskit/opflow/evolutions/trotterizations/trotter.py deleted file mode 100644 index eb1c48d7e27d..000000000000 --- a/qiskit/opflow/evolutions/trotterizations/trotter.py +++ /dev/null @@ -1,34 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Trotter Class""" - -from qiskit.opflow.evolutions.trotterizations.suzuki import Suzuki -from qiskit.utils.deprecation import deprecate_func - - -class Trotter(Suzuki): - r""" - Deprecated: Simple Trotter expansion, composing the evolution circuits of each Operator in the sum - together ``reps`` times and dividing the evolution time of each by ``reps``. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, reps: int = 1) -> None: - r""" - Args: - reps: The number of times to repeat the Trotterization circuit. - """ - super().__init__(order=1, reps=reps) diff --git a/qiskit/opflow/evolutions/trotterizations/trotterization_base.py b/qiskit/opflow/evolutions/trotterizations/trotterization_base.py deleted file mode 100644 index 222d338dfdce..000000000000 --- a/qiskit/opflow/evolutions/trotterizations/trotterization_base.py +++ /dev/null @@ -1,67 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Trotterization Algorithm Base""" - -from abc import abstractmethod - -from qiskit.opflow.evolutions.evolution_base import EvolutionBase -from qiskit.opflow.operator_base import OperatorBase -from qiskit.utils.deprecation import deprecate_func - -# TODO centralize handling of commuting groups - - -class TrotterizationBase(EvolutionBase): - """Deprecated: A base for Trotterization methods, algorithms for approximating exponentiations of - operator sums by compositions of exponentiations. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, reps: int = 1) -> None: - super().__init__() - self._reps = reps - - @property - def reps(self) -> int: - """The number of repetitions to use in the Trotterization, improving the approximation - accuracy. - """ - return self._reps - - @reps.setter - def reps(self, reps: int) -> None: - r"""Set the number of repetitions to use in the Trotterization.""" - self._reps = reps - - @abstractmethod - def convert(self, operator: OperatorBase) -> OperatorBase: - r""" - Convert a ``SummedOp`` into a ``ComposedOp`` or ``CircuitOp`` representing an - approximation of e^-i*``op_sum``. - - Args: - operator: The ``SummedOp`` to evolve. - - Returns: - The Operator approximating op_sum's evolution. - - Raises: - TypeError: A non-SummedOps Operator is passed into ``convert``. - - """ - raise NotImplementedError - - # TODO @abstractmethod - trotter_error_bound diff --git a/qiskit/opflow/evolutions/trotterizations/trotterization_factory.py b/qiskit/opflow/evolutions/trotterizations/trotterization_factory.py deleted file mode 100644 index f8d119140502..000000000000 --- a/qiskit/opflow/evolutions/trotterizations/trotterization_factory.py +++ /dev/null @@ -1,52 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""TrotterizationFactory Class""" - -from qiskit.opflow.evolutions.trotterizations.qdrift import QDrift -from qiskit.opflow.evolutions.trotterizations.suzuki import Suzuki -from qiskit.opflow.evolutions.trotterizations.trotter import Trotter -from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase -from qiskit.utils.deprecation import deprecate_func - - -class TrotterizationFactory: - """Deprecated: A factory for conveniently creating TrotterizationBase instances.""" - - @staticmethod - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def build(mode: str = "trotter", reps: int = 1) -> TrotterizationBase: - """A factory for conveniently creating TrotterizationBase instances. - - Args: - mode: One of 'trotter', 'suzuki', 'qdrift' - reps: The number of times to repeat the Trotterization circuit. - - Returns: - The desired TrotterizationBase instance. - - Raises: - ValueError: A string not in ['trotter', 'suzuki', 'qdrift'] is given for mode. - """ - if mode == "trotter": - return Trotter(reps=reps) - - elif mode == "suzuki": - return Suzuki(reps=reps) - - elif mode == "qdrift": - return QDrift(reps=reps) - - raise ValueError(f"Trotter mode {mode} not supported") diff --git a/qiskit/opflow/exceptions.py b/qiskit/opflow/exceptions.py deleted file mode 100644 index 27bc0f6cc14d..000000000000 --- a/qiskit/opflow/exceptions.py +++ /dev/null @@ -1,28 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Exception for errors raised by Opflow module.""" - -from qiskit.exceptions import QiskitError -from qiskit.utils.deprecation import deprecate_func - - -class OpflowError(QiskitError): - """Deprecated: For Opflow specific errors.""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, *message): - """Set the error message.""" - super().__init__(*message) diff --git a/qiskit/opflow/expectations/__init__.py b/qiskit/opflow/expectations/__init__.py deleted file mode 100644 index 885d4b7e36f6..000000000000 --- a/qiskit/opflow/expectations/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Expectations (:mod:`qiskit.opflow.expectations`) -================================================ - -.. currentmodule:: qiskit.opflow.expectations - -.. deprecated:: 0.24.0 - - The :mod:`qiskit.opflow` module is deprecated and will be removed no earlier - than 3 months after the release date. For code migration guidelines, - visit https://qisk.it/opflow_migration. - -Expectations are converters which enable the computation of the expectation -value of an Observable with respect to some state function. They traverse an Operator tree, -replacing :class:`~qiskit.opflow.state_fns.OperatorStateFn` measurements with equivalent -measurements which are more amenable to computation on quantum or classical hardware. -For example, if one would like to measure the -expectation value of an Operator ``o`` expressed as a sum of Paulis with respect to some state -function, but only has access to diagonal measurements on Quantum hardware, we can create a -measurement ~StateFn(o), use a :class:`PauliExpectation` to convert it to a diagonal measurement -and circuit pre-rotations to append to the state, and sample this circuit on Quantum hardware with -a :class:`~qiskit.opflow.converters.CircuitSampler`. All in all, this would be: -``my_sampler.convert(my_expect.convert(~StateFn(o)) @ my_state).eval()``. - - -Expectation Base Class ----------------------- - -The ExpectationBase class gives an interface for algorithms to ask for Expectations as -execution settings. For example, if an algorithm contains an expectation value step within it, -such as :class:`~qiskit.algorithms.VQE`, the algorithm can give the opportunity for the user -to pass an ExpectationBase of their choice to be used in that expectation value step. - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - ExpectationBase - -Expectations ------------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - ExpectationFactory - AerPauliExpectation - MatrixExpectation - PauliExpectation - CVaRExpectation -""" - -from .expectation_base import ExpectationBase -from .expectation_factory import ExpectationFactory -from .pauli_expectation import PauliExpectation -from .aer_pauli_expectation import AerPauliExpectation -from .matrix_expectation import MatrixExpectation -from .cvar_expectation import CVaRExpectation - -__all__ = [ - "ExpectationBase", - "ExpectationFactory", - "PauliExpectation", - "AerPauliExpectation", - "CVaRExpectation", - "MatrixExpectation", -] diff --git a/qiskit/opflow/expectations/aer_pauli_expectation.py b/qiskit/opflow/expectations/aer_pauli_expectation.py deleted file mode 100644 index b68054281457..000000000000 --- a/qiskit/opflow/expectations/aer_pauli_expectation.py +++ /dev/null @@ -1,162 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""AerPauliExpectation Class""" - -import logging -from functools import reduce -from operator import add -from typing import Union - -from qiskit.exceptions import MissingOptionalLibraryError -from qiskit.opflow.expectations.expectation_base import ExpectationBase -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.state_fns.circuit_state_fn import CircuitStateFn -from qiskit.opflow.state_fns.operator_state_fn import OperatorStateFn -from qiskit.quantum_info import SparsePauliOp -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - - -class AerPauliExpectation(ExpectationBase): - r"""An Expectation converter for using Aer's operator snapshot to - take expectations of quantum state circuits over Pauli observables. - - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - def convert(self, operator: OperatorBase) -> OperatorBase: - """Accept an Operator and return a new Operator with the Pauli measurements replaced by - AerSnapshot-based expectation circuits. - - Args: - operator: The operator to convert. If it contains non-hermitian terms, the - operator is decomposed into hermitian and anti-hermitian parts. - - Returns: - The converted operator. - """ - - if isinstance(operator, OperatorStateFn) and operator.is_measurement: - if isinstance(operator.primitive, ListOp): - is_herm = all((op.is_hermitian() for op in operator.primitive.oplist)) - else: - is_herm = operator.primitive.is_hermitian() - - if not is_herm: - pauli_sum_re = ( - self._replace_pauli_sums( - 1 / 2 * (operator.primitive + operator.primitive.adjoint()).reduce() - ) - * operator.coeff - ) - pauli_sum_im = ( - self._replace_pauli_sums( - 1 / 2j * (operator.primitive - operator.primitive.adjoint()).reduce() - ) - * operator.coeff - ) - pauli_sum = (pauli_sum_re + 1j * pauli_sum_im).reduce() - else: - pauli_sum = self._replace_pauli_sums(operator.primitive) * operator.coeff - return pauli_sum - elif isinstance(operator, ListOp): - return operator.traverse(self.convert) - else: - return operator - - @classmethod - def _replace_pauli_sums(cls, operator): - try: - from qiskit.providers.aer.library import SaveExpectationValue - except ImportError as ex: - raise MissingOptionalLibraryError( - libname="qiskit-aer", - name="AerPauliExpectation", - pip_install="pip install qiskit-aer", - ) from ex - # The 'expval_measurement' label on the save instruction is special - the - # CircuitSampler will look for it to know that the circuit is a Expectation - # measurement, and not simply a - # circuit to replace with a DictStateFn - if operator.__class__ == ListOp: - return operator.traverse(cls._replace_pauli_sums) - - if isinstance(operator, PauliSumOp): - save_instruction = SaveExpectationValue(operator.primitive, "expval_measurement") - return CircuitStateFn( - save_instruction, coeff=operator.coeff, is_measurement=True, from_operator=True - ) - - # Change to Pauli representation if necessary - if {"Pauli"} != operator.primitive_strings(): - logger.warning( - "Measured Observable is not composed of only Paulis, converting to " - "Pauli representation, which can be expensive." - ) - # Setting massive=False because this conversion is implicit. User can perform this - # action on the Observable with massive=True explicitly if they so choose. - operator = operator.to_pauli_op(massive=False) - - if isinstance(operator, SummedOp): - sparse_pauli = reduce( - add, (meas.coeff * SparsePauliOp(meas.primitive) for meas in operator.oplist) - ) - save_instruction = SaveExpectationValue(sparse_pauli, "expval_measurement") - return CircuitStateFn( - save_instruction, coeff=operator.coeff, is_measurement=True, from_operator=True - ) - - if isinstance(operator, PauliOp): - sparse_pauli = operator.coeff * SparsePauliOp(operator.primitive) - save_instruction = SaveExpectationValue(sparse_pauli, "expval_measurement") - return CircuitStateFn(save_instruction, is_measurement=True, from_operator=True) - - raise TypeError( - f"Conversion of OperatorStateFn of {operator.__class__.__name__} is not defined." - ) - - def compute_variance(self, exp_op: OperatorBase) -> Union[list, float]: - r""" - Compute the variance of the expectation estimator. Because Aer takes this expectation - with matrix multiplication, the estimation is exact and the variance is always 0, - but we need to return those values in a way which matches the Operator's structure. - - Args: - exp_op: The full expectation value Operator after sampling. - - Returns: - The variances or lists thereof (if exp_op contains ListOps) of the expectation value - estimation, equal to 0. - """ - - # Need to do this to mimic Op structure - def sum_variance(operator): - if isinstance(operator, ComposedOp): - return 0.0 - elif isinstance(operator, ListOp): - return operator.combo_fn([sum_variance(op) for op in operator.oplist]) - raise TypeError(f"Variance cannot be computed for {operator.__class__.__name__}.") - - return sum_variance(exp_op) diff --git a/qiskit/opflow/expectations/cvar_expectation.py b/qiskit/opflow/expectations/cvar_expectation.py deleted file mode 100644 index 9a6711c498ae..000000000000 --- a/qiskit/opflow/expectations/cvar_expectation.py +++ /dev/null @@ -1,126 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The CVaR (Conditional Value at Risk) expectation class.""" - -from typing import Optional, Union - -from qiskit.opflow.expectations.aer_pauli_expectation import AerPauliExpectation -from qiskit.opflow.expectations.expectation_base import ExpectationBase -from qiskit.opflow.expectations.pauli_expectation import PauliExpectation -from qiskit.opflow.list_ops import ComposedOp, ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.state_fns import CVaRMeasurement, OperatorStateFn -from qiskit.utils.deprecation import deprecate_func - - -class CVaRExpectation(ExpectationBase): - r"""Deprecated: Compute the Conditional Value at Risk (CVaR) expectation value. - - The standard approach to calculating the expectation value of a Hamiltonian w.r.t. a - state is to take the sample mean of the measurement outcomes. This corresponds to an estimator - of the energy. However in several problem settings with a diagonal Hamiltonian, e.g. - in combinatorial optimization where the Hamiltonian encodes a cost function, we are not - interested in calculating the energy but in the lowest possible value we can find. - - To this end, we might consider using the best observed sample as a cost function during - variational optimization. The issue here, is that this can result in a non-smooth optimization - surface. To resolve this issue, we can smooth the optimization surface by using not just the - best observed sample, but instead average over some fraction of best observed samples. - This is exactly what the CVaR estimator accomplishes [1]. - - It is empirically shown, that this can lead to faster convergence for combinatorial - optimization problems. - - Let :math:`\alpha` be a real number in :math:`[0,1]` which specifies the fraction of best - observed samples which are used to compute the objective function. Observe that if - :math:`\alpha = 1`, CVaR is equivalent to a standard expectation value. Similarly, - if :math:`\alpha = 0`, then CVaR corresponds to using the best observed sample. - Intermediate values of :math:`\alpha` interpolate between these two objective functions. - - References: - - [1]: Barkoutsos, P. K., Nannicini, G., Robert, A., Tavernelli, I., and Woerner, S., - "Improving Variational Quantum Optimization using CVaR" - `arXiv:1907.04769 `_ - - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, alpha: float, expectation: Optional[ExpectationBase] = None) -> None: - """ - Args: - alpha: The alpha value describing the quantile considered in the expectation value. - expectation: An expectation object to compute the expectation value. Defaults - to the PauliExpectation calculation. - - Raises: - NotImplementedError: If the ``expectation`` is an AerPauliExpecation. - """ - super().__init__() - self.alpha = alpha - if isinstance(expectation, AerPauliExpectation): - raise NotImplementedError("AerPauliExpecation currently not supported.") - if expectation is None: - expectation = PauliExpectation() - self.expectation = expectation - - def convert(self, operator: OperatorBase) -> OperatorBase: - """Return an expression that computes the CVaR expectation upon calling ``eval``. - Args: - operator: The operator to convert. - - Returns: - The converted operator. - """ - expectation = self.expectation.convert(operator) - - # replace OperatorMeasurements by CVaRMeasurement - def replace_with_cvar(operator): - if isinstance(operator, OperatorStateFn) and operator.is_measurement: - return CVaRMeasurement(operator.primitive, alpha=self.alpha) - elif isinstance(operator, ListOp): - return operator.traverse(replace_with_cvar) - return operator - - return replace_with_cvar(expectation) - - def compute_variance(self, exp_op: OperatorBase) -> Union[list, float]: - """Returns the variance of the CVaR calculation - - Args: - exp_op: The operator whose evaluation yields an expectation - of some StateFn against a diagonal observable. - - Returns: - The variance of the CVaR estimate corresponding to the converted - exp_op. - Raises: - ValueError: If the exp_op does not correspond to an expectation value. - """ - - def cvar_variance(operator): - if isinstance(operator, ComposedOp): - sfdict = operator.oplist[1] - measurement = operator.oplist[0] - return measurement.eval_variance(sfdict) - - elif isinstance(operator, ListOp): - return operator.combo_fn([cvar_variance(op) for op in operator.oplist]) - - raise ValueError("Input operator does not correspond to a value expectation value.") - - cvar_op = self.convert(exp_op) - return cvar_variance(cvar_op) diff --git a/qiskit/opflow/expectations/expectation_base.py b/qiskit/opflow/expectations/expectation_base.py deleted file mode 100644 index 593c43b83833..000000000000 --- a/qiskit/opflow/expectations/expectation_base.py +++ /dev/null @@ -1,72 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""ExpectationBase Class""" - -from abc import abstractmethod -from typing import Union - -import numpy as np - -from qiskit.opflow.converters import ConverterBase -from qiskit.opflow.operator_base import OperatorBase -from qiskit.utils.deprecation import deprecate_func - - -class ExpectationBase(ConverterBase): - r""" - Deprecated: A base for Expectation value converters. Expectations are converters which enable the - computation of the expectation value of an Observable with respect to some state function. - They traverse an Operator tree, replacing OperatorStateFn measurements with equivalent - measurements which are more amenable to computation on quantum or classical hardware. For - example, if one would like to measure the expectation value of an Operator ``o`` expressed - as a sum of Paulis with respect to some state function, but only has access to diagonal - measurements on Quantum hardware, we can create a measurement ~StateFn(o), - use a ``PauliExpectation`` to convert it to a diagonal measurement and circuit - pre-rotations to a append to the state, and sample this circuit on Quantum hardware with - a CircuitSampler. All in all, this would be: - ``my_sampler.convert(my_expect.convert(~StateFn(o)) @ my_state).eval()``. - - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - @abstractmethod - def convert(self, operator: OperatorBase) -> OperatorBase: - """Accept an Operator and return a new Operator with the measurements replaced by - alternate methods to compute the expectation value. - - Args: - operator: The operator to convert. - - Returns: - The converted operator. - """ - raise NotImplementedError - - @abstractmethod - def compute_variance(self, exp_op: OperatorBase) -> Union[list, complex, np.ndarray]: - """Compute the variance of the expectation estimator. - - Args: - exp_op: The full expectation value Operator after sampling. - - Returns: - The variances or lists thereof (if exp_op contains ListOps) of the expectation value - estimation. - """ - raise NotImplementedError diff --git a/qiskit/opflow/expectations/expectation_factory.py b/qiskit/opflow/expectations/expectation_factory.py deleted file mode 100644 index 58a72ed6daca..000000000000 --- a/qiskit/opflow/expectations/expectation_factory.py +++ /dev/null @@ -1,126 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""ExpectationFactory Class""" - -import logging -from typing import Optional, Union - -from qiskit import BasicAer -from qiskit.opflow.expectations.aer_pauli_expectation import AerPauliExpectation -from qiskit.opflow.expectations.expectation_base import ExpectationBase -from qiskit.opflow.expectations.matrix_expectation import MatrixExpectation -from qiskit.opflow.expectations.pauli_expectation import PauliExpectation -from qiskit.opflow.operator_base import OperatorBase -from qiskit.providers import Backend -from qiskit.utils.backend_utils import is_aer_qasm, is_statevector_backend -from qiskit.utils import QuantumInstance, optionals -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - - -class ExpectationFactory: - - """Deprecated: factory class for convenient automatic selection of an Expectation based on the - Operator to be converted and backend used to sample the expectation value. - """ - - @staticmethod - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def build( - operator: OperatorBase, - backend: Optional[Union[Backend, QuantumInstance]] = None, - include_custom: bool = True, - ) -> ExpectationBase: - """ - A factory method for convenient automatic selection of an Expectation based on the - Operator to be converted and backend used to sample the expectation value. - - Args: - operator: The Operator whose expectation value will be taken. - backend: The backend which will be used to sample the expectation value. - include_custom: Whether the factory will include the (Aer) specific custom - expectations if their behavior against the backend might not be as expected. - For instance when using Aer qasm_simulator with paulis the Aer snapshot can - be used but the outcome lacks shot noise and hence does not intuitively behave - overall as people might expect when choosing a qasm_simulator. It is however - fast as long as the more state vector like behavior is acceptable. - - Returns: - The expectation algorithm which best fits the Operator and backend. - - Raises: - ValueError: If operator is not of a composition for which we know the best Expectation - method. - """ - backend_to_check = backend.backend if isinstance(backend, QuantumInstance) else backend - - # pylint: disable=cyclic-import - primitives = operator.primitive_strings() - if primitives in ({"Pauli"}, {"SparsePauliOp"}): - - if backend_to_check is None: - # If user has Aer but didn't specify a backend, use the Aer fast expectation - if optionals.HAS_AER: - from qiskit_aer import AerSimulator - - backend_to_check = AerSimulator() - # If user doesn't have Aer, use statevector_simulator - # for < 16 qubits, and qasm with warning for more. - else: - if operator.num_qubits <= 16: - backend_to_check = BasicAer.get_backend("statevector_simulator") - else: - logger.warning( - "%d qubits is a very large expectation value. " - "Consider installing Aer to use " - "Aer's fast expectation, which will perform better here. We'll use " - "the BasicAer qasm backend for this expectation to avoid having to " - "construct the %dx%d operator matrix.", - operator.num_qubits, - 2**operator.num_qubits, - 2**operator.num_qubits, - ) - backend_to_check = BasicAer.get_backend("qasm_simulator") - - # If the user specified Aer qasm backend and is using a - # Pauli operator, use the Aer fast expectation if we are including such - # custom behaviors. - if is_aer_qasm(backend_to_check) and include_custom: - return AerPauliExpectation() - - # If the user specified a statevector backend (either Aer or BasicAer), - # use a converter to produce a - # Matrix operator and compute using matmul - elif is_statevector_backend(backend_to_check): - if operator.num_qubits >= 16: - logger.warning( - "Note: Using a statevector_simulator with %d qubits can be very expensive. " - "Consider using the Aer qasm_simulator instead to take advantage of Aer's " - "built-in fast Pauli Expectation", - operator.num_qubits, - ) - return MatrixExpectation() - - # All other backends, including IBMQ, BasicAer QASM, go here. - else: - return PauliExpectation() - - elif primitives == {"Matrix"}: - return MatrixExpectation() - - else: - raise ValueError("Expectations of Mixed Operators not yet supported.") diff --git a/qiskit/opflow/expectations/matrix_expectation.py b/qiskit/opflow/expectations/matrix_expectation.py deleted file mode 100644 index 5265eff6e59d..000000000000 --- a/qiskit/opflow/expectations/matrix_expectation.py +++ /dev/null @@ -1,76 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""MatrixExpectation Class""" - -from typing import Union - -from qiskit.opflow.expectations.expectation_base import ExpectationBase -from qiskit.opflow.list_ops import ComposedOp, ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.state_fns.operator_state_fn import OperatorStateFn -from qiskit.utils.deprecation import deprecate_func - - -class MatrixExpectation(ExpectationBase): - """An Expectation converter which converts Operator measurements to - be matrix-based so they can be evaluated by matrix multiplication.""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - def convert(self, operator: OperatorBase) -> OperatorBase: - """Accept an Operator and return a new Operator with the Pauli measurements replaced by - Matrix based measurements. - - Args: - operator: The operator to convert. - - Returns: - The converted operator. - """ - if isinstance(operator, OperatorStateFn) and operator.is_measurement: - return operator.to_matrix_op() - elif isinstance(operator, ListOp): - return operator.traverse(self.convert) - else: - return operator - - def compute_variance(self, exp_op: OperatorBase) -> Union[list, float]: - r""" - Compute the variance of the expectation estimator. Because this expectation - works by matrix multiplication, the estimation is exact and the variance is - always 0, but we need to return those values in a way which matches the Operator's - structure. - - Args: - exp_op: The full expectation value Operator. - - Returns: - The variances or lists thereof (if exp_op contains ListOps) of the expectation value - estimation, equal to 0. - """ - - # Need to do this to mimic Op structure - def sum_variance(operator): - if isinstance(operator, ComposedOp): - return 0.0 - elif isinstance(operator, ListOp): - return operator.combo_fn([sum_variance(op) for op in operator.oplist]) - else: - return 0.0 - - return sum_variance(exp_op) diff --git a/qiskit/opflow/expectations/pauli_expectation.py b/qiskit/opflow/expectations/pauli_expectation.py deleted file mode 100644 index 7c8d9697db16..000000000000 --- a/qiskit/opflow/expectations/pauli_expectation.py +++ /dev/null @@ -1,117 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""PauliExpectation Class""" - -import logging -from typing import Union - -import numpy as np - -from qiskit.opflow.converters.abelian_grouper import AbelianGrouper -from qiskit.opflow.converters.pauli_basis_change import PauliBasisChange -from qiskit.opflow.expectations.expectation_base import ExpectationBase -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.opflow.state_fns.operator_state_fn import OperatorStateFn -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - - -class PauliExpectation(ExpectationBase): - r""" - An Expectation converter for Pauli-basis observables by changing Pauli measurements to a - diagonal ({Z, I}^n) basis and appending circuit post-rotations to the measured state function. - Optionally groups the Paulis with the same post-rotations (those that commute with one - another, or form Abelian groups) into single measurements to reduce circuit execution - overhead. - - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, group_paulis: bool = True) -> None: - """ - Args: - group_paulis: Whether to group the Pauli measurements into commuting sums, which all - have the same diagonalizing circuit. - - """ - super().__init__() - self._grouper = AbelianGrouper() if group_paulis else None - - def convert(self, operator: OperatorBase) -> OperatorBase: - """Accepts an Operator and returns a new Operator with the Pauli measurements replaced by - diagonal Pauli post-rotation based measurements so they can be evaluated by sampling and - averaging. - - Args: - operator: The operator to convert. - - Returns: - The converted operator. - """ - if isinstance(operator, ListOp): - return operator.traverse(self.convert).reduce() - - if isinstance(operator, OperatorStateFn) and operator.is_measurement: - # Change to Pauli representation if necessary - if ( - isinstance(operator.primitive, (ListOp, PrimitiveOp)) - and not isinstance(operator.primitive, PauliSumOp) - and {"Pauli", "SparsePauliOp"} < operator.primitive_strings() - ): - logger.warning( - "Measured Observable is not composed of only Paulis, converting to " - "Pauli representation, which can be expensive." - ) - # Setting massive=False because this conversion is implicit. User can perform this - # action on the Observable with massive=True explicitly if they so choose. - pauli_obsv = operator.primitive.to_pauli_op(massive=False) - operator = StateFn(pauli_obsv, is_measurement=True, coeff=operator.coeff) - - if self._grouper and isinstance(operator.primitive, (ListOp, PauliSumOp)): - grouped = self._grouper.convert(operator.primitive) - operator = StateFn(grouped, is_measurement=True, coeff=operator.coeff) - - # Convert the measurement into diagonal basis (PauliBasisChange chooses - # this basis by default). - cob = PauliBasisChange(replacement_fn=PauliBasisChange.measurement_replacement_fn) - return cob.convert(operator).reduce() - - return operator - - def compute_variance(self, exp_op: OperatorBase) -> Union[list, float, np.ndarray]: - def sum_variance(operator): - if isinstance(operator, ComposedOp): - sfdict = operator.oplist[1] - measurement = operator.oplist[0] - average = np.asarray(measurement.eval(sfdict)) - variance = sum( - (v * (np.asarray(measurement.eval(b)) - average)) ** 2 - for (b, v) in sfdict.primitive.items() - ) - return operator.coeff * variance - - elif isinstance(operator, ListOp): - return operator.combo_fn([sum_variance(op) for op in operator.oplist]) - - return 0.0 - - return sum_variance(exp_op) diff --git a/qiskit/opflow/gradients/__init__.py b/qiskit/opflow/gradients/__init__.py deleted file mode 100644 index 9c2625d078ca..000000000000 --- a/qiskit/opflow/gradients/__init__.py +++ /dev/null @@ -1,194 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -r""" -Gradients (:mod:`qiskit.opflow.gradients`) -========================================== - -.. deprecated:: 0.24.0 - - The :mod:`qiskit.opflow` module is deprecated and will be removed no earlier - than 3 months after the release date. For code migration guidelines, - visit https://qisk.it/opflow_migration. - -Given an operator that represents either a quantum state resp. an expectation value, -the gradient framework enables the evaluation of gradients, natural gradients, -Hessians, as well as the Quantum Fisher Information. - -Suppose a parameterized quantum state `|ψ(θ)〉 = V(θ)|ψ〉` with input state `|ψ〉` and parameterized -Ansatz `V(θ)`, and an Operator `O(ω)`. - - -**Gradients** - -We want to compute one of: -* :math:`d⟨ψ(θ)|O(ω)|ψ(θ)〉/ dω` -* :math:`d⟨ψ(θ)|O(ω)|ψ(θ)〉/ dθ` -* :math:`d⟨ψ(θ)|i〉⟨i|ψ(θ)〉/ dθ` - -The last case corresponds to the gradient w.r.t. the sampling probabilities of `|ψ(θ)`. -These gradients can be computed with different methods, i.e. a parameter shift, a linear combination -of unitaries and a finite difference method. - -**Examples** - -.. code-block:: - - x = Parameter('x') - ham = x * X - a = Parameter('a') - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.p(params[0], q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) - - value_dict = {x: 0.1, a: np.pi / 4} - - ham_grad = Gradient(grad_method='param_shift').convert(operator=op, params=[x]) - ham_grad.assign_parameters(value_dict).eval() - - state_grad = Gradient(grad_method='lin_comb').convert(operator=op, params=[a]) - state_grad.assign_parameters(value_dict).eval() - - prob_grad = Gradient(grad_method='fin_diff').convert( - operator=CircuitStateFn(primitive=qc, coeff=1.), params=[a] - ) - prob_grad.assign_parameters(value_dict).eval() - -**Hessians** - -We want to compute one of: -* :math:`d^2⟨ψ(θ)|O(ω)|ψ(θ)〉/ dω^2` -* :math:`d^2⟨ψ(θ)|O(ω)|ψ(θ)〉/ dθ^2` -* :math:`d^2⟨ψ(θ)|O(ω)|ψ(θ)〉/ dθ dω` -* :math:`d^2⟨ψ(θ)|i〉⟨i|ψ(θ)〉/ dθ^2` - -The last case corresponds to the Hessian w.r.t. the sampling probabilities of `|ψ(θ)〉`. -Just as the first order gradients, the Hessians can be evaluated with different methods, i.e. a -parameter shift, a linear combination of unitaries and a finite difference method. -Given a tuple of parameters ``Hessian().convert(op, param_tuple)`` returns the value for the second -order derivative. -If a list of parameters is given ``Hessian().convert(op, param_list)`` returns the full Hessian for -all the given parameters according to the given parameter order. - -**QFI** - -The Quantum Fisher Information `QFI` is a metric tensor which is representative for the -representation capacity of a parameterized quantum state `|ψ(θ)〉 = V(θ)|ψ〉` generated by an -input state `|ψ〉` and a parameterized Ansatz `V(θ)`. -The entries of the `QFI` for a pure state read -:math:`\mathrm{QFI}_{kl} = 4 \mathrm{Re}[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉]`. - -Just as for the previous derivative types, the QFI can be computed using different methods: a full -representation based on a linear combination of unitaries implementation, a block-diagonal and a -diagonal representation based on an overlap method. - -**Examples** - -.. code-block:: - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.p(params[0], q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) - - value_dict = {x: 0.1, a: np.pi / 4} - - qfi = QFI('lin_comb_full').convert( - operator=CircuitStateFn(primitive=qc, coeff=1.), params=[a] - ) - qfi.assign_parameters(value_dict).eval() - -**NaturalGradients** - -The natural gradient is a special gradient method which re-scales a gradient w.r.t. a state -parameter with the inverse of the corresponding Quantum Fisher Information (QFI) -:math:`\mathrm{QFI}^{-1} d⟨ψ(θ)|O(ω)|ψ(θ)〉/ dθ`. -Hereby, we can choose a gradient as well as a QFI method and a regularization method which is used -together with a least square solver instead of exact inversion of the QFI: - -**Examples** - -.. code-block:: - - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) - nat_grad = NaturalGradient(grad_method='lin_comb, - qfi_method='lin_comb_full', - regularization='ridge').convert(operator=op, params=params) - -The derivative classes come with a `gradient_wrapper()` function which returns the corresponding -callable and are thus compatible with the optimizers. - -.. currentmodule:: qiskit.opflow.gradients - -Base Classes ------------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - DerivativeBase - GradientBase - HessianBase - QFIBase - -Converters ----------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - CircuitGradient - CircuitQFI - -Derivatives ------------ - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - Gradient - Hessian - NaturalGradient - QFI - -""" - -from .circuit_gradients.circuit_gradient import CircuitGradient -from .circuit_qfis.circuit_qfi import CircuitQFI -from .derivative_base import DerivativeBase -from .gradient_base import GradientBase -from .gradient import Gradient -from .natural_gradient import NaturalGradient -from .hessian_base import HessianBase -from .hessian import Hessian -from .qfi_base import QFIBase -from .qfi import QFI - -__all__ = [ - "DerivativeBase", - "CircuitGradient", - "GradientBase", - "Gradient", - "NaturalGradient", - "HessianBase", - "Hessian", - "QFIBase", - "QFI", - "CircuitQFI", -] diff --git a/qiskit/opflow/gradients/circuit_gradients/__init__.py b/qiskit/opflow/gradients/circuit_gradients/__init__.py deleted file mode 100644 index 16953b57ff21..000000000000 --- a/qiskit/opflow/gradients/circuit_gradients/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -The module for first order derivatives. -""" -from .circuit_gradient import CircuitGradient -from .lin_comb import LinComb -from .param_shift import ParamShift - -__all__ = ["CircuitGradient", "LinComb", "ParamShift"] diff --git a/qiskit/opflow/gradients/circuit_gradients/circuit_gradient.py b/qiskit/opflow/gradients/circuit_gradients/circuit_gradient.py deleted file mode 100644 index 284cd7da7eb7..000000000000 --- a/qiskit/opflow/gradients/circuit_gradients/circuit_gradient.py +++ /dev/null @@ -1,108 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""CircuitGradient Class""" - -from abc import abstractmethod -from typing import List, Union, Optional, Tuple, Set - -from qiskit import QuantumCircuit, QiskitError, transpile -from qiskit.circuit import ParameterExpression, ParameterVector -from qiskit.utils.deprecation import deprecate_func -from ...converters.converter_base import ConverterBase -from ...operator_base import OperatorBase - - -class CircuitGradient(ConverterBase): - r"""Deprecated: Circuit to gradient operator converter. - - Converter for changing parameterized circuits into operators - whose evaluation yields the gradient with respect to the circuit parameters. - - This is distinct from DerivativeBase converters which take gradients of composite - operators and handle things like differentiating combo_fn's and enforcing product rules - when operator coefficients are parameterized. - - CircuitGradient - uses quantum techniques to get derivatives of circuits - DerivativeBase - uses classical techniques to differentiate operator flow data structures - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - # pylint: disable=arguments-differ - @abstractmethod - def convert( - self, - operator: OperatorBase, - params: Optional[ - Union[ - ParameterExpression, - ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - ] - ] = None, - ) -> OperatorBase: - r""" - Args: - operator: The operator we are taking the gradient of - params: The parameters we are taking the gradient wrt: ω - If a ParameterExpression, ParameterVector or List[ParameterExpression] is given, - then the 1st order derivative of the operator is calculated. - If a Tuple[ParameterExpression, ParameterExpression] or - List[Tuple[ParameterExpression, ParameterExpression]] - is given, then the 2nd order derivative of the operator is calculated. - - Returns: - An operator whose evaluation yields the Gradient. - - Raises: - ValueError: If ``params`` contains a parameter not present in ``operator``. - """ - raise NotImplementedError - - @staticmethod - def _transpile_to_supported_operations( - circuit: QuantumCircuit, supported_gates: Set[str] - ) -> QuantumCircuit: - """Transpile the given circuit into a gate set for which the gradients may be computed. - - Args: - circuit: Quantum circuit to be transpiled into supported operations. - supported_gates: Set of quantum operations supported by a gradient method intended to - be used on the quantum circuit. - - Returns: - Quantum circuit which is transpiled into supported operations. - - Raises: - QiskitError: when circuit transpiling fails. - - """ - unique_ops = set(circuit.count_ops()) - if not unique_ops.issubset(supported_gates): - try: - circuit = transpile( - circuit, basis_gates=list(supported_gates), optimization_level=0 - ) - except Exception as exc: - raise QiskitError( - f"Could not transpile the circuit provided {circuit} into supported gates " - f"{supported_gates}." - ) from exc - return circuit diff --git a/qiskit/opflow/gradients/circuit_gradients/lin_comb.py b/qiskit/opflow/gradients/circuit_gradients/lin_comb.py deleted file mode 100644 index 1e06512e763e..000000000000 --- a/qiskit/opflow/gradients/circuit_gradients/lin_comb.py +++ /dev/null @@ -1,909 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module to compute the state gradient with the linear combination method.""" - -from collections.abc import Iterable -from copy import deepcopy -from functools import partial -from itertools import product -from typing import List, Optional, Tuple, Union, Callable - -import scipy -import numpy as np - -from qiskit.circuit import Gate, Instruction -from qiskit.circuit import ( - CircuitInstruction, - QuantumCircuit, - QuantumRegister, - ParameterVector, - ParameterExpression, - Parameter, -) -from qiskit.circuit.parametertable import ParameterReferences, ParameterTable -from qiskit.circuit.controlledgate import ControlledGate -from qiskit.circuit.library import SGate, SdgGate, XGate -from qiskit.circuit.library.standard_gates import ( - CXGate, - CYGate, - CZGate, - IGate, - RXGate, - RXXGate, - RYGate, - RYYGate, - RZGate, - RZXGate, - RZZGate, - PhaseGate, - UGate, - ZGate, -) -from qiskit.quantum_info import partial_trace -from qiskit.utils.deprecation import deprecate_func -from ...operator_base import OperatorBase -from ...list_ops.list_op import ListOp -from ...list_ops.composed_op import ComposedOp -from ...list_ops.summed_op import SummedOp -from ...operator_globals import Z, I, Y, One, Zero -from ...primitive_ops.primitive_op import PrimitiveOp -from ...state_fns.state_fn import StateFn -from ...state_fns.circuit_state_fn import CircuitStateFn -from ...state_fns.dict_state_fn import DictStateFn -from ...state_fns.vector_state_fn import VectorStateFn -from ...state_fns.sparse_vector_state_fn import SparseVectorStateFn -from ...exceptions import OpflowError -from .circuit_gradient import CircuitGradient -from ...converters import PauliBasisChange - - -class LinComb(CircuitGradient): - """Deprecated: Compute the state gradient d⟨ψ(ω)|O(θ)|ψ(ω)〉/ dω respectively the gradients of the - sampling probabilities of the basis states of - a state |ψ(ω)〉w.r.t. ω. - This method employs a linear combination of unitaries, - see e.g. https://arxiv.org/pdf/1811.11184.pdf - """ - - SUPPORTED_GATES = { - "rx", - "ry", - "rz", - "rzx", - "rzz", - "ryy", - "rxx", - "p", - "u", - "controlledgate", - "cx", - "cy", - "cz", - "ccx", - "swap", - "iswap", - "t", - "s", - "sdg", - "x", - "y", - "z", - } - - # pylint: disable=signature-differs, arguments-differ - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, aux_meas_op: OperatorBase = Z): - """ - Args: - aux_meas_op: The operator that the auxiliary qubit is measured with respect to. - For ``aux_meas_op = Z`` we compute 2Re[(dω⟨ψ(ω)|)O(θ)|ψ(ω)〉], - for ``aux_meas_op = -Y`` we compute 2Im[(dω⟨ψ(ω)|)O(θ)|ψ(ω)〉], and - for ``aux_meas_op = Z - 1j * Y`` we compute 2(dω⟨ψ(ω)|)O(θ)|ψ(ω)〉. - Raises: - ValueError: If the provided auxiliary measurement operator is not supported. - """ - super().__init__() - if aux_meas_op not in [Z, -Y, (Z - 1j * Y)]: - raise ValueError( - "This auxiliary measurement operator is currently not supported. Please choose " - "either Z, -Y, or Z - 1j * Y. " - ) - self._aux_meas_op = aux_meas_op - - def convert( - self, - operator: OperatorBase, - params: Union[ - ParameterExpression, - ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - ], - ) -> OperatorBase: - """Convert ``operator`` into an operator that represents the gradient w.r.t. ``params``. - - Args: - operator: The operator we are taking the gradient of: ⟨ψ(ω)|O(θ)|ψ(ω)〉 - params: The parameters we are taking the gradient wrt: ω - If a ParameterExpression, ParameterVector or List[ParameterExpression] is given, - then the 1st order derivative of the operator is calculated. - If a Tuple[ParameterExpression, ParameterExpression] or - List[Tuple[ParameterExpression, ParameterExpression]] - is given, then the 2nd order derivative of the operator is calculated. - Returns: - An operator corresponding to the gradient resp. Hessian. The order is in accordance with - the order of the given parameters. - """ - return self._prepare_operator(operator, params) - - # pylint: disable=too-many-return-statements - def _prepare_operator( - self, - operator: OperatorBase, - params: Union[ - ParameterExpression, - ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - ], - ) -> OperatorBase: - """Traverse ``operator`` to get back the adapted operator representing the gradient. - - Args: - operator: The operator we are taking the gradient of: ⟨ψ(ω)|O(θ)|ψ(ω)〉. - params: The parameters we are taking the gradient wrt: ω. - If a ``ParameterExpression```, ``ParameterVector`` or ``List[ParameterExpression]`` - is given, then the 1st order derivative of the operator is calculated. - If a ``Tuple[ParameterExpression, ParameterExpression]`` or - ``List[Tuple[ParameterExpression, ParameterExpression]]`` - is given, then the 2nd order derivative of the operator is calculated. - Returns: - The adapted operator. - Measurement operators are attached with an additional Z term acting - on an additional working qubit. - Quantum states - which must be given as circuits - are adapted. An additional - working qubit controls intercepting gates. - See e.g. [1]. - - Raises: - ValueError: If ``operator`` does not correspond to an expectation value. - TypeError: If the ``StateFn`` corresponding to the quantum state could not be extracted - from ``operator``. - OpflowError: If third or higher order gradients are requested. - - References: - [1]: Evaluating analytic gradients on quantum hardware - Maria Schuld, Ville Bergholm, Christian Gogolin, Josh Izaac, and Nathan Killoran - Phys. Rev. A 99, 032331 – Published 21 March 2019 - - """ - - if isinstance(operator, ComposedOp): - # Get the measurement and the state operator - if not isinstance(operator[0], StateFn) or not operator[0].is_measurement: - raise ValueError("The given operator does not correspond to an expectation value") - if not isinstance(operator[-1], StateFn) or operator[-1].is_measurement: - raise ValueError("The given operator does not correspond to an expectation value") - if operator[0].is_measurement: - meas = deepcopy(operator.oplist[0]) - meas = meas.primitive * meas.coeff - if len(operator.oplist) == 2: - state_op = operator[1] - if not isinstance(state_op, StateFn): - raise TypeError( - "The StateFn representing the quantum state could not be extracted." - ) - if isinstance(params, (ParameterExpression, ParameterVector)) or ( - isinstance(params, list) - and all(isinstance(param, ParameterExpression) for param in params) - ): - - return self._gradient_states( - state_op, - meas_op=(2 * meas), - target_params=params, - ) - elif isinstance(params, tuple) or ( - isinstance(params, list) - and all(isinstance(param, tuple) for param in params) - ): - return self._hessian_states( - state_op, - meas_op=(4 * (I ^ meas)), - target_params=params, - ) # type: ignore - else: - raise OpflowError( - "The linear combination gradient does only support the " - "computation of 1st gradients and 2nd order gradients." - ) - else: - state_op = deepcopy(operator) - state_op.oplist.pop(0) - if not isinstance(state_op, StateFn): - raise TypeError( - "The StateFn representing the quantum state could not be extracted." - ) - - if isinstance(params, (ParameterExpression, ParameterVector)) or ( - isinstance(params, list) - and all(isinstance(param, ParameterExpression) for param in params) - ): - return state_op.traverse( - partial( - self._gradient_states, - meas_op=(2 * meas), - target_params=params, - ) - ) - elif isinstance(params, tuple) or ( - isinstance(params, list) - and all(isinstance(param, tuple) for param in params) - ): - return state_op.traverse( - partial( - self._hessian_states, - meas_op=(4 * I ^ meas), - target_params=params, - ) - ) - - raise OpflowError( - "The linear combination gradient only supports the " - "computation of 1st and 2nd order gradients." - ) - else: - return operator.traverse(partial(self._prepare_operator, params=params)) - elif isinstance(operator, ListOp): - return operator.traverse(partial(self._prepare_operator, params=params)) - elif isinstance(operator, StateFn): - if operator.is_measurement: - return operator.traverse(partial(self._prepare_operator, params=params)) - else: - if isinstance(params, (ParameterExpression, ParameterVector)) or ( - isinstance(params, list) - and all(isinstance(param, ParameterExpression) for param in params) - ): - return self._gradient_states(operator, target_params=params) - elif isinstance(params, tuple) or ( - isinstance(params, list) and all(isinstance(param, tuple) for param in params) - ): - return self._hessian_states(operator, target_params=params) # type: ignore - else: - raise OpflowError( - "The linear combination gradient does only support the computation " - "of 1st gradients and 2nd order gradients." - ) - elif isinstance(operator, PrimitiveOp): - return operator - return operator - - @staticmethod - def _grad_combo_fn(x, state_op): - def get_result(item): - if isinstance(item, (DictStateFn, SparseVectorStateFn)): - item = item.primitive - if isinstance(item, VectorStateFn): - item = item.primitive.data - - if isinstance(item, dict): - prob_dict = {} - for key, val in item.items(): - prob_counts = val * np.conj(val) - if int(key[0]) == 1: - prob_counts *= -1 - suffix = key[1:] - prob_dict[suffix] = prob_dict.get(suffix, 0) + prob_counts - for key in prob_dict: - prob_dict[key] *= 2 - return prob_dict - elif isinstance(item, scipy.sparse.spmatrix): - # Generate the operator which computes the linear combination - trace = _z_exp(item) - return trace - elif isinstance(item, Iterable): - # Generate the operator which computes the linear combination - lin_comb_op = 2 * Z ^ (I ^ state_op.num_qubits) - lin_comb_op = lin_comb_op.to_matrix() - outer = np.outer(item, item.conj()) - return list( - np.diag(partial_trace(lin_comb_op.dot(outer), [state_op.num_qubits]).data) - ) - else: - raise TypeError( - "The state result should be either a DictStateFn or a VectorStateFn." - ) - - if not isinstance(x, Iterable): - return get_result(x) - elif len(x) == 1: - return get_result(x[0]) - else: - result = [] - for item in x: - result.append(get_result(item)) - return result - - @staticmethod - def _hess_combo_fn(x, state_op): - def get_result(item): - if isinstance(item, DictStateFn): - item = item.primitive - if isinstance(item, VectorStateFn): - item = item.primitive.data - if isinstance(item, Iterable): - # Generate the operator which computes the linear combination - lin_comb_op = 4 * (I ^ (state_op.num_qubits + 1)) ^ Z - lin_comb_op = lin_comb_op.to_matrix() - return list( - np.diag( - partial_trace(lin_comb_op.dot(np.outer(item, np.conj(item))), [0, 1]).data - ) - ) - elif isinstance(item, scipy.sparse.spmatrix): - # Generate the operator which computes the linear combination - trace = _z_exp(item) - return trace - elif isinstance(item, dict): - prob_dict = {} - for key, val in item.values(): - prob_counts = val * np.conj(val) - if int(key[-1]) == 1: - prob_counts *= -1 - prefix = key[:-2] - prob_dict[prefix] = prob_dict.get(prefix, 0) + prob_counts - for key in prob_dict: - prob_dict[key] *= 4 - return prob_dict - else: - raise TypeError( - "The state result should be either a DictStateFn or a VectorStateFn." - ) - - if not isinstance(x, Iterable): - return get_result(x) - elif len(x) == 1: - return get_result(x[0]) - else: - result = [] - for item in x: - result.append(get_result(item)) - return result - - @staticmethod - def _gate_gradient_dict(gate: Gate) -> List[Tuple[List[complex], List[Instruction]]]: - r"""Given a parameterized gate U(theta) with derivative - dU(theta)/dtheta = sum_ia_iU(theta)V_i. - This function returns a:=[a_0, ...] and V=[V_0, ...] - Suppose U takes multiple parameters, i.e., U(theta^0, ... theta^k). - The returned coefficients and gates are ordered accordingly. - Only parameterized Qiskit gates are supported. - - Args: - gate: The gate for which the derivative is being computed. - - Returns: - The coefficients and the gates used for the metric computation for each parameter of - the respective gates ``[([a^0], [V^0]) ..., ([a^k], [V^k])]``. - - Raises: - OpflowError: If the input gate is controlled by another state but '|1>^{\otimes k}' - TypeError: If the input gate is not a supported parameterized gate. - """ - - # pylint: disable=too-many-return-statements - if isinstance(gate, PhaseGate): - # theta - return [([0.5j, -0.5j], [IGate(), CZGate()])] - if isinstance(gate, UGate): - # theta, lambda, phi - return [([-0.5j], [CZGate()]), ([-0.5j], [CZGate()]), ([-0.5j], [CZGate()])] - if isinstance(gate, RXGate): - # theta - return [([-0.5j], [CXGate()])] - if isinstance(gate, RYGate): - # theta - return [([-0.5j], [CYGate()])] - if isinstance(gate, RZGate): - # theta - return [([-0.5j], [CZGate()])] - if isinstance(gate, RXXGate): - # theta - cxx_circ = QuantumCircuit(3) - cxx_circ.cx(0, 1) - cxx_circ.cx(0, 2) - cxx = cxx_circ.to_instruction() - return [([-0.5j], [cxx])] - if isinstance(gate, RYYGate): - # theta - cyy_circ = QuantumCircuit(3) - cyy_circ.cy(0, 1) - cyy_circ.cy(0, 2) - cyy = cyy_circ.to_instruction() - return [([-0.5j], [cyy])] - if isinstance(gate, RZZGate): - # theta - czz_circ = QuantumCircuit(3) - czz_circ.cz(0, 1) - czz_circ.cz(0, 2) - czz = czz_circ.to_instruction() - return [([-0.5j], [czz])] - if isinstance(gate, RZXGate): - # theta - czx_circ = QuantumCircuit(3) - czx_circ.cx(0, 2) - czx_circ.cz(0, 1) - czx = czx_circ.to_instruction() - return [([-0.5j], [czx])] - if isinstance(gate, ControlledGate): - # TODO support arbitrary control states - if gate.ctrl_state != 2**gate.num_ctrl_qubits - 1: - raise OpflowError( - "Function only support controlled gates with control state `1` on all control " - "qubits." - ) - - base_coeffs_gates = LinComb._gate_gradient_dict(gate.base_gate) - coeffs_gates = [] - # The projectors needed for the gradient of a controlled gate are integrated by a sum - # of gates. - # The following line generates the decomposition gates. - - proj_gates_controlled = [ - [(-1) ** p.count(ZGate()), p] - for p in product([IGate(), ZGate()], repeat=gate.num_ctrl_qubits) - ] - for base_coeffs, base_gates in base_coeffs_gates: # loop over parameters - coeffs = [] - gates = [] - for phase, proj_gates in proj_gates_controlled: - coeffs.extend([phase * c / (2**gate.num_ctrl_qubits) for c in base_coeffs]) - for base_gate in base_gates: - controlled_circ = QuantumCircuit(gate.num_ctrl_qubits + gate.num_qubits) - for i, proj_gate in enumerate(proj_gates): - if isinstance(proj_gate, ZGate): - controlled_circ.cz(0, i + 1) - if not isinstance(base_gate, IGate): - controlled_circ.append( - base_gate, - [ - 0, - range( - gate.num_ctrl_qubits + 1, - gate.num_ctrl_qubits + gate.num_qubits, - ), - ], - ) - gates.append(controlled_circ.to_instruction()) - c_g = (coeffs, gates) - coeffs_gates.append(c_g) - return coeffs_gates - raise TypeError(f"Unrecognized parameterized gate, {gate}") - - @staticmethod - def apply_grad_gate( - circuit, - gate, - param_index, - grad_gate, - grad_coeff, - qr_superpos, - open_ctrl=False, - trim_after_grad_gate=False, - ): - """Util function to apply a gradient gate for the linear combination of unitaries method. - Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as - superposition qubit. Also adds the appropriate sign-fix gates on the superposition qubit. - - Args: - circuit (QuantumCircuit): The circuit in which to do the replacements. - gate (Gate): The gate instance to replace. - param_index (int): The index of the parameter in ``gate``. - grad_gate (Gate): A controlled gate encoding the gradient of ``gate``. - grad_coeff (float): A coefficient to the gradient component. Might not be one if the - gradient contains multiple summed terms. - qr_superpos (QuantumRegister): A ``QuantumRegister`` of size 1 contained in ``circuit`` - that is used as control for ``grad_gate``. - open_ctrl (bool): If True use an open control for ``grad_gate`` instead of closed. - trim_after_grad_gate (bool): If True remove all gates after the ``grad_gate``. Can - be used to reduce the circuit depth in e.g. computing an overlap of gradients. - - Returns: - QuantumCircuit: A copy of the original circuit with the gradient gate added. - - Raises: - RuntimeError: If ``gate`` is not in ``circuit``. - """ - qr_superpos_qubits = tuple(qr_superpos) - # copy the input circuit taking the gates by reference - out = QuantumCircuit(*circuit.qregs) - out._data = circuit._data.copy() - out._parameter_table = ParameterTable( - {param: values.copy() for param, values in circuit._parameter_table.items()} - ) - - # get the data index and qubits of the target gate TODO use built-in - gate_idx, gate_qubits = None, None - for i, instruction in enumerate(out._data): - if instruction.operation is gate: - gate_idx, gate_qubits = i, instruction.qubits - break - if gate_idx is None: - raise RuntimeError("The specified gate could not be found in the circuit data.") - - # initialize replacement instructions - replacement = [] - - # insert the phase fix before the target gate better documentation - sign = np.sign(grad_coeff) - is_complex = np.iscomplex(grad_coeff) - - if sign < 0 and is_complex: - replacement.append(CircuitInstruction(SdgGate(), qr_superpos_qubits, ())) - elif sign < 0: - replacement.append(CircuitInstruction(ZGate(), qr_superpos_qubits, ())) - elif is_complex: - replacement.append(CircuitInstruction(SGate(), qr_superpos_qubits, ())) - # else no additional gate required - - # open control if specified - if open_ctrl: - replacement += [CircuitInstruction(XGate(), qr_superpos_qubits, [])] - - # compute the replacement - if isinstance(gate, UGate) and param_index == 0: - theta = gate.params[2] - rz_plus, rz_minus = RZGate(theta), RZGate(-theta) - replacement += [CircuitInstruction(rz_plus, (qubit,), ()) for qubit in gate_qubits] - replacement += [ - CircuitInstruction(RXGate(np.pi / 2), (qubit,), ()) for qubit in gate_qubits - ] - replacement.append(CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, [])) - replacement += [ - CircuitInstruction(RXGate(-np.pi / 2), (qubit,), ()) for qubit in gate_qubits - ] - replacement += [CircuitInstruction(rz_minus, (qubit,), ()) for qubit in gate_qubits] - - # update parametertable if necessary - if isinstance(theta, ParameterExpression): - # This dangerously subverts ParameterTable by abusing the fact that binding will - # mutate the exact instruction instance, and relies on all instances of `rz_plus` - # that were added before being the same in memory, which QuantumCircuit usually - # ensures is not the case. I'm leaving this as close to its previous form as - # possible, to avoid introducing further complications, but this whole method - # accesses internal attributes of `QuantumCircuit` and needs rewriting. - # - Jake Lishman, 2022-03-02. - out._update_parameter_table(CircuitInstruction(rz_plus, (gate_qubits[0],), ())) - out._update_parameter_table(CircuitInstruction(rz_minus, (gate_qubits[0],), ())) - - if open_ctrl: - replacement.append(CircuitInstruction(XGate(), qr_superpos_qubits, ())) - - if not trim_after_grad_gate: - replacement.append(CircuitInstruction(gate, gate_qubits, ())) - - elif isinstance(gate, UGate) and param_index == 1: - # gradient gate is applied after the original gate in this case - replacement.append(CircuitInstruction(gate, gate_qubits, ())) - replacement.append(CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, ())) - if open_ctrl: - replacement.append(CircuitInstruction(XGate(), qr_superpos_qubits, ())) - - else: - replacement.append(CircuitInstruction(grad_gate, qr_superpos_qubits + gate_qubits, ())) - if open_ctrl: - replacement.append(CircuitInstruction(XGate(), qr_superpos_qubits, ())) - if not trim_after_grad_gate: - replacement.append(CircuitInstruction(gate, gate_qubits, ())) - - # replace the parameter we compute the derivative of with the replacement - # TODO can this be done more efficiently? - if trim_after_grad_gate: # remove everything after the gradient gate - out._data[gate_idx:] = replacement - # reset parameter table - table = ParameterTable() - for instruction in out._data: - for idx, param_expression in enumerate(instruction.operation.params): - if isinstance(param_expression, ParameterExpression): - for param in param_expression.parameters: - if param not in table.keys(): - table[param] = ParameterReferences(((instruction.operation, idx),)) - else: - table[param].add((instruction.operation, idx)) - - out._parameter_table = table - - else: - out._data[gate_idx : gate_idx + 1] = replacement - - return out - - def _aux_meas_basis_trafo( - self, aux_meas_op: OperatorBase, state: StateFn, state_op: StateFn, combo_fn: Callable - ) -> ListOp: - """ - This function applies the necessary basis transformation to measure the quantum state in - a different basis -- given by the auxiliary measurement operator ``aux_meas_op``. - - Args: - aux_meas_op: The auxiliary measurement operator defines the necessary measurement basis. - state: This operator represents the gradient or Hessian before the basis transformation. - state_op: The operator representing the quantum state for which we compute the gradient - or Hessian. - combo_fn: This ``combo_fn`` defines whether the target is a gradient or Hessian. - - - Returns: - Operator representing the gradient or Hessian. - - Raises: - ValueError: If ``aux_meas_op`` is neither ``Z`` nor ``-Y`` nor ``Z - 1j * Y``. - - """ - if aux_meas_op == Z - 1j * Y: - state_z = ListOp( - [state], - combo_fn=partial(combo_fn, state_op=state_op), - ) - pbc = PauliBasisChange(replacement_fn=PauliBasisChange.measurement_replacement_fn) - pbc = pbc.convert(-Y ^ (I ^ (state.num_qubits - 1))) - state_y = pbc[-1] @ state - state_y = ListOp( - [state_y], - combo_fn=partial(combo_fn, state_op=state_op), - ) - return state_z - 1j * state_y - - elif aux_meas_op == -Y: - pbc = PauliBasisChange(replacement_fn=PauliBasisChange.measurement_replacement_fn) - pbc = pbc.convert(aux_meas_op ^ (I ^ (state.num_qubits - 1))) - state = pbc[-1] @ state - return -1 * ListOp( - [state], - combo_fn=partial(combo_fn, state_op=state_op), - ) - elif aux_meas_op == Z: - return ListOp( - [state], - combo_fn=partial(combo_fn, state_op=state_op), - ) - else: - raise ValueError( - f"The auxiliary measurement operator passed {aux_meas_op} is not supported. " - "Only Y, Z, or Z - 1j * Y are valid." - ) - - def _gradient_states( - self, - state_op: StateFn, - meas_op: Optional[OperatorBase] = None, - target_params: Optional[Union[Parameter, List[Parameter]]] = None, - open_ctrl: bool = False, - trim_after_grad_gate: bool = False, - ) -> ListOp: - """Generate the gradient states. - - Args: - state_op: The operator representing the quantum state for which we compute the gradient. - meas_op: The operator representing the observable for which we compute the gradient. - target_params: The parameters we are taking the gradient wrt: ω - open_ctrl: If True use an open control for ``grad_gate`` instead of closed. - trim_after_grad_gate: If True remove all gates after the ``grad_gate``. Can - be used to reduce the circuit depth in e.g. computing an overlap of gradients. - - Returns: - ListOp of StateFns as quantum circuits which are the states w.r.t. which we compute the - gradient. If a parameter appears multiple times, one circuit is created per - parameterized gates to compute the product rule. - - Raises: - QiskitError: If one of the circuits could not be constructed. - TypeError: If the operators is of unsupported type. - ValueError: If the auxiliary operator preparation fails. - """ - # unroll separately from the H gate since we need the H gate to be the first - # operation in the data attributes of the circuit - unrolled = self._transpile_to_supported_operations(state_op.primitive, self.SUPPORTED_GATES) - qr_superpos = QuantumRegister(1) - state_qc = QuantumCircuit(*state_op.primitive.qregs, qr_superpos) - state_qc.h(qr_superpos) - - state_qc.compose(unrolled, inplace=True) - - # Define the working qubit to realize the linear combination of unitaries - if not isinstance(target_params, (list, np.ndarray)): - target_params = [target_params] - - oplist = [] - for param in target_params: - if param not in state_qc.parameters: - oplist += [~Zero @ One] - else: - param_gates = state_qc._parameter_table[param] - sub_oplist = [] - for gate, idx in param_gates: - grad_coeffs, grad_gates = self._gate_gradient_dict(gate)[idx] - - # construct the states - for grad_coeff, grad_gate in zip(grad_coeffs, grad_gates): - grad_circuit = self.apply_grad_gate( - state_qc, - gate, - idx, - grad_gate, - grad_coeff, - qr_superpos, - open_ctrl, - trim_after_grad_gate, - ) - # apply final Hadamard on superposition qubit - grad_circuit.h(qr_superpos) - - # compute the correct coefficient and append to list of circuits - coeff = np.sqrt(np.abs(grad_coeff)) * state_op.coeff - state = CircuitStateFn(grad_circuit, coeff=coeff) - - # apply the chain rule if the parameter expression if required - param_expression = gate.params[idx] - - if isinstance(meas_op, OperatorBase): - state = ( - StateFn(self._aux_meas_op ^ meas_op, is_measurement=True) @ state - ) - - else: - state = self._aux_meas_basis_trafo( - self._aux_meas_op, state, state_op, self._grad_combo_fn - ) - - if param_expression != param: # parameter is not identity, apply chain rule - param_grad = param_expression.gradient(param) - state *= param_grad - - sub_oplist += [state] - - oplist += [SummedOp(sub_oplist) if len(sub_oplist) > 1 else sub_oplist[0]] - - return ListOp(oplist) if len(oplist) > 1 else oplist[0] - - def _hessian_states( - self, - state_op: StateFn, - meas_op: Optional[OperatorBase] = None, - target_params: Optional[ - Union[ - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - ] - ] = None, - ) -> OperatorBase: - """Generate the operator states whose evaluation returns the Hessian (items). - - Args: - state_op: The operator representing the quantum state for which we compute the Hessian. - meas_op: The operator representing the observable for which we compute the gradient. - target_params: The parameters we are computing the Hessian wrt: ω - - Returns: - Operators which give the Hessian. If a parameter appears multiple times, one circuit is - created per parameterized gates to compute the product rule. - - Raises: - QiskitError: If one of the circuits could not be constructed. - TypeError: If ``operator`` is of unsupported type. - ValueError: If the auxiliary operator preparation fails. - """ - if not isinstance(target_params, list): - target_params = [target_params] - - if not all(isinstance(params, tuple) for params in target_params): - raise TypeError( - "Please define in the parameters for which the Hessian is evaluated " - "either as parameter tuple or a list of parameter tuples" - ) - - # create circuit with two additional qubits - qr_add0 = QuantumRegister(1, "s0") - qr_add1 = QuantumRegister(1, "s1") - state_qc = QuantumCircuit(*state_op.primitive.qregs, qr_add0, qr_add1) - - # add Hadamards - state_qc.h(qr_add0) - state_qc.h(qr_add1) - - # compose with the original circuit - state_qc.compose(state_op.primitive, inplace=True) - - # create a copy of the original circuit with an additional working qubit register - oplist = [] - for param_a, param_b in target_params: - if param_a not in state_qc.parameters or param_b not in state_qc.parameters: - oplist += [~Zero @ One] - else: - sub_oplist = [] - param_gates_a = state_qc._parameter_table[param_a] - param_gates_b = state_qc._parameter_table[param_b] - for gate_a, idx_a in param_gates_a: - grad_coeffs_a, grad_gates_a = self._gate_gradient_dict(gate_a)[idx_a] - - for grad_coeff_a, grad_gate_a in zip(grad_coeffs_a, grad_gates_a): - grad_circuit = self.apply_grad_gate( - state_qc, gate_a, idx_a, grad_gate_a, grad_coeff_a, qr_add0 - ) - - for gate_b, idx_b in param_gates_b: - grad_coeffs_b, grad_gates_b = self._gate_gradient_dict(gate_b)[idx_b] - - for grad_coeff_b, grad_gate_b in zip(grad_coeffs_b, grad_gates_b): - hessian_circuit = self.apply_grad_gate( - grad_circuit, gate_b, idx_b, grad_gate_b, grad_coeff_b, qr_add1 - ) - - # final Hadamards and CZ - hessian_circuit.h(qr_add0) - hessian_circuit.cz(qr_add1[0], qr_add0[0]) - hessian_circuit.h(qr_add1) - - coeff = state_op.coeff - coeff *= np.sqrt(np.abs(grad_coeff_a) * np.abs(grad_coeff_b)) - state = CircuitStateFn(hessian_circuit, coeff=coeff) - - if meas_op is not None: - state = ( - StateFn(self._aux_meas_op ^ meas_op, is_measurement=True) - @ state - ) - else: - state = self._aux_meas_basis_trafo( - self._aux_meas_op, state, state_op, self._hess_combo_fn - ) - - # Chain Rule Parameter Expression - param_grad = 1 - for gate, idx, param in zip( - [gate_a, gate_b], [idx_a, idx_b], [param_a, param_b] - ): - param_expression = gate.params[idx] - if param_expression != param: # need to apply chain rule - param_grad *= param_expression.gradient(param) - - if param_grad != 1: - state *= param_grad - - sub_oplist += [state] - - oplist += [SummedOp(sub_oplist) if len(sub_oplist) > 1 else sub_oplist[0]] - - return ListOp(oplist) if len(oplist) > 1 else oplist[0] - - -def _z_exp(spmatrix): - """Compute the sampling probabilities of the qubits after applying measurement on the - auxiliary qubit.""" - - dok = spmatrix.todok() - num_qubits = int(np.log2(dok.shape[1])) - exp = scipy.sparse.dok_matrix((1, 2 ** (num_qubits - 1))) - - for index, amplitude in dok.items(): - binary = bin(index[1])[2:].zfill(num_qubits) - sign = -1 if binary[0] == "1" else 1 - new_index = int(binary[1:], 2) - exp[(0, new_index)] = exp[(0, new_index)] + 2 * sign * np.abs(amplitude) ** 2 - - return exp diff --git a/qiskit/opflow/gradients/circuit_gradients/param_shift.py b/qiskit/opflow/gradients/circuit_gradients/param_shift.py deleted file mode 100644 index 5329cc4c14c0..000000000000 --- a/qiskit/opflow/gradients/circuit_gradients/param_shift.py +++ /dev/null @@ -1,429 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module to compute the state gradient with the parameter shift rule.""" - -from collections.abc import Iterable -from copy import deepcopy -from functools import partial -from typing import List, Union, Tuple, Dict - -import scipy -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter, ParameterExpression, ParameterVector -from qiskit.utils.deprecation import deprecate_func -from .circuit_gradient import CircuitGradient -from ...operator_base import OperatorBase -from ...state_fns.state_fn import StateFn -from ...operator_globals import Zero, One -from ...state_fns.circuit_state_fn import CircuitStateFn -from ...primitive_ops.circuit_op import CircuitOp -from ...list_ops.summed_op import SummedOp -from ...list_ops.list_op import ListOp -from ...list_ops.composed_op import ComposedOp -from ...state_fns.dict_state_fn import DictStateFn -from ...state_fns.vector_state_fn import VectorStateFn -from ...state_fns.sparse_vector_state_fn import SparseVectorStateFn -from ...exceptions import OpflowError -from ..derivative_base import _coeff_derivative - - -class ParamShift(CircuitGradient): - """Deprecated: Compute the gradient d⟨ψ(ω)|O(θ)|ψ(ω)〉/ dω respectively the gradients of the sampling - probabilities of the basis states of a state |ψ(ω)〉w.r.t. ω with the parameter shift - method. - """ - - SUPPORTED_GATES = {"x", "y", "z", "h", "rx", "ry", "rz", "p", "u", "cx", "cy", "cz"} - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, analytic: bool = True, epsilon: float = 1e-6): - r""" - Args: - analytic: If True use the parameter shift rule to compute analytic gradients, - else use a finite difference approach - epsilon: The offset size to use when computing finite difference gradients. - Ignored if analytic == True - - Raises: - ValueError: If method != ``fin_diff`` and ``epsilon`` is not None. - """ - super().__init__() - self._analytic = analytic - self._epsilon = epsilon - - @property - def analytic(self) -> bool: - """Returns ``analytic`` flag. - - Returns: - ``analytic`` flag. - - """ - return self._analytic - - @property - def epsilon(self) -> float: - """Returns ``epsilon``. - - Returns: - ``epsilon``. - - """ - return self._epsilon - - # pylint: disable=signature-differs - def convert( - self, - operator: OperatorBase, - params: Union[ - ParameterExpression, - ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - ], - ) -> OperatorBase: - """ - Args: - operator: The operator corresponding to our quantum state we are taking the - gradient of: |ψ(ω)〉 - params: The parameters we are taking the gradient wrt: ω - If a ParameterExpression, ParameterVector or List[ParameterExpression] is given, - then the 1st order derivative of the operator is calculated. - If a Tuple[ParameterExpression, ParameterExpression] or - List[Tuple[ParameterExpression, ParameterExpression]] - is given, then the 2nd order derivative of the operator is calculated. - - Returns: - An operator corresponding to the gradient resp. Hessian. The order is in accordance with - the order of the given parameters. - - Raises: - OpflowError: If the parameters are given in an invalid format. - - """ - if isinstance(params, (ParameterExpression, ParameterVector)): - return self._parameter_shift(operator, params) - elif isinstance(params, tuple): - return self._parameter_shift(self._parameter_shift(operator, params[0]), params[1]) - elif isinstance(params, Iterable): - if all(isinstance(param, ParameterExpression) for param in params): - return self._parameter_shift(operator, params) - elif all(isinstance(param, tuple) for param in params): - return ListOp( - [ - self._parameter_shift(self._parameter_shift(operator, pair[0]), pair[1]) - for pair in params - ] - ) - else: - raise OpflowError( - "The linear combination gradient does only support " - "the computation " - "of 1st gradients and 2nd order gradients." - ) - else: - raise OpflowError( - "The linear combination gradient does only support the computation " - "of 1st gradients and 2nd order gradients." - ) - - # pylint: disable=too-many-return-statements - def _parameter_shift( - self, operator: OperatorBase, params: Union[ParameterExpression, ParameterVector, List] - ) -> OperatorBase: - r""" - Args: - operator: The operator containing circuits we are taking the derivative of. - params: The parameters (ω) we are taking the derivative with respect to. If - a ParameterVector is provided, each parameter will be shifted. - - Returns: - param_shifted_op: An operator object which evaluates to the respective gradients. - - Raises: - ValueError: If the given parameters do not occur in the provided operator - TypeError: If the operator has more than one circuit representing the quantum state - """ - if isinstance(params, (ParameterVector, list)): - param_grads = [self._parameter_shift(operator, param) for param in params] - absent_params = [ - params[i] for i, grad_ops in enumerate(param_grads) if grad_ops is None - ] - if len(absent_params) > 0: - raise ValueError( - "The following parameters do not appear in the provided operator: ", - absent_params, - ) - return ListOp(absent_params) - - # By this point, it's only one parameter - param = params - - if isinstance(operator, ListOp) and not isinstance(operator, ComposedOp): - return_op = operator.traverse(partial(self._parameter_shift, params=param)) - - # Remove any branch of the tree where the relevant parameter does not occur - trimmed_oplist = [op for op in return_op.oplist if op is not None] - # If all branches are None, remove the parent too - if len(trimmed_oplist) == 0: - return None - # Rebuild the operator with the trimmed down oplist - properties = {"coeff": return_op._coeff, "abelian": return_op._abelian} - if return_op.__class__ == ListOp: - properties["combo_fn"] = return_op.combo_fn - return return_op.__class__(oplist=trimmed_oplist, **properties) - - else: - circs = self.get_unique_circuits(operator) - - if len(circs) > 1: - raise TypeError( - "Please define an operator with a single circuit representing " - "the quantum state." - ) - if len(circs) == 0: - return operator - circ = circs[0] - - if self.analytic: - # Unroll the circuit into a gate set for which the gradient may be computed - # using pi/2 shifts. - circ = ParamShift._transpile_to_supported_operations(circ, self.SUPPORTED_GATES) - operator = ParamShift._replace_operator_circuit(operator, circ) - - if param not in circ._parameter_table: - return ~Zero @ One - - shifted_ops = [] - summed_shifted_op = None - - iref_to_data_index = {id(inst.operation): idx for idx, inst in enumerate(circ.data)} - - for param_reference in circ._parameter_table[param]: - original_gate, param_index = param_reference - m = iref_to_data_index[id(original_gate)] - - pshift_op = deepcopy(operator) - mshift_op = deepcopy(operator) - - # We need the circuit objects of the newly instantiated operators - pshift_circ = self.get_unique_circuits(pshift_op)[0] - mshift_circ = self.get_unique_circuits(mshift_op)[0] - - pshift_gate = pshift_circ.data[m].operation - mshift_gate = mshift_circ.data[m].operation - - p_param = pshift_gate.params[param_index] - m_param = mshift_gate.params[param_index] - # For analytic gradients the circuit parameters are shifted once by +pi/2 and - # once by -pi/2. - if self.analytic: - shift_constant = 0.5 - pshift_gate.params[param_index] = p_param + (np.pi / (4 * shift_constant)) - mshift_gate.params[param_index] = m_param - (np.pi / (4 * shift_constant)) - # For finite difference gradients the circuit parameters are shifted once by - # +epsilon and once by -epsilon. - else: - shift_constant = 1.0 / (2 * self._epsilon) - pshift_gate.params[param_index] = p_param + self._epsilon - mshift_gate.params[param_index] = m_param - self._epsilon - # The results of the shifted operators are now evaluated according the parameter - # shift / finite difference formula. - if isinstance(operator, ComposedOp): - shifted_op = shift_constant * (pshift_op - mshift_op) - # If the operator represents a quantum state then we apply a special combo - # function to evaluate probability gradients. - elif isinstance(operator, StateFn): - shifted_op = ListOp( - [pshift_op, mshift_op], - combo_fn=partial(self._prob_combo_fn, shift_constant=shift_constant), - ) - else: - raise TypeError( - "Probability gradients are not supported for the given operator type" - ) - - if isinstance(p_param, ParameterExpression) and not isinstance(p_param, Parameter): - expr_grad = _coeff_derivative(p_param, param) - shifted_op *= expr_grad - if not summed_shifted_op: - summed_shifted_op = shifted_op - else: - summed_shifted_op += shifted_op - - shifted_ops.append(summed_shifted_op) - - if not SummedOp(shifted_ops).reduce(): - return ~StateFn(Zero) @ One - else: - return SummedOp(shifted_ops).reduce() - - @staticmethod - def _prob_combo_fn( - x: Union[ - DictStateFn, - VectorStateFn, - SparseVectorStateFn, - List[Union[DictStateFn, VectorStateFn, SparseVectorStateFn]], - ], - shift_constant: float, - ) -> Union[Dict, np.ndarray]: - """Implement the combo_fn used to evaluate probability gradients - - Args: - x: Output of an operator evaluation - shift_constant: Shifting constant factor needed for proper rescaling - - Returns: - Array representing the probability gradients w.r.t. the given operator and parameters - - Raises: - TypeError: if ``x`` is not DictStateFn, VectorStateFn or their list. - - """ - # Note: In the probability gradient case, the amplitudes still need to be converted - # into sampling probabilities. - - def get_primitives(item): - if isinstance(item, (DictStateFn, SparseVectorStateFn)): - item = item.primitive - if isinstance(item, VectorStateFn): - item = item.primitive.data - return item - - is_statefn = False - if isinstance(x, list): - # Check if all items in x are a StateFn items - if all(isinstance(item, StateFn) for item in x): - is_statefn = True - items = [get_primitives(item) for item in x] - else: - # Check if x is a StateFn item - if isinstance(x, StateFn): - is_statefn = True - items = [get_primitives(x)] - if isinstance(items[0], dict): - prob_dict: Dict[str, float] = {} - for i, item in enumerate(items): - for key, prob_counts in item.items(): - prob_dict[key] = ( - prob_dict.get(key, 0) + shift_constant * ((-1) ** i) * prob_counts - ) - return prob_dict - elif isinstance(items[0], scipy.sparse.spmatrix): - # If x was given as StateFn the state amplitudes need to be multiplied in order to - # evaluate the sampling probabilities which are then subtracted according to the - # parameter shift rule. - if is_statefn: - return shift_constant * np.subtract( - items[0].multiply(np.conj(items[0])), items[1].multiply(np.conj(items[1])) - ) - # If x was not given as a StateFn the state amplitudes were already converted into - # sampling probabilities which are then only subtracted according to the - # parameter shift rule. - else: - return shift_constant * np.subtract(items[0], items[1]) - elif isinstance(items[0], Iterable): - # If x was given as StateFn the state amplitudes need to be multiplied in order to - # evaluate the sampling probabilities which are then subtracted according to the - # parameter shift rule. - if is_statefn: - return shift_constant * np.subtract( - np.multiply(items[0], np.conj(items[0])), - np.multiply(items[1], np.conj(items[1])), - ) - # If x was not given as a StateFn the state amplitudes were already converted into - # sampling probabilities which are then only subtracted according to the - # parameter shift rule. - else: - return shift_constant * np.subtract(items[0], items[1]) - raise TypeError( - "Probability gradients can only be evaluated from VectorStateFs or DictStateFns." - ) - - @staticmethod - def _replace_operator_circuit(operator: OperatorBase, circuit: QuantumCircuit) -> OperatorBase: - """Replace a circuit element in an operator with a single element given as circuit - - Args: - operator: Operator for which the circuit representing the quantum state shall be - replaced - circuit: Circuit which shall replace the circuit in the given operator - - Returns: - Operator with replaced circuit quantum state function - - """ - if isinstance(operator, CircuitStateFn): - return CircuitStateFn(circuit, coeff=operator.coeff) - elif isinstance(operator, CircuitOp): - return CircuitOp(circuit, coeff=operator.coeff) - elif isinstance(operator, (ComposedOp, ListOp)): - return operator.traverse(partial(ParamShift._replace_operator_circuit, circuit=circuit)) - else: - return operator - - @classmethod - def get_unique_circuits(cls, operator: OperatorBase) -> List[QuantumCircuit]: - """Traverse the operator and return all unique circuits - - Args: - operator: An operator that potentially includes QuantumCircuits - - Returns: - A list of all unique quantum circuits that appear in the operator - - """ - if isinstance(operator, CircuitStateFn): - return [operator.primitive] - - def get_circuit(op): - return op.primitive if isinstance(op, (CircuitStateFn, CircuitOp)) else None - - unrolled_op = cls.unroll_operator(operator) - circuits = [] - for ops in unrolled_op: - if not isinstance(ops, list): - ops = [ops] - for op in ops: - if isinstance(op, (CircuitStateFn, CircuitOp, QuantumCircuit)): - c = get_circuit(op) - if c and c not in circuits: - circuits.append(c) - return circuits - - @classmethod - def unroll_operator(cls, operator: OperatorBase) -> Union[OperatorBase, List[OperatorBase]]: - """Traverse the operator and return all OperatorBase objects flattened - into a single list. This is used as a subroutine to extract all - circuits within a large composite operator. - - Args: - operator: An OperatorBase type object - - Returns: - A single flattened list of all OperatorBase objects within the - input operator - - """ - if isinstance(operator, ListOp): - return [cls.unroll_operator(op) for op in operator] - if hasattr(operator, "primitive") and isinstance(operator.primitive, ListOp): - return [operator.__class__(op) for op in operator.primitive] - return operator diff --git a/qiskit/opflow/gradients/circuit_qfis/__init__.py b/qiskit/opflow/gradients/circuit_qfis/__init__.py deleted file mode 100644 index d32126acd523..000000000000 --- a/qiskit/opflow/gradients/circuit_qfis/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module for first order derivatives.""" - -from .circuit_qfi import CircuitQFI -from .lin_comb_full import LinCombFull -from .overlap_diag import OverlapDiag -from .overlap_block_diag import OverlapBlockDiag - -__all__ = ["CircuitQFI", "LinCombFull", "OverlapDiag", "OverlapBlockDiag"] diff --git a/qiskit/opflow/gradients/circuit_qfis/circuit_qfi.py b/qiskit/opflow/gradients/circuit_qfis/circuit_qfi.py deleted file mode 100644 index 9a11d619e6c7..000000000000 --- a/qiskit/opflow/gradients/circuit_qfis/circuit_qfi.py +++ /dev/null @@ -1,65 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""CircuitQFI Class""" - -from abc import abstractmethod -from typing import List, Union - -from qiskit.circuit import ParameterExpression, ParameterVector -from qiskit.utils.deprecation import deprecate_func -from ...converters.converter_base import ConverterBase -from ...operator_base import OperatorBase - - -class CircuitQFI(ConverterBase): - r"""Deprecated: Circuit to Quantum Fisher Information operator converter. - - Converter for changing parameterized circuits into operators - whose evaluation yields Quantum Fisher Information metric tensor - with respect to the given circuit parameters - - This is distinct from DerivativeBase converters which take gradients of composite - operators and handle things like differentiating combo_fn's and enforcing product rules - when operator coefficients are parameterized. - - CircuitQFI - uses quantum techniques to get the QFI of circuits - DerivativeBase - uses classical techniques to differentiate opflow data structures - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - # pylint: disable=arguments-differ - @abstractmethod - def convert( - self, - operator: OperatorBase, - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], - ) -> OperatorBase: - r""" - Args: - operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` - for which we compute the QFI. - params: The parameters :math:`\omega` with respect to which we are computing the QFI. - - Returns: - An operator whose evaluation yields the QFI metric tensor. - - Raises: - ValueError: If ``params`` contains a parameter not present in ``operator``. - """ - raise NotImplementedError diff --git a/qiskit/opflow/gradients/circuit_qfis/lin_comb_full.py b/qiskit/opflow/gradients/circuit_qfis/lin_comb_full.py deleted file mode 100644 index 71f4eea3d8c2..000000000000 --- a/qiskit/opflow/gradients/circuit_qfis/lin_comb_full.py +++ /dev/null @@ -1,227 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module for Quantum the Fisher Information.""" - -from typing import List, Union - -import numpy as np -from qiskit.circuit import QuantumCircuit, QuantumRegister, ParameterVector, ParameterExpression -from qiskit.utils.arithmetic import triu_to_dense -from qiskit.utils.deprecation import deprecate_func -from ...operator_base import OperatorBase -from ...list_ops.list_op import ListOp -from ...list_ops.summed_op import SummedOp -from ...operator_globals import I, Z, Y -from ...state_fns.state_fn import StateFn -from ...state_fns.circuit_state_fn import CircuitStateFn -from ..circuit_gradients.lin_comb import LinComb -from .circuit_qfi import CircuitQFI - - -class LinCombFull(CircuitQFI): - r"""Deprecated: Compute the full Quantum Fisher Information (QFI). - - Given a pure, parameterized quantum state this class uses the linear combination of unitaries - See also :class:`~qiskit.opflow.QFI`. - """ - - # pylint: disable=signature-differs, arguments-differ - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - aux_meas_op: OperatorBase = Z, - phase_fix: bool = True, - ): - """ - Args: - aux_meas_op: The operator that the auxiliary qubit is measured with respect to. - For ``aux_meas_op = Z`` we compute 4Re[(dω⟨ψ(ω)|)O(θ)|ψ(ω)〉], - for ``aux_meas_op = -Y`` we compute 4Im[(dω⟨ψ(ω)|)O(θ)|ψ(ω)〉], and - for ``aux_meas_op = Z - 1j * Y`` we compute 4(dω⟨ψ(ω)|)O(θ)|ψ(ω)〉. - phase_fix: Whether or not to compute and add the additional phase fix term - Re[(dω⟨<ψ(ω)|)|ψ(ω)><ψ(ω)|(dω|ψ(ω))>]. - Raises: - ValueError: If the provided auxiliary measurement operator is not supported. - """ - super().__init__() - if aux_meas_op not in [Z, -Y, (Z - 1j * Y)]: - raise ValueError( - "This auxiliary measurement operator is currently not supported. Please choose " - "either Z, -Y, or Z - 1j * Y. " - ) - self._aux_meas_op = aux_meas_op - self._phase_fix = phase_fix - - def convert( - self, - operator: CircuitStateFn, - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], - ) -> ListOp: - r""" - Args: - operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` - for which we compute the QFI. - params: The parameters :math:`\omega` with respect to which we are computing the QFI. - Returns: - A ``ListOp[ListOp]`` where the operator at position ``[k][l]`` corresponds to the matrix - element :math:`k, l` of the QFI. - - Raises: - TypeError: If ``operator`` is an unsupported type. - """ - # QFI & phase fix observable - qfi_observable = StateFn( - 4 * self._aux_meas_op ^ (I ^ operator.num_qubits), is_measurement=True - ) - - # Check if the given operator corresponds to a quantum state given as a circuit. - if not isinstance(operator, CircuitStateFn): - raise TypeError( - "LinCombFull is only compatible with states that are given as " - f"CircuitStateFn, not {type(operator)}" - ) - - # If a single parameter is given wrap it into a list. - if isinstance(params, ParameterExpression): - params = [params] - elif isinstance(params, ParameterVector): - params = params[:] # unroll to list - - if self._phase_fix: - # First, the operators are computed which can compensate for a potential phase-mismatch - # between target and trained state, i.e.〈ψ|∂lψ〉 - phase_fix_observable = I ^ operator.num_qubits - gradient_states = LinComb(aux_meas_op=(Z - 1j * Y))._gradient_states( - operator, - meas_op=phase_fix_observable, - target_params=params, - open_ctrl=False, - trim_after_grad_gate=True, - ) - - # pylint: disable=unidiomatic-typecheck - if type(gradient_states) == ListOp: - phase_fix_states = gradient_states.oplist - else: - phase_fix_states = [gradient_states] - - # Get 4 * Re[〈∂kψ|∂lψ] - qfi_operators = [] - # Add a working qubit - qr_work = QuantumRegister(1, "work_qubit") - state_qc = QuantumCircuit(*operator.primitive.qregs, qr_work) - state_qc.h(qr_work) - # unroll separately from the H gate since we need the H gate to be the first - # operation in the data attributes of the circuit - unrolled = LinComb._transpile_to_supported_operations( - operator.primitive, LinComb.SUPPORTED_GATES - ) - state_qc.compose(unrolled, inplace=True) - - # Get the circuits needed to compute〈∂iψ|∂jψ〉 - for i, param_i in enumerate(params): # loop over parameters - qfi_ops = [] - for j, param_j in enumerate(params[i:], i): - # Get the gates of the quantum state which are parameterized by param_i - qfi_op = [] - param_gates_i = state_qc._parameter_table[param_i] - for gate_i, idx_i in param_gates_i: - grad_coeffs_i, grad_gates_i = LinComb._gate_gradient_dict(gate_i)[idx_i] - - # get the location of gate_i, used for trimming - location_i = None - for idx, instruction in enumerate(state_qc._data): - if instruction.operation is gate_i: - location_i = idx - break - - for grad_coeff_i, grad_gate_i in zip(grad_coeffs_i, grad_gates_i): - - # Get the gates of the quantum state which are parameterized by param_j - param_gates_j = state_qc._parameter_table[param_j] - for gate_j, idx_j in param_gates_j: - grad_coeffs_j, grad_gates_j = LinComb._gate_gradient_dict(gate_j)[idx_j] - - # get the location of gate_j, used for trimming - location_j = None - for idx, instruction in enumerate(state_qc._data): - if instruction.operation is gate_j: - location_j = idx - break - - for grad_coeff_j, grad_gate_j in zip(grad_coeffs_j, grad_gates_j): - - grad_coeff_ij = np.conj(grad_coeff_i) * grad_coeff_j - qfi_circuit = LinComb.apply_grad_gate( - state_qc, - gate_i, - idx_i, - grad_gate_i, - grad_coeff_ij, - qr_work, - open_ctrl=True, - trim_after_grad_gate=(location_j < location_i), - ) - - # create a copy of the original circuit with the same registers - qfi_circuit = LinComb.apply_grad_gate( - qfi_circuit, - gate_j, - idx_j, - grad_gate_j, - 1, - qr_work, - open_ctrl=False, - trim_after_grad_gate=(location_j >= location_i), - ) - - qfi_circuit.h(qr_work) - # Convert the quantum circuit into a CircuitStateFn and add the - # coefficients i, j and the original operator coefficient - coeff = operator.coeff - coeff *= np.sqrt(np.abs(grad_coeff_i) * np.abs(grad_coeff_j)) - state = CircuitStateFn(qfi_circuit, coeff=coeff) - - param_grad = 1 - for gate, idx, param in zip( - [gate_i, gate_j], [idx_i, idx_j], [param_i, param_j] - ): - param_expression = gate.params[idx] - param_grad *= param_expression.gradient(param) - meas = param_grad * qfi_observable - - term = meas @ state - - qfi_op.append(term) - - # Compute −4 * Re(〈∂kψ|ψ〉〈ψ|∂lψ〉) - def phase_fix_combo_fn(x): - return -4 * np.real(x[0] * np.conjugate(x[1])) - - if self._phase_fix: - phase_fix_op = ListOp( - [phase_fix_states[i], phase_fix_states[j]], combo_fn=phase_fix_combo_fn - ) - # Add the phase fix quantities to the entries of the QFI - # Get 4 * Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] - qfi_ops += [SummedOp(qfi_op) + phase_fix_op] - else: - qfi_ops += [SummedOp(qfi_op)] - - qfi_operators.append(ListOp(qfi_ops)) - - # Return estimate of the full QFI -- A QFI is by definition positive semi-definite. - return ListOp(qfi_operators, combo_fn=triu_to_dense) diff --git a/qiskit/opflow/gradients/circuit_qfis/overlap_block_diag.py b/qiskit/opflow/gradients/circuit_qfis/overlap_block_diag.py deleted file mode 100644 index 86b0bd1094cf..000000000000 --- a/qiskit/opflow/gradients/circuit_qfis/overlap_block_diag.py +++ /dev/null @@ -1,194 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module for the Quantum Fisher Information.""" - -from typing import List, Union - -import numpy as np -from scipy.linalg import block_diag -from qiskit.circuit import Parameter, ParameterVector, ParameterExpression -from qiskit.utils.arithmetic import triu_to_dense -from qiskit.utils.deprecation import deprecate_func -from ...list_ops.list_op import ListOp -from ...primitive_ops.circuit_op import CircuitOp -from ...expectations.pauli_expectation import PauliExpectation -from ...operator_globals import Zero -from ...state_fns.state_fn import StateFn -from ...state_fns.circuit_state_fn import CircuitStateFn -from ...exceptions import OpflowError - -from .circuit_qfi import CircuitQFI -from ..derivative_base import _coeff_derivative -from .overlap_diag import _get_generators, _partition_circuit - - -class OverlapBlockDiag(CircuitQFI): - r"""Deprecated: Compute the block-diagonal of the QFI given a pure, parameterized quantum state. - - The blocks are given by all parameterized gates in quantum circuit layer. - See also :class:`~qiskit.opflow.QFI`. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - def convert( - self, - operator: Union[CircuitOp, CircuitStateFn], - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], - ) -> ListOp: - r""" - Args: - operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` - for which we compute the QFI. - params: The parameters :math:`\omega` with respect to which we are computing the QFI. - - Returns: - A ``ListOp[ListOp]`` where the operator at position ``[k][l]`` corresponds to the matrix - element :math:`k, l` of the QFI. - - Raises: - NotImplementedError: If ``operator`` is neither ``CircuitOp`` nor ``CircuitStateFn``. - """ - if not isinstance(operator, (CircuitOp, CircuitStateFn)): - raise NotImplementedError("operator must be a CircuitOp or CircuitStateFn") - return self._block_diag_approx(operator=operator, params=params) - - def _block_diag_approx( - self, - operator: Union[CircuitOp, CircuitStateFn], - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], - ) -> ListOp: - r""" - Args: - operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` - for which we compute the QFI. - params: The parameters :math:`\omega` with respect to which we are computing the QFI. - - Returns: - A ``ListOp[ListOp]`` where the operator at position ``[k][l]`` corresponds to the matrix - element :math:`k, l` of the QFI. - - Raises: - NotImplementedError: If a circuit is found such that one parameter controls multiple - gates, or one gate contains multiple parameters. - OpflowError: If there are more than one parameter. - - """ - - # If a single parameter is given wrap it into a list. - if isinstance(params, ParameterExpression): - params = [params] - - circuit = operator.primitive - # Partition the circuit into layers, and build the circuits to prepare $\psi_i$ - layers = _partition_circuit(circuit) - if layers[-1].num_parameters == 0: - layers.pop(-1) - - block_params = [list(layer.parameters) for layer in layers] - # Remove any parameters found which are not in params - block_params = [[param for param in block if param in params] for block in block_params] - - # Determine the permutation needed to ensure that the final - # operator is consistent with the ordering of the input parameters - perm = [params.index(param) for block in block_params for param in block] - - psis = [CircuitOp(layer) for layer in layers] - for i, psi in enumerate(psis): - if i == 0: - continue - psis[i] = psi @ psis[i - 1] - - # Get generators - # TODO: make this work for other types of rotations - # NOTE: This assumes that each parameter only affects one rotation. - # we need to think more about what happens if multiple rotations - # are controlled with a single parameter. - - generators = _get_generators(params, circuit) - - blocks = [] - - # Psi_i = layer_i @ layer_i-1 @ ... @ layer_0 @ Zero - for k, psi_i in enumerate(psis): - params = block_params[k] - block = np.zeros((len(params), len(params))).tolist() - - # calculate all single-operator terms - single_terms = np.zeros(len(params)).tolist() - for i, p_i in enumerate(params): - generator = generators[p_i] - psi_gen_i = ~StateFn(generator) @ psi_i @ Zero - psi_gen_i = PauliExpectation().convert(psi_gen_i) - single_terms[i] = psi_gen_i - - def get_parameter_expression(circuit, param): - if len(circuit._parameter_table[param]) > 1: - raise NotImplementedError( - "OverlapDiag does not yet support multiple " - "gates parameterized by a single parameter. For such " - "circuits use LinCombFull" - ) - gate = next(iter(circuit._parameter_table[param]))[0] - if len(gate.params) > 1: - raise OpflowError( - "OverlapDiag cannot yet support gates with more than one parameter." - ) - - param_value = gate.params[0] - return param_value - - # Calculate all double-operator terms - # and build composite operators for each matrix entry - for i, p_i in enumerate(params): - generator_i = generators[p_i] - param_expr_i = get_parameter_expression(circuit, p_i) - for j, p_j in enumerate(params[i:], i): - if i == j: - block[i][i] = ListOp([single_terms[i]], combo_fn=lambda x: 1 - x[0] ** 2) - if isinstance(param_expr_i, ParameterExpression) and not isinstance( - param_expr_i, Parameter - ): - expr_grad_i = _coeff_derivative(param_expr_i, p_i) - block[i][i] *= expr_grad_i * expr_grad_i - continue - - generator_j = generators[p_j] - generator = ~generator_j @ generator_i - param_expr_j = get_parameter_expression(circuit, p_j) - - psi_gen_ij = ~StateFn(generator) @ psi_i @ Zero - psi_gen_ij = PauliExpectation().convert(psi_gen_ij) - cross_term = ListOp([single_terms[i], single_terms[j]], combo_fn=np.prod) - block[i][j] = psi_gen_ij - cross_term - - # pylint: disable=unidiomatic-typecheck - if type(param_expr_i) == ParameterExpression: - expr_grad_i = _coeff_derivative(param_expr_i, p_i) - block[i][j] *= expr_grad_i - if type(param_expr_j) == ParameterExpression: - expr_grad_j = _coeff_derivative(param_expr_j, p_j) - block[i][j] *= expr_grad_j - - wrapped_block = ListOp( - [ListOp([block[i][j] for j in range(i, len(params))]) for i in range(len(params))], - combo_fn=triu_to_dense, - ) - blocks.append(wrapped_block) - - return ListOp(oplist=blocks, combo_fn=lambda x: np.real(block_diag(*x))[:, perm][perm, :]) diff --git a/qiskit/opflow/gradients/circuit_qfis/overlap_diag.py b/qiskit/opflow/gradients/circuit_qfis/overlap_diag.py deleted file mode 100644 index 3f630fe304b9..000000000000 --- a/qiskit/opflow/gradients/circuit_qfis/overlap_diag.py +++ /dev/null @@ -1,274 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module for Quantum the Fisher Information.""" - -import copy -from typing import List, Union - -import numpy as np -from qiskit.circuit import ParameterVector, ParameterExpression -from qiskit.circuit.library import RZGate, RXGate, RYGate -from qiskit.converters import dag_to_circuit, circuit_to_dag -from qiskit.utils.deprecation import deprecate_func -from ...list_ops.list_op import ListOp -from ...primitive_ops.circuit_op import CircuitOp -from ...expectations.pauli_expectation import PauliExpectation -from ...operator_globals import I, Z, Y, X, Zero -from ...state_fns.state_fn import StateFn -from ...state_fns.circuit_state_fn import CircuitStateFn - - -from .circuit_qfi import CircuitQFI -from ..derivative_base import _coeff_derivative - - -class OverlapDiag(CircuitQFI): - r"""Deprecated: Compute the diagonal of the QFI given a pure, parameterized quantum state. - - See also :class:`~qiskit.opflow.QFI`. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - def convert( - self, - operator: Union[CircuitOp, CircuitStateFn], - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], - ) -> ListOp: - r""" - Args: - operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` - for which we compute the QFI. - params: The parameters :math:`\omega` with respect to which we are computing the QFI. - - Returns: - A ``ListOp[ListOp]`` where the operator at position ``[k][l]`` corresponds to the matrix - element :math:`k, l` of the QFI. - - Raises: - NotImplementedError: If ``operator`` is neither ``CircuitOp`` nor ``CircuitStateFn``. - - """ - - if not isinstance(operator, CircuitStateFn): - raise NotImplementedError("operator must be a CircuitStateFn") - - return self._diagonal_approx(operator=operator, params=params) - - # TODO, for some reason diagonal_approx doesn't use the same get_parameter_expression method. - # This should be fixed. - def _diagonal_approx( - self, - operator: Union[CircuitOp, CircuitStateFn], - params: Union[ParameterExpression, ParameterVector, List], - ) -> ListOp: - """ - Args: - operator: The operator corresponding to the quantum state |ψ(ω)〉for which we compute - the QFI - params: The parameters we are computing the QFI wrt: ω - - Returns: - ListOp where the operator at position k corresponds to QFI_k,k - - Raises: - NotImplementedError: If a circuit is found such that one parameter controls multiple - gates, or one gate contains multiple parameters. - TypeError: If a circuit is found that includes more than one parameter as they are - currently not supported for the overlap diagonal QFI method. - - """ - - if not isinstance(operator, (CircuitOp, CircuitStateFn)): - raise NotImplementedError("operator must be a CircuitOp or CircuitStateFn") - - # If a single parameter is given wrap it into a list. - if isinstance(params, ParameterExpression): - params = [params] - - circuit = operator.primitive - - # Partition the circuit into layers, and build the circuits to prepare $\psi_i$ - layers = _partition_circuit(circuit) - if layers[-1].num_parameters == 0: - layers.pop(-1) - - psis = [CircuitOp(layer) for layer in layers] - for i, psi in enumerate(psis): - if i == 0: - continue - psis[i] = psi @ psis[i - 1] - - # TODO: make this work for other types of rotations - # NOTE: This assumes that each parameter only affects one rotation. - # we need to think more about what happens if multiple rotations - # are controlled with a single parameter. - generators = _get_generators(params, circuit) - - diag = [] - for param in params: - if len(circuit._parameter_table[param]) > 1: - raise NotImplementedError( - "OverlapDiag does not yet support multiple " - "gates parameterized by a single parameter. For such " - "circuits use LinCombFull" - ) - - gate = next(iter(circuit._parameter_table[param]))[0] - - if len(gate.params) != 1: - raise TypeError( - "OverlapDiag cannot yet support gates with more than one parameter." - ) - - param_value = gate.params[0] - generator = generators[param] - meas_op = ~StateFn(generator) - - # get appropriate psi_i - psi = [(psi) for psi in psis if param in psi.primitive.parameters][0] - - op = meas_op @ psi @ Zero - if type(param_value) == ParameterExpression: # pylint: disable=unidiomatic-typecheck - expr_grad = _coeff_derivative(param_value, param) - op *= expr_grad - rotated_op = PauliExpectation().convert(op) - diag.append(rotated_op) - - grad_op = ListOp(diag, combo_fn=lambda x: np.diag(np.real([1 - y**2 for y in x]))) - return grad_op - - -def _partition_circuit(circuit): - dag = circuit_to_dag(circuit) - dag_layers = [i["graph"] for i in dag.serial_layers()] - num_qubits = circuit.num_qubits - layers = list( - zip(dag_layers, [{x: False for x in range(0, num_qubits)} for layer in dag_layers]) - ) - - # initialize the ledger - # The ledger tracks which qubits in each layer are available to have - # gates from subsequent layers shifted backward. - # The idea being that all parameterized gates should have - # no descendants within their layer - bit_indices = {bit: index for index, bit in enumerate(circuit.qubits)} - for i, (layer, ledger) in enumerate(layers): - op_node = layer.op_nodes()[0] - is_param = op_node.op.is_parameterized() - qargs = op_node.qargs - indices = [bit_indices[qarg] for qarg in qargs] - if is_param: - for index in indices: - ledger[index] = True - - def apply_node_op(node, dag, back=True): - op = copy.copy(node.op) - qargs = copy.copy(node.qargs) - cargs = copy.copy(node.cargs) - if back: - dag.apply_operation_back(op, qargs, cargs) - else: - dag.apply_operation_front(op, qargs, cargs) - - converged = False - - for _ in range(dag.depth() + 1): - if converged: - break - - converged = True - - for i, (layer, ledger) in enumerate(layers): - if i == len(layers) - 1: - continue - - (next_layer, next_ledger) = layers[i + 1] - for next_node in next_layer.op_nodes(): - is_param = next_node.op.is_parameterized() - qargs = next_node.qargs - indices = [bit_indices[qarg] for qarg in qargs] - - # If the next_node can be moved back a layer without - # without becoming the descendant of a parameterized gate, - # then do it. - if not any(ledger[x] for x in indices): - - apply_node_op(next_node, layer) - next_layer.remove_op_node(next_node) - - if is_param: - for index in indices: - ledger[index] = True - next_ledger[index] = False - - converged = False - - # clean up empty layers left behind. - if len(next_layer.op_nodes()) == 0: - layers.pop(i + 1) - - partitioned_circs = [dag_to_circuit(layer[0]) for layer in layers] - return partitioned_circs - - -def _get_generators(params, circuit): - dag = circuit_to_dag(circuit) - layers = list(dag.serial_layers()) - - generators = {} - num_qubits = dag.num_qubits() - bit_indices = {bit: index for index, bit in enumerate(circuit.qubits)} - - for layer in layers: - instr = layer["graph"].op_nodes()[0].op - # if no gate is parameterized, skip - if not any(isinstance(param, ParameterExpression) for param in instr.params): - continue - - if len(instr.params) != 1: - raise NotImplementedError( - "The QFI diagonal approximation currently only supports " - "gates with a single free parameter." - ) - param_value = instr.params[0] - - for param in params: - if param in param_value.parameters: - - if isinstance(instr, RYGate): - generator = Y - elif isinstance(instr, RZGate): - generator = Z - elif isinstance(instr, RXGate): - generator = X - else: - raise NotImplementedError(f"Generator for gate {instr.name} not implemented.") - - # get all qubit indices in this layer where the param parameterizes - # an operation. - indices = [[bit_indices[q] for q in qreg] for qreg in layer["partition"]] - indices = [item for sublist in indices for item in sublist] - - if len(indices) > 1: - raise NotImplementedError - index = indices[0] - generator = (I ^ (index)) ^ generator ^ (I ^ (num_qubits - index - 1)) - generators[param] = generator - - return generators diff --git a/qiskit/opflow/gradients/derivative_base.py b/qiskit/opflow/gradients/derivative_base.py deleted file mode 100644 index 9ffbc6f0cdf0..000000000000 --- a/qiskit/opflow/gradients/derivative_base.py +++ /dev/null @@ -1,244 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""DerivativeBase Class""" - -from abc import abstractmethod -from typing import Callable, Iterable, List, Optional, Tuple, Union - -import numpy as np -from qiskit.utils.deprecation import deprecate_func -from qiskit.utils.quantum_instance import QuantumInstance -from qiskit.circuit import ParameterExpression, ParameterVector -from qiskit.providers import Backend -from ..converters.converter_base import ConverterBase -from ..expectations import ExpectationBase, PauliExpectation -from ..list_ops.composed_op import ComposedOp -from ..list_ops.list_op import ListOp -from ..list_ops.tensored_op import TensoredOp -from ..operator_base import OperatorBase -from ..primitive_ops.primitive_op import PrimitiveOp -from ..state_fns import StateFn, OperatorStateFn - -OperatorType = Union[StateFn, PrimitiveOp, ListOp] - - -class DerivativeBase(ConverterBase): - r"""Deprecated: Base class for differentiating opflow objects. - - Converter for differentiating opflow objects and handling - things like properly differentiating combo_fn's and enforcing product rules - when operator coefficients are parameterized. - - This is distinct from CircuitGradient converters which use quantum - techniques such as parameter shifts and linear combination of unitaries - to compute derivatives of circuits. - - CircuitGradient - uses quantum techniques to get derivatives of circuits - DerivativeBase - uses classical techniques to differentiate opflow data structures - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - - # pylint: disable=arguments-differ - @abstractmethod - def convert( - self, - operator: OperatorBase, - params: Optional[ - Union[ParameterVector, ParameterExpression, List[ParameterExpression]] - ] = None, - ) -> OperatorBase: - r""" - Args: - operator: The operator we are taking the gradient, Hessian or QFI of - params: The parameters we are taking the gradient, Hessian or QFI with respect to. - - Returns: - An operator whose evaluation yields the gradient, Hessian or QFI. - - Raises: - ValueError: If ``params`` contains a parameter not present in ``operator``. - """ - raise NotImplementedError - - def gradient_wrapper( - self, - operator: OperatorBase, - bind_params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], - grad_params: Optional[ - Union[ - ParameterExpression, - ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - ] - ] = None, - backend: Optional[Union[Backend, QuantumInstance]] = None, - expectation: Optional[ExpectationBase] = None, - ) -> Callable[[Iterable], np.ndarray]: - """Get a callable function which provides the respective gradient, Hessian or QFI for given - parameter values. This callable can be used as gradient function for optimizers. - - Args: - operator: The operator for which we want to get the gradient, Hessian or QFI. - bind_params: The operator parameters to which the parameter values are assigned. - grad_params: The parameters with respect to which we are taking the gradient, Hessian - or QFI. If grad_params = None, then grad_params = bind_params - backend: The quantum backend or QuantumInstance to use to evaluate the gradient, - Hessian or QFI. - expectation: The expectation converter to be used. If none is set then - `PauliExpectation()` is used. - - Returns: - Function to compute a gradient, Hessian or QFI. The function - takes an iterable as argument which holds the parameter values. - """ - from ..converters import CircuitSampler - - if grad_params is None: - grad_params = bind_params - - grad = self.convert(operator, grad_params) - if expectation is None: - expectation = PauliExpectation() - grad = expectation.convert(grad) - - sampler = CircuitSampler(backend=backend) if backend is not None else None - - def gradient_fn(p_values): - p_values_dict = dict(zip(bind_params, p_values)) - if not backend: - converter = grad.assign_parameters(p_values_dict) - return np.real(converter.eval()) - else: - p_values_list = {k: [v] for k, v in p_values_dict.items()} - sampled = sampler.convert(grad, p_values_list) - fully_bound = sampled.bind_parameters(p_values_dict) - return np.real(fully_bound.eval()[0]) - - return gradient_fn - - @staticmethod - @deprecate_func( - since="0.18.0", additional_msg="Instead, use the ParameterExpression.gradient method." - ) - def parameter_expression_grad( - param_expr: ParameterExpression, param: ParameterExpression - ) -> Union[ParameterExpression, float]: - """Get the derivative of a parameter expression w.r.t. the given parameter. - - Args: - param_expr: The Parameter Expression for which we compute the derivative - param: Parameter w.r.t. which we want to take the derivative - - Returns: - ParameterExpression representing the gradient of param_expr w.r.t. param - """ - return _coeff_derivative(param_expr, param) - - @classmethod - def _erase_operator_coeffs(cls, operator: OperatorBase) -> OperatorBase: - """This method traverses an input operator and deletes all of the coefficients - - Args: - operator: An operator type object. - - Returns: - An operator which is equal to the input operator but whose coefficients - have all been set to 1.0 - - Raises: - TypeError: If unknown operator type is reached. - """ - if isinstance(operator, PrimitiveOp): - return operator / operator.coeff - op_coeff = operator.coeff # type: ignore - return (operator / op_coeff).traverse(cls._erase_operator_coeffs) - - @classmethod - def _factor_coeffs_out_of_composed_op(cls, operator: OperatorBase) -> OperatorBase: - """Factor all coefficients of ComposedOp out into a single global coefficient. - - Part of the automatic differentiation logic inside of Gradient and Hessian - counts on the fact that no product or chain rules need to be computed between - operators or coefficients within a ComposedOp. To ensure this condition is met, - this function traverses an operator and replaces each ComposedOp with an equivalent - ComposedOp, but where all coefficients have been factored out and placed onto the - ComposedOp. Note that this cannot be done properly if an OperatorMeasurement contains - a SummedOp as it's primitive. - - Args: - operator: The operator whose coefficients are being re-organized - - Returns: - An operator equivalent to the input operator, but whose coefficients have been - reorganized - - Raises: - ValueError: If an element within a ComposedOp has a primitive of type ListOp, - then it is not possible to factor all coefficients out of the ComposedOp. - """ - if isinstance(operator, ListOp) and not isinstance(operator, ComposedOp): - return operator.traverse(cls._factor_coeffs_out_of_composed_op) - if isinstance(operator, ComposedOp): - total_coeff = operator.coeff - take_norm_of_coeffs = False - for k, op in enumerate(operator.oplist): - if take_norm_of_coeffs: - total_coeff *= op.coeff * np.conj(op.coeff) # type: ignore - else: - total_coeff *= op.coeff # type: ignore - if hasattr(op, "primitive"): - prim = op.primitive # type: ignore - if isinstance(op, StateFn) and isinstance(prim, TensoredOp): - # Check if any of the coefficients in the TensoredOp is a - # ParameterExpression - for prim_op in prim.oplist: - # If a coefficient is a ParameterExpression make sure that the - # coefficients are pulled together correctly - if isinstance(prim_op.coeff, ParameterExpression): - prim_tensored = StateFn( - prim.reduce(), is_measurement=op.is_measurement, coeff=op.coeff - ) - operator.oplist[k] = prim_tensored - return operator.traverse(cls._factor_coeffs_out_of_composed_op) - elif isinstance(prim, ListOp): - raise ValueError( - "This operator was not properly decomposed. " - "By this point, all operator measurements should " - "contain single operators, otherwise the coefficient " - "gradients will not be handled properly." - ) - if hasattr(prim, "coeff"): - if take_norm_of_coeffs: - total_coeff *= prim._coeff * np.conj(prim._coeff) - else: - total_coeff *= prim._coeff - if isinstance(op, OperatorStateFn) and op.is_measurement: - take_norm_of_coeffs = True - return cls._erase_operator_coeffs(operator).mul(total_coeff) - - else: - return operator - - -def _coeff_derivative(coeff, param): - if isinstance(coeff, ParameterExpression) and len(coeff.parameters) > 0: - return coeff.gradient(param) - return 0 diff --git a/qiskit/opflow/gradients/gradient.py b/qiskit/opflow/gradients/gradient.py deleted file mode 100644 index f9762da93e41..000000000000 --- a/qiskit/opflow/gradients/gradient.py +++ /dev/null @@ -1,230 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The base interface for Opflow's gradient.""" - -from typing import Union, List, Optional -import numpy as np - -from qiskit.circuit import ParameterExpression, ParameterVector -from qiskit.circuit._utils import sort_parameters -from qiskit.utils import optionals as _optionals -from qiskit.utils.deprecation import deprecate_func -from .circuit_gradients.circuit_gradient import CircuitGradient -from ..expectations.pauli_expectation import PauliExpectation -from .gradient_base import GradientBase -from .derivative_base import _coeff_derivative -from ..list_ops.composed_op import ComposedOp -from ..list_ops.list_op import ListOp -from ..list_ops.summed_op import SummedOp -from ..list_ops.tensored_op import TensoredOp -from ..operator_base import OperatorBase -from ..operator_globals import Zero, One -from ..state_fns.circuit_state_fn import CircuitStateFn -from ..exceptions import OpflowError - - -class Gradient(GradientBase): - """Deprecated: Convert an operator expression to the first-order gradient.""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, grad_method: Union[str, CircuitGradient] = "param_shift", **kwargs): - super().__init__(grad_method=grad_method, **kwargs) - - def convert( - self, - operator: OperatorBase, - params: Optional[ - Union[ParameterVector, ParameterExpression, List[ParameterExpression]] - ] = None, - ) -> OperatorBase: - r""" - Args: - operator: The operator we are taking the gradient of. - params: The parameters we are taking the gradient with respect to. If not - explicitly passed, they are inferred from the operator and sorted by name. - - Returns: - An operator whose evaluation yields the Gradient. - - Raises: - ValueError: If ``params`` contains a parameter not present in ``operator``. - ValueError: If ``operator`` is not parameterized. - """ - if len(operator.parameters) == 0: - raise ValueError("The operator we are taking the gradient of is not parameterized!") - if params is None: - params = sort_parameters(operator.parameters) - if isinstance(params, (ParameterVector, list)): - param_grads = [self.convert(operator, param) for param in params] - absent_params = [ - params[i] for i, grad_ops in enumerate(param_grads) if grad_ops is None - ] - if len(absent_params) > 0: - raise ValueError( - "The following parameters do not appear in the provided operator: ", - absent_params, - ) - return ListOp(param_grads) - - param = params - # Preprocessing - expec_op = PauliExpectation(group_paulis=False).convert(operator).reduce() - cleaned_op = self._factor_coeffs_out_of_composed_op(expec_op) - return self.get_gradient(cleaned_op, param) - - # pylint: disable=too-many-return-statements - def get_gradient( - self, - operator: OperatorBase, - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], - ) -> OperatorBase: - """Get the gradient for the given operator w.r.t. the given parameters - - Args: - operator: Operator w.r.t. which we take the gradient. - params: Parameters w.r.t. which we compute the gradient. - - Returns: - Operator which represents the gradient w.r.t. the given params. - - Raises: - ValueError: If ``params`` contains a parameter not present in ``operator``. - OpflowError: If the coefficient of the operator could not be reduced to 1. - OpflowError: If the differentiation of a combo_fn requires JAX but the package is not - installed. - TypeError: If the operator does not include a StateFn given by a quantum circuit - Exception: Unintended code is reached - MissingOptionalLibraryError: jax not installed - """ - - def is_coeff_c(coeff, c): - if isinstance(coeff, ParameterExpression): - expr = coeff._symbol_expr - return expr == c - return coeff == c - - def is_coeff_c_abs(coeff, c): - if isinstance(coeff, ParameterExpression): - expr = coeff._symbol_expr - return np.abs(expr) == c - return np.abs(coeff) == c - - if isinstance(params, (ParameterVector, list)): - param_grads = [self.get_gradient(operator, param) for param in params] - # If get_gradient returns None, then the corresponding parameter was probably not - # present in the operator. This needs to be looked at more carefully as other things can - # probably trigger a return of None. - absent_params = [ - params[i] for i, grad_ops in enumerate(param_grads) if grad_ops is None - ] - if len(absent_params) > 0: - raise ValueError( - "The following parameters do not appear in the provided operator: ", - absent_params, - ) - return ListOp(param_grads) - - # By now params is a single parameter - param = params - # Handle Product Rules - if not is_coeff_c(operator._coeff, 1.0) and not is_coeff_c(operator._coeff, 1.0j): - # Separate the operator from the coefficient - coeff = operator._coeff - op = operator / coeff - if np.iscomplex(coeff): - from .circuit_gradients.lin_comb import LinComb - - if isinstance(self.grad_method, LinComb): - op *= 1j - coeff /= 1j - - # Get derivative of the operator (recursively) - d_op = self.get_gradient(op, param) - # ..get derivative of the coeff - d_coeff = _coeff_derivative(coeff, param) - - grad_op = 0 - if d_op != ~Zero @ One and not is_coeff_c(coeff, 0.0): - grad_op += coeff * d_op - if op != ~Zero @ One and not is_coeff_c(d_coeff, 0.0): - grad_op += d_coeff * op - if grad_op == 0: - grad_op = ~Zero @ One - return grad_op - - # Base Case, you've hit a ComposedOp! - # Prior to execution, the composite operator was standardized and coefficients were - # collected. Any operator measurements were converted to Pauli-Z measurements and rotation - # circuits were applied. Additionally, all coefficients within ComposedOps were collected - # and moved out front. - if isinstance(operator, ComposedOp): - - # Gradient of an expectation value - if not is_coeff_c_abs(operator._coeff, 1.0): - raise OpflowError( - "Operator pre-processing failed. Coefficients were not properly " - "collected inside the ComposedOp." - ) - - # Do some checks to make sure operator is sensible - # TODO add compatibility with sum of circuit state fns - if not isinstance(operator[-1], CircuitStateFn): - raise TypeError( - "The gradient framework is compatible with states that are given as " - "CircuitStateFn" - ) - - return self.grad_method.convert(operator, param) - - elif isinstance(operator, CircuitStateFn): - # Gradient of an a state's sampling probabilities - if not is_coeff_c(operator._coeff, 1.0): - raise OpflowError( - "Operator pre-processing failed. Coefficients were not properly " - "collected inside the ComposedOp." - ) - return self.grad_method.convert(operator, param) - - # Handle the chain rule - elif isinstance(operator, ListOp): - grad_ops = [self.get_gradient(op, param) for op in operator.oplist] - - # pylint: disable=comparison-with-callable - if operator.combo_fn == ListOp.default_combo_fn: # If using default - return ListOp(oplist=grad_ops) - elif isinstance(operator, SummedOp): - return SummedOp(oplist=[grad for grad in grad_ops if grad != ~Zero @ One]).reduce() - elif isinstance(operator, TensoredOp): - return TensoredOp(oplist=grad_ops) - - if operator.grad_combo_fn: - grad_combo_fn = operator.grad_combo_fn - else: - _optionals.HAS_JAX.require_now("automatic differentiation") - from jax import jit, grad - - grad_combo_fn = jit(grad(operator.combo_fn, holomorphic=True)) - - def chain_rule_combo_fn(x): - result = np.dot(x[1], x[0]) - if isinstance(result, np.ndarray): - result = list(result) - return result - - return ListOp( - [ListOp(operator.oplist, combo_fn=grad_combo_fn), ListOp(grad_ops)], - combo_fn=chain_rule_combo_fn, - ) diff --git a/qiskit/opflow/gradients/gradient_base.py b/qiskit/opflow/gradients/gradient_base.py deleted file mode 100644 index f8da278acc93..000000000000 --- a/qiskit/opflow/gradients/gradient_base.py +++ /dev/null @@ -1,76 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The base interface for Aqua's gradient.""" - -from typing import Union - -from qiskit.utils.deprecation import deprecate_func -from .circuit_gradients.circuit_gradient import CircuitGradient -from .derivative_base import DerivativeBase - - -class GradientBase(DerivativeBase): - """Deprecated: Base class for first-order operator gradient. - - Convert an operator expression to the first-order gradient. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, grad_method: Union[str, CircuitGradient] = "param_shift", **kwargs): - r""" - Args: - grad_method: The method used to compute the state/probability gradient. Can be either - ``'param_shift'`` or ``'lin_comb'`` or ``'fin_diff'``. - Ignored for gradients w.r.t observable parameters. - kwargs (dict): Optional parameters for a CircuitGradient - - Raises: - ValueError: If method != ``fin_diff`` and ``epsilon`` is not None. - """ - super().__init__() - if isinstance(grad_method, CircuitGradient): - self._grad_method = grad_method - elif grad_method == "param_shift": - from .circuit_gradients.param_shift import ParamShift - - self._grad_method = ParamShift(analytic=True) - - elif grad_method == "fin_diff": - from .circuit_gradients.param_shift import ParamShift - - epsilon = kwargs.get("epsilon", 1e-6) - self._grad_method = ParamShift(analytic=False, epsilon=epsilon) - - elif grad_method == "lin_comb": - from .circuit_gradients.lin_comb import LinComb - - self._grad_method = LinComb() - else: - raise ValueError( - "Unrecognized input provided for `grad_method`. Please provide" - " a CircuitGradient object or one of the pre-defined string" - " arguments: {'param_shift', 'fin_diff', 'lin_comb'}. " - ) - - @property - def grad_method(self) -> CircuitGradient: - """Returns ``CircuitGradient``. - - Returns: - ``CircuitGradient``. - - """ - return self._grad_method diff --git a/qiskit/opflow/gradients/hessian.py b/qiskit/opflow/gradients/hessian.py deleted file mode 100644 index 3ec39f5674ca..000000000000 --- a/qiskit/opflow/gradients/hessian.py +++ /dev/null @@ -1,292 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module to compute Hessians.""" - -from typing import Union, List, Tuple, Optional -import numpy as np - -from qiskit.circuit import ParameterVector, ParameterExpression -from qiskit.circuit._utils import sort_parameters -from qiskit.utils import optionals as _optionals -from qiskit.utils.deprecation import deprecate_func -from ..operator_globals import Zero, One -from ..state_fns.circuit_state_fn import CircuitStateFn -from ..state_fns.state_fn import StateFn -from ..expectations.pauli_expectation import PauliExpectation -from ..list_ops.list_op import ListOp -from ..list_ops.composed_op import ComposedOp -from ..list_ops.summed_op import SummedOp -from ..list_ops.tensored_op import TensoredOp -from ..operator_base import OperatorBase -from .gradient import Gradient -from .derivative_base import _coeff_derivative -from .hessian_base import HessianBase -from ..exceptions import OpflowError -from ...utils.arithmetic import triu_to_dense -from .circuit_gradients.circuit_gradient import CircuitGradient - - -class Hessian(HessianBase): - """Deprecated: Compute the Hessian of an expected value.""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, hess_method: Union[str, CircuitGradient] = "param_shift", **kwargs): - super().__init__(hess_method=hess_method, **kwargs) - - def convert( - self, - operator: OperatorBase, - params: Optional[ - Union[ - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - List[ParameterExpression], - ParameterVector, - ] - ] = None, - ) -> OperatorBase: - """ - Args: - operator: The operator for which we compute the Hessian - params: The parameters we are computing the Hessian with respect to - Either give directly the tuples/list of tuples for which the second order - derivative is to be computed or give a list of parameters to build the - full Hessian for those parameters. If not explicitly passed, the full Hessian is - constructed. The parameters are then inferred from the operator and sorted by - name. - - Returns: - OperatorBase: An operator whose evaluation yields the Hessian - """ - - expec_op = PauliExpectation(group_paulis=False).convert(operator).reduce() - cleaned_op = self._factor_coeffs_out_of_composed_op(expec_op) - return self.get_hessian(cleaned_op, params) - - # pylint: disable=too-many-return-statements - def get_hessian( - self, - operator: OperatorBase, - params: Optional[ - Union[ - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - List[ParameterExpression], - ParameterVector, - ] - ] = None, - ) -> OperatorBase: - """Get the Hessian for the given operator w.r.t. the given parameters - - Args: - operator: Operator w.r.t. which we take the Hessian. - params: Parameters w.r.t. which we compute the Hessian. If not explicitly passed, - the full Hessian is constructed. The parameters are then inferred from the operator - and sorted by name. - - Returns: - Operator which represents the gradient w.r.t. the given params. - - Raises: - ValueError: If ``params`` contains a parameter not present in ``operator``. - ValueError: If ``operator`` is not parameterized. - OpflowError: If the coefficient of the operator could not be reduced to 1. - OpflowError: If the differentiation of a combo_fn - requires JAX but the package is not installed. - TypeError: If the operator does not include a StateFn given by a quantum circuit - TypeError: If the parameters were given in an unsupported format. - Exception: Unintended code is reached - MissingOptionalLibraryError: jax not installed - """ - if len(operator.parameters) == 0: - raise ValueError("The operator we are taking the gradient of is not parameterized!") - if params is None: - params = sort_parameters(operator.parameters) - # if input is a tuple instead of a list, wrap it into a list - if isinstance(params, (ParameterVector, list)): - # Case: a list of parameters were given, compute the Hessian for all param pairs - if all(isinstance(param, ParameterExpression) for param in params): - return ListOp( - [ - ListOp( - [ - self.get_hessian(operator, (p_i, p_j)) - for i, p_i in enumerate(params[j:], j) - ] - ) - for j, p_j in enumerate(params) - ], - combo_fn=triu_to_dense, - ) - # Case: a list was given containing tuples of parameter pairs. - elif all(isinstance(param, tuple) for param in params): - # Compute the Hessian entries corresponding to these pairs of parameters. - return ListOp([self.get_hessian(operator, param_pair) for param_pair in params]) - - def is_coeff_c(coeff, c): - if isinstance(coeff, ParameterExpression): - expr = coeff._symbol_expr - return expr == c - return coeff == c - - # If a gradient is requested w.r.t a single parameter, then call the - # Gradient().get_gradient method. - if isinstance(params, ParameterExpression): - return Gradient(grad_method=self._hess_method).get_gradient(operator, params) - - if (not isinstance(params, tuple)) or (not len(params) == 2): - raise TypeError("Parameters supplied in unsupported format.") - - # By this point, it's only one parameter tuple - p_0 = params[0] - p_1 = params[1] - - # Handle Product Rules - if not is_coeff_c(operator._coeff, 1.0): - # Separate the operator from the coefficient - coeff = operator._coeff - op = operator / coeff - # Get derivative of the operator (recursively) - d0_op = self.get_hessian(op, p_0) - d1_op = self.get_hessian(op, p_1) - # ..get derivative of the coeff - d0_coeff = _coeff_derivative(coeff, p_0) - d1_coeff = _coeff_derivative(coeff, p_1) - - dd_op = self.get_hessian(op, params) - dd_coeff = _coeff_derivative(d0_coeff, p_1) - - grad_op = 0 - # Avoid creating operators that will evaluate to zero - if dd_op != ~Zero @ One and not is_coeff_c(coeff, 0): - grad_op += coeff * dd_op - if d0_op != ~Zero @ One and not is_coeff_c(d1_coeff, 0): - grad_op += d1_coeff * d0_op - if d1_op != ~Zero @ One and not is_coeff_c(d0_coeff, 0): - grad_op += d0_coeff * d1_op - if not is_coeff_c(dd_coeff, 0): - grad_op += dd_coeff * op - - if grad_op == 0: - return ~Zero @ One - - return grad_op - - # Base Case, you've hit a ComposedOp! - # Prior to execution, the composite operator was standardized and coefficients were - # collected. Any operator measurements were converted to Pauli-Z measurements and rotation - # circuits were applied. Additionally, all coefficients within ComposedOps were collected - # and moved out front. - if isinstance(operator, ComposedOp): - - if not is_coeff_c(operator.coeff, 1.0): - raise OpflowError( - "Operator pre-processing failed. Coefficients were not properly " - "collected inside the ComposedOp." - ) - - # Do some checks to make sure operator is sensible - # TODO enable compatibility with sum of CircuitStateFn operators - if not isinstance(operator[-1], CircuitStateFn): - raise TypeError( - "The gradient framework is compatible with states that are given as " - "CircuitStateFn" - ) - - return self.hess_method.convert(operator, params) - - # This is the recursive case where the chain rule is handled - elif isinstance(operator, ListOp): - # These operators correspond to (d_op/d θ0,θ1) for op in operator.oplist - # and params = (θ0,θ1) - dd_ops = [self.get_hessian(op, params) for op in operator.oplist] - - # TODO Note that this check to see if the ListOp has a default combo_fn - # will fail if the user manually specifies the default combo_fn. - # I.e operator = ListOp([...], combo_fn=lambda x:x) will not pass this check and - # later on jax will try to differentiate it and fail. - # An alternative is to check the byte code of the operator's combo_fn against the - # default one. - # This will work but look very ugly and may have other downsides I'm not aware of - if operator.combo_fn == ListOp([]).combo_fn: - return ListOp(oplist=dd_ops) - elif isinstance(operator, SummedOp): - return SummedOp(oplist=dd_ops) - elif isinstance(operator, TensoredOp): - return TensoredOp(oplist=dd_ops) - - # These operators correspond to (d g_i/d θ0)•(d g_i/d θ1) for op in operator.oplist - # and params = (θ0,θ1) - d1d0_ops = ListOp( - [ - ListOp( - [ - Gradient(grad_method=self._hess_method).convert(op, param) - for param in params - ], - combo_fn=np.prod, - ) - for op in operator.oplist - ] - ) - - _optionals.HAS_JAX.require_now("automatic differentiation") - from jax import grad, jit - - if operator.grad_combo_fn: - first_partial_combo_fn = operator.grad_combo_fn - - second_partial_combo_fn = jit( - grad(lambda x: first_partial_combo_fn(x)[0], holomorphic=True) - ) - else: - first_partial_combo_fn = jit(grad(operator.combo_fn, holomorphic=True)) - second_partial_combo_fn = jit( - grad(lambda x: first_partial_combo_fn(x)[0], holomorphic=True) - ) - - # For a general combo_fn F(g_0, g_1, ..., g_k) - # dF/d θ0,θ1 = sum_i: (∂F/∂g_i)•(d g_i/ d θ0,θ1) + (∂F/∂^2 g_i)•(d g_i/d θ0)•(d g_i/d - # θ1) - - # term1 = (∂F/∂g_i)•(d g_i/ d θ0,θ1) - term1 = ListOp( - [ListOp(operator.oplist, combo_fn=first_partial_combo_fn), ListOp(dd_ops)], - combo_fn=lambda x: np.dot(x[1], x[0]), - ) - # term2 = (∂F/∂^2 g_i)•(d g_i/d θ0)•(d g_i/d θ1) - term2 = ListOp( - [ListOp(operator.oplist, combo_fn=second_partial_combo_fn), d1d0_ops], - combo_fn=lambda x: np.dot(x[1], x[0]), - ) - - return SummedOp([term1, term2]) - - elif isinstance(operator, StateFn): - if not operator.is_measurement: - return self.hess_method.convert(operator, params) - - else: - raise TypeError( - "The computation of Hessians is only supported for Operators which " - "represent expectation values or quantum states." - ) - - else: - raise TypeError( - "The computation of Hessians is only supported for Operators which " - "represent expectation values." - ) diff --git a/qiskit/opflow/gradients/hessian_base.py b/qiskit/opflow/gradients/hessian_base.py deleted file mode 100644 index 2230ec28d824..000000000000 --- a/qiskit/opflow/gradients/hessian_base.py +++ /dev/null @@ -1,74 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module to compute Hessians.""" - -from typing import Union - -from qiskit.utils.deprecation import deprecate_func -from .circuit_gradients.circuit_gradient import CircuitGradient -from .derivative_base import DerivativeBase - - -class HessianBase(DerivativeBase): - """Deprecated: Base class for the Hessian of an expected value.""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, hess_method: Union[str, CircuitGradient] = "param_shift", **kwargs): - r""" - Args: - hess_method: The method used to compute the state/probability gradient. Can be either - ``'param_shift'`` or ``'lin_comb'`` or ``'fin_diff'``. - Ignored for gradients w.r.t observable parameters. - kwargs (dict): Optional parameters for a CircuitGradient - - Raises: - ValueError: If method != ``fin_diff`` and ``epsilon`` is not None. - """ - super().__init__() - if isinstance(hess_method, CircuitGradient): - self._hess_method = hess_method - elif hess_method == "param_shift": - from .circuit_gradients import ParamShift - - self._hess_method = ParamShift() - - elif hess_method == "fin_diff": - from .circuit_gradients import ParamShift - - epsilon = kwargs.get("epsilon", 1e-6) - self._hess_method = ParamShift(analytic=False, epsilon=epsilon) - - elif hess_method == "lin_comb": - from .circuit_gradients import LinComb - - self._hess_method = LinComb() - - else: - raise ValueError( - "Unrecognized input provided for `hess_method`. Please provide" - " a CircuitGradient object or one of the pre-defined string" - " arguments: {'param_shift', 'fin_diff', 'lin_comb'}. " - ) - - @property - def hess_method(self) -> CircuitGradient: - """Returns ``CircuitGradient``. - - Returns: - ``CircuitGradient``. - - """ - return self._hess_method diff --git a/qiskit/opflow/gradients/natural_gradient.py b/qiskit/opflow/gradients/natural_gradient.py deleted file mode 100644 index 39c6cb8f4b73..000000000000 --- a/qiskit/opflow/gradients/natural_gradient.py +++ /dev/null @@ -1,560 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Natural Gradient.""" - -from collections.abc import Iterable -from typing import List, Tuple, Callable, Optional, Union -import numpy as np - -from qiskit.circuit import ParameterVector, ParameterExpression -from qiskit.circuit._utils import sort_parameters -from qiskit.utils import optionals as _optionals -from qiskit.utils.deprecation import deprecate_func -from ..operator_base import OperatorBase -from ..list_ops.list_op import ListOp -from ..list_ops.composed_op import ComposedOp -from ..state_fns.circuit_state_fn import CircuitStateFn -from .circuit_gradients import CircuitGradient -from .circuit_qfis import CircuitQFI -from .gradient import Gradient -from .gradient_base import GradientBase -from .qfi import QFI - -# Error tolerance variable -ETOL = 1e-8 -# Cut-off ratio for small singular values for least square solver -RCOND = 1e-2 - - -class NaturalGradient(GradientBase): - r"""Deprecated: Convert an operator expression to the first-order gradient. - - Given an ill-posed inverse problem - - x = arg min{||Ax-C||^2} (1) - - one can use regularization schemes can be used to stabilize the system and find a numerical - solution - - x_lambda = arg min{||Ax-C||^2 + lambda*R(x)} (2) - - where R(x) represents the penalization term. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - grad_method: Union[str, CircuitGradient] = "lin_comb", - qfi_method: Union[str, CircuitQFI] = "lin_comb_full", - regularization: Optional[str] = None, - **kwargs, - ): - r""" - Args: - grad_method: The method used to compute the state gradient. Can be either - ``'param_shift'`` or ``'lin_comb'`` or ``'fin_diff'``. - qfi_method: The method used to compute the QFI. Can be either - ``'lin_comb_full'`` or ``'overlap_block_diag'`` or ``'overlap_diag'``. - regularization: Use the following regularization with a least square method to solve the - underlying system of linear equations - Can be either None or ``'ridge'`` or ``'lasso'`` or ``'perturb_diag'`` - ``'ridge'`` and ``'lasso'`` use an automatic optimal parameter search - If regularization is None but the metric is ill-conditioned or singular then - a least square solver is used without regularization - kwargs (dict): Optional parameters for a CircuitGradient - """ - super().__init__(grad_method) - - self._qfi_method = QFI(qfi_method) - self._regularization = regularization - self._epsilon = kwargs.get("epsilon", 1e-6) - - def convert( - self, - operator: OperatorBase, - params: Optional[ - Union[ParameterVector, ParameterExpression, List[ParameterExpression]] - ] = None, - ) -> OperatorBase: - r""" - Args: - operator: The operator we are taking the gradient of. - params: The parameters we are taking the gradient with respect to. If not explicitly - passed, they are inferred from the operator and sorted by name. - - Returns: - An operator whose evaluation yields the NaturalGradient. - - Raises: - TypeError: If ``operator`` does not represent an expectation value or the quantum - state is not ``CircuitStateFn``. - ValueError: If ``params`` contains a parameter not present in ``operator``. - ValueError: If ``operator`` is not parameterized. - """ - if not isinstance(operator, ComposedOp): - if not (isinstance(operator, ListOp) and len(operator.oplist) == 1): - raise TypeError( - "Please provide the operator either as ComposedOp or as ListOp of " - "a CircuitStateFn potentially with a combo function." - ) - - if not isinstance(operator[-1], CircuitStateFn): - raise TypeError( - "Please make sure that the operator for which you want to compute " - "Quantum Fisher Information represents an expectation value or a " - "loss function and that the quantum state is given as " - "CircuitStateFn." - ) - if len(operator.parameters) == 0: - raise ValueError("The operator we are taking the gradient of is not parameterized!") - if params is None: - params = sort_parameters(operator.parameters) - if not isinstance(params, Iterable): - params = [params] - # Instantiate the gradient - grad = Gradient(self._grad_method, epsilon=self._epsilon).convert(operator, params) - # Instantiate the QFI metric which is used to re-scale the gradient - metric = self._qfi_method.convert(operator[-1], params) * 0.25 - - def combo_fn(x): - return self.nat_grad_combo_fn(x, self.regularization) - - # Define the ListOp which combines the gradient and the QFI according to the combination - # function defined above. - return ListOp([grad, metric], combo_fn=combo_fn) - - @staticmethod - def nat_grad_combo_fn(x: tuple, regularization: Optional[str] = None) -> np.ndarray: - r""" - Natural Gradient Function Implementation. - - Args: - x: Iterable consisting of Gradient, Quantum Fisher Information. - regularization: Regularization method. - - Returns: - Natural Gradient. - - Raises: - ValueError: If the gradient has imaginary components that are non-negligible. - - """ - gradient = x[0] - metric = x[1] - if np.amax(np.abs(np.imag(gradient))) > ETOL: - raise ValueError( - "The imaginary part of the gradient are non-negligible. The largest absolute " - f"imaginary value in the gradient is {np.amax(np.abs(np.imag(gradient)))}. " - "Please increase the number of shots." - ) - gradient = np.real(gradient) - - if np.amax(np.abs(np.imag(metric))) > ETOL: - raise ValueError( - "The imaginary part of the metric are non-negligible. The largest " - "absolute imaginary value in the gradient is " - f"{np.amax(np.abs(np.imag(metric)))}. Please " - "increase the number of shots." - ) - metric = np.real(metric) - - if regularization is not None: - # If a regularization method is chosen then use a regularized solver to - # construct the natural gradient. - nat_grad = NaturalGradient._regularized_sle_solver( - metric, gradient, regularization=regularization - ) - else: - # Check if numerical instabilities lead to a metric which is not positive semidefinite - w, v = np.linalg.eigh(metric) - - if not all(ew >= (-1) * ETOL for ew in w): - raise ValueError( - f"The underlying metric has at least one Eigenvalue < -{ETOL}. " - f"The smallest Eigenvalue is {np.amin(w)} " - "Please use a regularized least-square solver for this problem or " - "increase the number of backend shots.", - ) - if not all(ew >= 0 for ew in w): - # If not all eigenvalues are non-negative, set them to a small positive - # value - w = [max(ETOL, ew) for ew in w] - # Recompose the adapted eigenvalues with the eigenvectors to get a new metric - metric = np.real(v @ np.diag(w) @ np.linalg.inv(v)) - nat_grad = np.linalg.lstsq(metric, gradient, rcond=RCOND)[0] - return nat_grad - - @property - def qfi_method(self) -> CircuitQFI: - """Returns ``CircuitQFI``. - - Returns: ``CircuitQFI``. - - """ - return self._qfi_method.qfi_method - - @property - def regularization(self) -> Optional[str]: - """Returns the regularization option. - - Returns: the regularization option. - - """ - return self._regularization - - @staticmethod - def _reg_term_search( - metric: np.ndarray, - gradient: np.ndarray, - reg_method: Callable[[np.ndarray, np.ndarray, float], float], - lambda1: float = 1e-3, - lambda4: float = 1.0, - tol: float = 1e-8, - ) -> Tuple[float, np.ndarray]: - """ - This method implements a search for a regularization parameter lambda by finding for the - corner of the L-curve. - More explicitly, one has to evaluate a suitable lambda by finding a compromise between - the error in the solution and the norm of the regularization. - This function implements a method presented in - `A simple algorithm to find the L-curve corner in the regularization of inverse problems - ` - - Args: - metric: See (1) and (2). - gradient: See (1) and (2). - reg_method: Given the metric, gradient and lambda the regularization method must return - ``x_lambda`` - see (2). - lambda1: Left starting point for L-curve corner search. - lambda4: Right starting point for L-curve corner search. - tol: Termination threshold. - - Returns: - Regularization coefficient which is the solution to the regularization inverse problem. - """ - - def _get_curvature(x_lambda: List) -> float: - """Calculate Menger curvature - - Menger, K. (1930). Untersuchungen ̈uber Allgemeine Metrik. Math. Ann.,103(1), 466–501 - - Args: - ``x_lambda: [[x_lambdaj], [x_lambdak], [x_lambdal]]`` - ``lambdaj < lambdak < lambdal`` - - Returns: - Menger Curvature - - """ - eps = [] - eta = [] - for x in x_lambda: - try: - eps.append(np.log(np.linalg.norm(np.matmul(metric, x) - gradient) ** 2)) - except ValueError: - eps.append( - np.log(np.linalg.norm(np.matmul(metric, np.transpose(x)) - gradient) ** 2) - ) - eta.append(np.log(max(np.linalg.norm(x) ** 2, ETOL))) - p_temp = 1 - c_k = 0 - for i in range(3): - p_temp *= (eps[np.mod(i + 1, 3)] - eps[i]) ** 2 + ( - eta[np.mod(i + 1, 3)] - eta[i] - ) ** 2 - c_k += eps[i] * eta[np.mod(i + 1, 3)] - eps[np.mod(i + 1, 3)] * eta[i] - c_k = 2 * c_k / max(1e-4, np.sqrt(p_temp)) - return c_k - - def get_lambda2_lambda3(lambda1, lambda4): - gold_sec = (1 + np.sqrt(5)) / 2.0 - lambda2 = 10 ** ((np.log10(lambda4) + np.log10(lambda1) * gold_sec) / (1 + gold_sec)) - lambda3 = 10 ** (np.log10(lambda1) + np.log10(lambda4) - np.log10(lambda2)) - return lambda2, lambda3 - - lambda2, lambda3 = get_lambda2_lambda3(lambda1, lambda4) - lambda_ = [lambda1, lambda2, lambda3, lambda4] - x_lambda = [] - for lam in lambda_: - x_lambda.append(reg_method(metric, gradient, lam)) - counter = 0 - while (lambda_[3] - lambda_[0]) / lambda_[3] >= tol: - counter += 1 - c_2 = _get_curvature(x_lambda[:-1]) - c_3 = _get_curvature(x_lambda[1:]) - while c_3 < 0: - lambda_[3] = lambda_[2] - x_lambda[3] = x_lambda[2] - lambda_[2] = lambda_[1] - x_lambda[2] = x_lambda[1] - lambda2, _ = get_lambda2_lambda3(lambda_[0], lambda_[3]) - lambda_[1] = lambda2 - x_lambda[1] = reg_method(metric, gradient, lambda_[1]) - c_3 = _get_curvature(x_lambda[1:]) - - if c_2 > c_3: - lambda_mc = lambda_[1] - x_mc = x_lambda[1] - lambda_[3] = lambda_[2] - x_lambda[3] = x_lambda[2] - lambda_[2] = lambda_[1] - x_lambda[2] = x_lambda[1] - lambda2, _ = get_lambda2_lambda3(lambda_[0], lambda_[3]) - lambda_[1] = lambda2 - x_lambda[1] = reg_method(metric, gradient, lambda_[1]) - else: - lambda_mc = lambda_[2] - x_mc = x_lambda[2] - lambda_[0] = lambda_[1] - x_lambda[0] = x_lambda[1] - lambda_[1] = lambda_[2] - x_lambda[1] = x_lambda[2] - _, lambda3 = get_lambda2_lambda3(lambda_[0], lambda_[3]) - lambda_[2] = lambda3 - x_lambda[2] = reg_method(metric, gradient, lambda_[2]) - return lambda_mc, x_mc - - @staticmethod - @_optionals.HAS_SKLEARN.require_in_call - def _ridge( - metric: np.ndarray, - gradient: np.ndarray, - lambda_: float = 1.0, - lambda1: float = 1e-4, - lambda4: float = 1e-1, - tol_search: float = 1e-8, - fit_intercept: bool = True, - normalize: bool = False, - copy_a: bool = True, - max_iter: int = 1000, - tol: float = 0.0001, - solver: str = "auto", - random_state: Optional[int] = None, - ) -> Tuple[float, np.ndarray]: - """ - Ridge Regression with automatic search for a good regularization term lambda - x_lambda = arg min{||Ax-C||^2 + lambda*||x||_2^2} (3) - `Scikit Learn Ridge Regression - ` - - Args: - metric: See (1) and (2). - gradient: See (1) and (2). - lambda_ : regularization parameter used if auto_search = False - lambda1: left starting point for L-curve corner search - lambda4: right starting point for L-curve corner search - tol_search: termination threshold for regularization parameter search - fit_intercept: if True calculate intercept - normalize: ignored if fit_intercept=False, if True normalize A for regression - copy_a: if True A is copied, else overwritten - max_iter: max. number of iterations if solver is CG - tol: precision of the regression solution - solver: solver {‘auto’, ‘svd’, ‘cholesky’, ‘lsqr’, ‘sparse_cg’, ‘sag’, ‘saga’} - random_state: seed for the pseudo random number generator used when data is shuffled - - Returns: - regularization coefficient, solution to the regularization inverse problem - - Raises: - MissingOptionalLibraryError: scikit-learn not installed - - """ - from sklearn.linear_model import Ridge - from sklearn.preprocessing import StandardScaler - - reg = Ridge( - alpha=lambda_, - fit_intercept=fit_intercept, - copy_X=copy_a, - max_iter=max_iter, - tol=tol, - solver=solver, - random_state=random_state, - ) - - def reg_method(a, c, alpha): - reg.set_params(alpha=alpha) - if normalize: - reg.fit(StandardScaler().fit_transform(a), c) - else: - reg.fit(a, c) - return reg.coef_ - - lambda_mc, x_mc = NaturalGradient._reg_term_search( - metric, gradient, reg_method, lambda1=lambda1, lambda4=lambda4, tol=tol_search - ) - return lambda_mc, np.transpose(x_mc) - - @staticmethod - @_optionals.HAS_SKLEARN.require_in_call - def _lasso( - metric: np.ndarray, - gradient: np.ndarray, - lambda_: float = 1.0, - lambda1: float = 1e-4, - lambda4: float = 1e-1, - tol_search: float = 1e-8, - fit_intercept: bool = True, - normalize: bool = False, - precompute: Union[bool, Iterable] = False, - copy_a: bool = True, - max_iter: int = 1000, - tol: float = 0.0001, - warm_start: bool = False, - positive: bool = False, - random_state: Optional[int] = None, - selection: str = "random", - ) -> Tuple[float, np.ndarray]: - """ - Lasso Regression with automatic search for a good regularization term lambda - x_lambda = arg min{||Ax-C||^2/(2*n_samples) + lambda*||x||_1} (4) - `Scikit Learn Lasso Regression - ` - - Args: - metric: Matrix of size mxn. - gradient: Vector of size m. - lambda_ : regularization parameter used if auto_search = False - lambda1: left starting point for L-curve corner search - lambda4: right starting point for L-curve corner search - tol_search: termination threshold for regularization parameter search - fit_intercept: if True calculate intercept - normalize: ignored if fit_intercept=False, if True normalize A for regression - precompute: If True compute and use Gram matrix to speed up calculations. - Gram matrix can also be given explicitly - copy_a: if True A is copied, else overwritten - max_iter: max. number of iterations if solver is CG - tol: precision of the regression solution - warm_start: if True reuse solution from previous fit as initialization - positive: if True force positive coefficients - random_state: seed for the pseudo random number generator used when data is shuffled - selection: {'cyclic', 'random'} - - Returns: - regularization coefficient, solution to the regularization inverse problem - - Raises: - MissingOptionalLibraryError: scikit-learn not installed - - """ - from sklearn.linear_model import Lasso - from sklearn.preprocessing import StandardScaler - - reg = Lasso( - alpha=lambda_, - fit_intercept=fit_intercept, - precompute=precompute, - copy_X=copy_a, - max_iter=max_iter, - tol=tol, - warm_start=warm_start, - positive=positive, - random_state=random_state, - selection=selection, - ) - - def reg_method(a, c, alpha): - reg.set_params(alpha=alpha) - if normalize: - reg.fit(StandardScaler().fit_transform(a), c) - else: - reg.fit(a, c) - return reg.coef_ - - lambda_mc, x_mc = NaturalGradient._reg_term_search( - metric, gradient, reg_method, lambda1=lambda1, lambda4=lambda4, tol=tol_search - ) - - return lambda_mc, x_mc - - @staticmethod - def _regularized_sle_solver( - metric: np.ndarray, - gradient: np.ndarray, - regularization: str = "perturb_diag", - lambda1: float = 1e-3, - lambda4: float = 1.0, - alpha: float = 0.0, - tol_norm_x: Tuple[float, float] = (1e-8, 5.0), - tol_cond_a: float = 1000.0, - ) -> np.ndarray: - """ - Solve a linear system of equations with a regularization method and automatic lambda fitting - - Args: - metric: Matrix of size mxn. - gradient: Vector of size m. - regularization: Regularization scheme to be used: 'ridge', 'lasso', - 'perturb_diag_elements' or 'perturb_diag' - lambda1: left starting point for L-curve corner search (for 'ridge' and 'lasso') - lambda4: right starting point for L-curve corner search (for 'ridge' and 'lasso') - alpha: perturbation coefficient for 'perturb_diag_elements' and 'perturb_diag' - tol_norm_x: tolerance for the norm of x - tol_cond_a: tolerance for the condition number of A - - Returns: - solution to the regularized system of linear equations - - """ - if regularization == "ridge": - _, x = NaturalGradient._ridge(metric, gradient, lambda1=lambda1) - elif regularization == "lasso": - _, x = NaturalGradient._lasso(metric, gradient, lambda1=lambda1) - elif regularization == "perturb_diag_elements": - alpha = 1e-7 - while np.linalg.cond(metric + alpha * np.diag(metric)) > tol_cond_a: - alpha *= 10 - # include perturbation in A to avoid singularity - x, _, _, _ = np.linalg.lstsq(metric + alpha * np.diag(metric), gradient, rcond=None) - elif regularization == "perturb_diag": - alpha = 1e-7 - while np.linalg.cond(metric + alpha * np.eye(len(gradient))) > tol_cond_a: - alpha *= 10 - # include perturbation in A to avoid singularity - x, _, _, _ = np.linalg.lstsq( - metric + alpha * np.eye(len(gradient)), gradient, rcond=None - ) - else: - # include perturbation in A to avoid singularity - x, _, _, _ = np.linalg.lstsq(metric, gradient, rcond=None) - - if np.linalg.norm(x) > tol_norm_x[1] or np.linalg.norm(x) < tol_norm_x[0]: - if regularization == "ridge": - lambda1 = lambda1 / 10.0 - _, x = NaturalGradient._ridge(metric, gradient, lambda1=lambda1, lambda4=lambda4) - elif regularization == "lasso": - lambda1 = lambda1 / 10.0 - _, x = NaturalGradient._lasso(metric, gradient, lambda1=lambda1) - elif regularization == "perturb_diag_elements": - while np.linalg.cond(metric + alpha * np.diag(metric)) > tol_cond_a: - if alpha == 0: - alpha = 1e-7 - else: - alpha *= 10 - # include perturbation in A to avoid singularity - x, _, _, _ = np.linalg.lstsq(metric + alpha * np.diag(metric), gradient, rcond=None) - else: - if alpha == 0: - alpha = 1e-7 - else: - alpha *= 10 - while np.linalg.cond(metric + alpha * np.eye(len(gradient))) > tol_cond_a: - # include perturbation in A to avoid singularity - x, _, _, _ = np.linalg.lstsq( - metric + alpha * np.eye(len(gradient)), gradient, rcond=None - ) - alpha *= 10 - return x diff --git a/qiskit/opflow/gradients/qfi.py b/qiskit/opflow/gradients/qfi.py deleted file mode 100644 index b16cd3b3ecc5..000000000000 --- a/qiskit/opflow/gradients/qfi.py +++ /dev/null @@ -1,74 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module for Quantum the Fisher Information.""" - -from typing import List, Union, Optional - -from qiskit.circuit import ParameterExpression, ParameterVector -from qiskit.circuit._utils import sort_parameters -from qiskit.utils.deprecation import deprecate_func -from ..list_ops.list_op import ListOp -from ..expectations.pauli_expectation import PauliExpectation -from ..state_fns.circuit_state_fn import CircuitStateFn -from .qfi_base import QFIBase -from .circuit_qfis import CircuitQFI - - -class QFI(QFIBase): - r"""Deprecated: Compute the Quantum Fisher Information (QFI). - - Computes the QFI given a pure, parameterized quantum state, where QFI is: - - .. math:: - - \mathrm{QFI}_{kl}= 4 \mathrm{Re}[\langle \partial_k \psi | \partial_l \psi \rangle - − \langle\partial_k \psi | \psi \rangle \langle\psi | \partial_l \psi \rangle]. - - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, qfi_method: Union[str, CircuitQFI] = "lin_comb_full"): - super().__init__(qfi_method=qfi_method) - - def convert( - self, - operator: CircuitStateFn, - params: Optional[ - Union[ParameterExpression, ParameterVector, List[ParameterExpression]] - ] = None, - ) -> ListOp: - r""" - Args: - operator: The operator corresponding to the quantum state \|ψ(ω)〉for which we compute - the QFI - params: The parameters we are computing the QFI wrt: ω - If not explicitly passed, they are inferred from the operator and sorted by name. - - Returns: - ListOp[ListOp] where the operator at position k,l corresponds to QFI_kl - - Raises: - ValueError: If operator is not parameterized. - """ - if len(operator.parameters) == 0: - raise ValueError("The operator we are taking the gradient of is not parameterized!") - - expec_op = PauliExpectation(group_paulis=False).convert(operator).reduce() - cleaned_op = self._factor_coeffs_out_of_composed_op(expec_op) - - if params is None: - params = sort_parameters(operator.parameters) - return self.qfi_method.convert(cleaned_op, params) diff --git a/qiskit/opflow/gradients/qfi_base.py b/qiskit/opflow/gradients/qfi_base.py deleted file mode 100644 index b09f9170dbad..000000000000 --- a/qiskit/opflow/gradients/qfi_base.py +++ /dev/null @@ -1,78 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The module for Quantum the Fisher Information.""" - -from typing import Union - -from qiskit.utils.deprecation import deprecate_func -from .derivative_base import DerivativeBase -from .circuit_qfis import CircuitQFI - - -class QFIBase(DerivativeBase): - - r"""Deprecated: Base class for Quantum Fisher Information (QFI). - - Compute the Quantum Fisher Information (QFI) given a pure, parameterized quantum state. - - The QFI is: - - [QFI]kl= Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] * 4. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, qfi_method: Union[str, CircuitQFI] = "lin_comb_full"): - r""" - Args: - qfi_method: The method used to compute the state/probability gradient. Can be either - a :class:`CircuitQFI` instance or one of the following pre-defined strings - ``'lin_comb_full'``, ``'overlap_diag'``` or ``'overlap_block_diag'```. - Raises: - ValueError: if ``qfi_method`` is neither a ``CircuitQFI`` object nor one of the - predefined strings. - """ - super().__init__() - if isinstance(qfi_method, CircuitQFI): - self._qfi_method = qfi_method - - elif qfi_method == "lin_comb_full": - from .circuit_qfis import LinCombFull - - self._qfi_method = LinCombFull() - elif qfi_method == "overlap_block_diag": - from .circuit_qfis import OverlapBlockDiag - - self._qfi_method = OverlapBlockDiag() - elif qfi_method == "overlap_diag": - from .circuit_qfis import OverlapDiag - - self._qfi_method = OverlapDiag() - else: - raise ValueError( - "Unrecognized input provided for `qfi_method`. Please provide" - " a CircuitQFI object or one of the pre-defined string" - " arguments: {'lin_comb_full', 'overlap_diag', " - "'overlap_block_diag'}. " - ) - - @property - def qfi_method(self) -> CircuitQFI: - """Returns ``CircuitQFI``. - - Returns: - ``CircuitQFI``. - """ - return self._qfi_method diff --git a/qiskit/opflow/list_ops/__init__.py b/qiskit/opflow/list_ops/__init__.py deleted file mode 100644 index b4e4a45b3d72..000000000000 --- a/qiskit/opflow/list_ops/__init__.py +++ /dev/null @@ -1,96 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -r""" -List Operators (:mod:`qiskit.opflow.list_ops`) -============================================== - -.. currentmodule:: qiskit.opflow.list_ops - -.. deprecated:: 0.24.0 - - The :mod:`qiskit.opflow` module is deprecated and will be removed no earlier - than 3 months after the release date. For code migration guidelines, - visit https://qisk.it/opflow_migration. - -List Operators are classes for storing and manipulating lists of Operators, State functions, -or Measurements, and include some rule or ``combo_fn`` defining how the Operator functions of the -list constituents should be combined to form to cumulative Operator function of the -:class:`ListOp`. For example, a :class:`SummedOp` has an addition-based ``combo_fn``, so once -the Operators in its list are evaluated against some bitstring to produce a list of results, -we know to add up those results to produce the final result of the :class:`SummedOp`'s evaluation. -In theory, this ``combo_fn`` can be any function over classical complex values, but for convenience -we've chosen for them to be defined over NumPy arrays and values. This way, large numbers of -evaluations, such as after calling :meth:`~ListOp.to_matrix` on the list constituents, -can be efficiently combined. While the combination function is defined over classical values, -it should be understood as the operation by which each Operators' underlying function is -combined to form the underlying Operator function of the :class:`ListOp`. In this way, the -:mod:`.list_ops` are the basis for constructing large and sophisticated Operators, -State Functions, and Measurements. - - -The base :class:`ListOp` class is particularly interesting, as its ``combo_fn`` is "the identity -list Operation". Meaning, if we understand the ``combo_fn`` as a function from a list of complex -values to some output, one such function is returning the list as\-is. This is powerful for -constructing compact hierarchical Operators which return many measurements in multiple -dimensional lists. For example, if we want to estimate the gradient of some Observable -measurement with respect to some parameters in the State function, we can construct separate -evaluation Operators for each parameter's gradient which we must keep track of ourselves in a -list, or we can construct a single :class:`ListOp` containing the evaluation Operators for each -parameter, so the :meth:`~ListOp.eval` function returns the full gradient vector. Another excellent -example of this power is constructing a Quantum kernel matrix: - -.. code-block:: - - data_sfn_list_op = ListOp(data_circuit_state_fns) - qkernel_op_circuits = ~data_sfn_list_op @ data_sfn_list_op - qkernel_sampled = CircuitSampler(backend).convert(qkernel_op_circuits) - qkernel_sampled.eval() - -This will return the two dimensional Quantum kernel matrix, where each element is the inner product -of some pair of the data State functions, or in other terms, a measurement of one data -:class:`~.state_fns.CircuitStateFn` by another. - -You'll encounter the :class:`ListOp` subclasses (:class:`SummedOp`, :class:`ComposedOp`, -or :class:`TensoredOp`) more often as lazy results of Operator construction operations than as -something you need to explicitly construct. Any time we don't know how to efficiently add, -compose, or tensor two :mod:`.primitive_ops` or :mod:`.state_fns` together, they're returned in -a :class:`SummedOp`, :class:`ComposedOp`, or :class:`TensoredOp`, respectively, so we can still work -with their combined function and perhaps convert them into an efficiently combine-able format later. - -Note: - Combination functions do not always behave predictably, and you must understand the - conversions you're making when you working with :mod:`.list_ops`. Most notably - sampling a sum - of two circuits on Quantum hardware does not incorporate interference between the - wavefunctions! In this case, we're sending our State functions through a depolarizing channel - before adding them, rather than adding them directly before the measurement. - -List Operators --------------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - ListOp - ComposedOp - SummedOp - TensoredOp - -""" - -from .list_op import ListOp -from .summed_op import SummedOp -from .composed_op import ComposedOp -from .tensored_op import TensoredOp - -__all__ = ["ListOp", "SummedOp", "TensoredOp", "ComposedOp"] diff --git a/qiskit/opflow/list_ops/composed_op.py b/qiskit/opflow/list_ops/composed_op.py deleted file mode 100644 index d47a81fbd796..000000000000 --- a/qiskit/opflow/list_ops/composed_op.py +++ /dev/null @@ -1,197 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""ComposedOp Class""" - -from functools import partial, reduce -from typing import List, Optional, Union, cast, Dict - -from numbers import Number -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import ParameterExpression -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.quantum_info import Statevector -from qiskit.utils.deprecation import deprecate_func - - -class ComposedOp(ListOp): - """Deprecated: A class for lazily representing compositions of Operators. Often Operators cannot be - efficiently composed with one another, but may be manipulated further so that they can be - composed later. This class holds logic to indicate that the Operators in ``oplist`` are meant to - be composed, and therefore if they reach a point in which they can be, such as after - conversion to QuantumCircuits or matrices, they can be reduced by composition.""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - oplist: List[OperatorBase], - coeff: Union[complex, ParameterExpression] = 1.0, - abelian: bool = False, - ) -> None: - """ - Args: - oplist: The Operators being composed. - coeff: A coefficient multiplying the operator - abelian: Indicates whether the Operators in ``oplist`` are known to mutually commute. - """ - super().__init__(oplist, combo_fn=partial(reduce, np.dot), coeff=coeff, abelian=abelian) - - @property - def num_qubits(self) -> int: - return self.oplist[0].num_qubits - - @property - def distributive(self) -> bool: - return False - - @property - def settings(self) -> Dict: - """Return settings.""" - return {"oplist": self._oplist, "coeff": self._coeff, "abelian": self._abelian} - - # TODO take advantage of the mixed product property, tensorpower each element in the composition - # def tensorpower(self, other): - # """ Tensor product with Self Multiple Times """ - # raise NotImplementedError - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) - - mat = self.coeff * reduce( - np.dot, [np.asarray(op.to_matrix(massive=massive)) for op in self.oplist] - ) - - # Note: As ComposedOp has a combo function of inner product we can end up here not with - # a matrix (array) but a scalar. In which case we make a single element array of it. - if isinstance(mat, Number): - mat = [mat] - - return np.asarray(mat, dtype=complex) - - def to_circuit(self) -> QuantumCircuit: - """Returns the quantum circuit, representing the composed operator. - - Returns: - The circuit representation of the composed operator. - - Raises: - OpflowError: for operators where a single underlying circuit can not be obtained. - """ - # pylint: disable=cyclic-import - from ..state_fns.circuit_state_fn import CircuitStateFn - from ..primitive_ops.primitive_op import PrimitiveOp - - circuit_op = self.to_circuit_op() - if isinstance(circuit_op, (PrimitiveOp, CircuitStateFn)): - return circuit_op.to_circuit() - raise OpflowError( - "Conversion to_circuit supported only for operators, where a single " - "underlying circuit can be produced." - ) - - def adjoint(self) -> "ComposedOp": - return ComposedOp([op.adjoint() for op in reversed(self.oplist)], coeff=self.coeff) - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - new_self = cast(ComposedOp, new_self) - - if front: - return other.compose(new_self) - # Try composing with last element in list - if isinstance(other, ComposedOp): - return ComposedOp(new_self.oplist + other.oplist, coeff=new_self.coeff * other.coeff) - - # Try composing with last element of oplist. We only try - # this if that last element isn't itself an - # ComposedOp, so we can tell whether composing the - # two elements directly worked. If it doesn't, - # continue to the final return statement below, appending other to the oplist. - if not isinstance(new_self.oplist[-1], ComposedOp): - comp_with_last = new_self.oplist[-1].compose(other) - # Attempt successful - if not isinstance(comp_with_last, ComposedOp): - new_oplist = new_self.oplist[0:-1] + [comp_with_last] - return ComposedOp(new_oplist, coeff=new_self.coeff) - - return ComposedOp(new_self.oplist + [other], coeff=new_self.coeff) - - def eval( - self, front: Optional[Union[str, dict, np.ndarray, OperatorBase, Statevector]] = None - ) -> Union[OperatorBase, complex]: - if self._is_empty(): - return 0.0 - - # pylint: disable=cyclic-import - from ..state_fns.state_fn import StateFn - - def tree_recursive_eval(r, l_arg): - if isinstance(r, list): - return [tree_recursive_eval(r_op, l_arg) for r_op in r] - else: - return l_arg.eval(r) - - eval_list = self.oplist.copy() - # Only one op needs to be multiplied, so just multiply the first. - eval_list[0] = eval_list[0] * self.coeff # type: ignore - if front and isinstance(front, OperatorBase): - eval_list = eval_list + [front] - elif front: - eval_list = [StateFn(front, is_measurement=True)] + eval_list # type: ignore - - return reduce(tree_recursive_eval, reversed(eval_list)) - - # Try collapsing list or trees of compositions into a single . - def non_distributive_reduce(self) -> OperatorBase: - """Reduce without attempting to expand all distributive compositions. - - Returns: - The reduced Operator. - """ - reduced_ops = [op.reduce() for op in self.oplist] - reduced_ops = reduce(lambda x, y: x.compose(y), reduced_ops) * self.coeff - if isinstance(reduced_ops, ComposedOp) and len(reduced_ops.oplist) > 1: - return reduced_ops - else: - return reduced_ops[0] - - def reduce(self) -> OperatorBase: - reduced_ops = [op.reduce() for op in self.oplist] - if len(reduced_ops) == 0: - return self.__class__([], coeff=self.coeff, abelian=self.abelian) - - def distribute_compose(l_arg, r): - if isinstance(l_arg, ListOp) and l_arg.distributive: - # Either ListOp or SummedOp, returns correct type - return l_arg.__class__( - [distribute_compose(l_op * l_arg.coeff, r) for l_op in l_arg.oplist] - ) - if isinstance(r, ListOp) and r.distributive: - return r.__class__([distribute_compose(l_arg, r_op * r.coeff) for r_op in r.oplist]) - else: - return l_arg.compose(r) - - reduced_ops = reduce(distribute_compose, reduced_ops) * self.coeff - if isinstance(reduced_ops, ListOp) and len(reduced_ops.oplist) == 1: - return reduced_ops.oplist[0] - else: - return cast(OperatorBase, reduced_ops) diff --git a/qiskit/opflow/list_ops/list_op.py b/qiskit/opflow/list_ops/list_op.py deleted file mode 100644 index abf2d2326d0c..000000000000 --- a/qiskit/opflow/list_ops/list_op.py +++ /dev/null @@ -1,640 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""ListOp Operator Class""" - -from functools import reduce -from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Sequence, Union, cast - -import numpy as np -from scipy.sparse import spmatrix - -from qiskit.circuit import ParameterExpression, QuantumCircuit -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.operator_base import OperatorBase -from qiskit.quantum_info import Statevector -from qiskit.utils import arithmetic -from qiskit.utils.deprecation import deprecate_func - - -class ListOp(OperatorBase): - """ - Deprecated: A Class for manipulating List Operators, and parent class to ``SummedOp``, - ``ComposedOp`` and ``TensoredOp``. - - List Operators are classes for storing and manipulating lists of Operators, State functions, - or Measurements, and include some rule or ``combo_fn`` defining how the Operator functions - of the list constituents should be combined to form to cumulative Operator function of the - ``ListOp``. For example, a ``SummedOp`` has an addition-based ``combo_fn``, so once the - Operators in its list are evaluated against some bitstring to produce a list of results, - we know to add up those results to produce the final result of the ``SummedOp``'s - evaluation. In theory, this ``combo_fn`` can be any function over classical complex values, - but for convenience we've chosen for them to be defined over NumPy arrays and values. This way, - large numbers of evaluations, such as after calling ``to_matrix`` on the list constituents, - can be efficiently combined. While the combination function is defined over classical - values, it should be understood as the operation by which each Operators' underlying - function is combined to form the underlying Operator function of the ``ListOp``. In this - way, the ``ListOps`` are the basis for constructing large and sophisticated Operators, - State Functions, and Measurements. - - The base ``ListOp`` class is particularly interesting, as its ``combo_fn`` is "the identity - list Operation". Meaning, if we understand the ``combo_fn`` as a function from a list of - complex values to some output, one such function is returning the list as-is. This is - powerful for constructing compact hierarchical Operators which return many measurements in - multiple dimensional lists. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - oplist: Sequence[OperatorBase], - combo_fn: Optional[Callable] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - abelian: bool = False, - grad_combo_fn: Optional[Callable] = None, - ) -> None: - """ - Args: - oplist: The list of ``OperatorBases`` defining this Operator's underlying function. - combo_fn: The recombination function to combine classical results of the - ``oplist`` Operators' eval functions (e.g. sum). Default is lambda x: x. - coeff: A coefficient multiplying the operator - abelian: Indicates whether the Operators in ``oplist`` are known to mutually commute. - grad_combo_fn: The gradient of recombination function. If None, the gradient will - be computed automatically. - Note that the default "recombination function" lambda above is essentially the - identity - it accepts the list of values, and returns them in a list. - """ - super().__init__() - self._oplist = self._check_input_types(oplist) - self._combo_fn = combo_fn - self._coeff = coeff - self._abelian = abelian - self._grad_combo_fn = grad_combo_fn - - def _check_input_types(self, oplist): - if all(isinstance(x, OperatorBase) for x in oplist): - return list(oplist) - else: - badval = next(x for x in oplist if not isinstance(x, OperatorBase)) - raise TypeError(f"ListOp expecting objects of type OperatorBase, got {badval}") - - def _state( - self, - coeff: Optional[Union[complex, ParameterExpression]] = None, - combo_fn: Optional[Callable] = None, - abelian: Optional[bool] = None, - grad_combo_fn: Optional[Callable] = None, - ) -> Dict: - return { - "coeff": coeff if coeff is not None else self.coeff, - "combo_fn": combo_fn if combo_fn is not None else self.combo_fn, - "abelian": abelian if abelian is not None else self.abelian, - "grad_combo_fn": grad_combo_fn if grad_combo_fn is not None else self.grad_combo_fn, - } - - @property - def settings(self) -> Dict: - """Return settings.""" - return { - "oplist": self._oplist, - "combo_fn": self._combo_fn, - "coeff": self._coeff, - "abelian": self._abelian, - "grad_combo_fn": self._grad_combo_fn, - } - - @property - def oplist(self) -> List[OperatorBase]: - """The list of ``OperatorBases`` defining the underlying function of this - Operator. - - Returns: - The Operators defining the ListOp - """ - return self._oplist - - @staticmethod - def default_combo_fn(x: Any) -> Any: - """ListOp default combo function i.e. lambda x: x""" - return x - - @property - def combo_fn(self) -> Callable: - """The function defining how to combine ``oplist`` (or Numbers, or NumPy arrays) to - produce the Operator's underlying function. For example, SummedOp's combination function - is to add all of the Operators in ``oplist``. - - Returns: - The combination function. - """ - if self._combo_fn is None: - return ListOp.default_combo_fn - return self._combo_fn - - @property - def grad_combo_fn(self) -> Optional[Callable]: - """The gradient of ``combo_fn``.""" - return self._grad_combo_fn - - @property - def abelian(self) -> bool: - """Whether the Operators in ``oplist`` are known to commute with one another. - - Returns: - A bool indicating whether the ``oplist`` is Abelian. - """ - return self._abelian - - @property - def distributive(self) -> bool: - """Indicates whether the ListOp or subclass is distributive under composition. - ListOp and SummedOp are, meaning that (opv @ op) = (opv[0] @ op + opv[1] @ op) - (using plus for SummedOp, list for ListOp, etc.), while ComposedOp and TensoredOp - do not behave this way. - - Returns: - A bool indicating whether the ListOp is distributive under composition. - """ - return True - - @property - def coeff(self) -> Union[complex, ParameterExpression]: - """The scalar coefficient multiplying the Operator. - - Returns: - The coefficient. - """ - return self._coeff - - @property - def coeffs(self) -> List[Union[complex, ParameterExpression]]: - """Return a list of the coefficients of the operators listed. - Raises exception for nested Listops. - """ - if any(isinstance(op, ListOp) for op in self.oplist): - raise TypeError("Coefficients are not returned for nested ListOps.") - return [self.coeff * op.coeff for op in self.oplist] - - def primitive_strings(self) -> Set[str]: - return reduce(set.union, [op.primitive_strings() for op in self.oplist]) - - @property - def num_qubits(self) -> int: - num_qubits0 = self.oplist[0].num_qubits - if not all(num_qubits0 == op.num_qubits for op in self.oplist): - raise ValueError("Operators in ListOp have differing numbers of qubits.") - return num_qubits0 - - def add(self, other: OperatorBase) -> "ListOp": - if self == other: - return self.mul(2.0) - - # Avoid circular dependency - # pylint: disable=cyclic-import - from .summed_op import SummedOp - - return SummedOp([self, other]) - - def adjoint(self) -> "ListOp": - # TODO do this lazily? Basically rebuilds the entire tree, and ops and adjoints almost - # always come in pairs, so an AdjointOp holding a reference could save copying. - if self.__class__ == ListOp: - return ListOp( - [op.adjoint() for op in self.oplist], **self._state(coeff=self.coeff.conjugate()) - ) # coeff is conjugated - return self.__class__( - [op.adjoint() for op in self.oplist], coeff=self.coeff.conjugate(), abelian=self.abelian - ) - - def traverse( - self, convert_fn: Callable, coeff: Optional[Union[complex, ParameterExpression]] = None - ) -> "ListOp": - """Apply the convert_fn to each node in the oplist. - - Args: - convert_fn: The function to apply to the internal OperatorBase. - coeff: A coefficient to multiply by after applying convert_fn. - If it is None, self.coeff is used instead. - - Returns: - The converted ListOp. - """ - if coeff is None: - coeff = self.coeff - - if self.__class__ == ListOp: - return ListOp([convert_fn(op) for op in self.oplist], **self._state(coeff=coeff)) - return self.__class__( - [convert_fn(op) for op in self.oplist], coeff=coeff, abelian=self.abelian - ) - - def equals(self, other: OperatorBase) -> bool: - if not isinstance(other, type(self)) or not len(self.oplist) == len(other.oplist): - return False - # Note, ordering matters here (i.e. different list orders will return False) - return self.coeff == other.coeff and all( - op1 == op2 for op1, op2 in zip(self.oplist, other.oplist) - ) - - # We need to do this because otherwise Numpy takes over scalar multiplication and wrecks it if - # isinstance(scalar, np.number) - this started happening when we added __get_item__(). - __array_priority__ = 10000 - - def mul(self, scalar: Union[complex, ParameterExpression]) -> "ListOp": - if not isinstance(scalar, (int, float, complex, ParameterExpression)): - raise ValueError( - "Operators can only be scalar multiplied by float or complex, not " - "{} of type {}.".format(scalar, type(scalar)) - ) - if self.__class__ == ListOp: - return ListOp(self.oplist, **self._state(coeff=scalar * self.coeff)) - return self.__class__(self.oplist, coeff=scalar * self.coeff, abelian=self.abelian) - - def tensor(self, other: OperatorBase) -> OperatorBase: - # Avoid circular dependency - # pylint: disable=cyclic-import - from .tensored_op import TensoredOp - - return TensoredOp([self, other]) - - def tensorpower(self, other: int) -> Union[OperatorBase, int]: - # Hack to make op1^(op2^0) work as intended. - if other == 0: - return 1 - if not isinstance(other, int) or other <= 0: - raise TypeError("Tensorpower can only take positive int arguments") - - # Avoid circular dependency - # pylint: disable=cyclic-import - from .tensored_op import TensoredOp - - return TensoredOp([self] * other) - - def _expand_dim(self, num_qubits: int) -> "ListOp": - oplist = [ - op._expand_dim(num_qubits + self.num_qubits - op.num_qubits) for op in self.oplist - ] - return ListOp(oplist, **self._state()) - - def permute(self, permutation: List[int]) -> "OperatorBase": - """Permute the qubits of the operator. - - Args: - permutation: A list defining where each qubit should be permuted. The qubit at index - j should be permuted to position permutation[j]. - - Returns: - A new ListOp representing the permuted operator. - - Raises: - OpflowError: if indices do not define a new index for each qubit. - """ - new_self = self - circuit_size = max(permutation) + 1 - - try: - if self.num_qubits != len(permutation): - raise OpflowError("New index must be defined for each qubit of the operator.") - except ValueError: - raise OpflowError( - "Permute is only possible if all operators in the ListOp have the " - "same number of qubits." - ) from ValueError - if self.num_qubits < circuit_size: - # pad the operator with identities - new_self = self._expand_dim(circuit_size - self.num_qubits) - qc = QuantumCircuit(circuit_size) - # extend the indices to match the size of the circuit - permutation = ( - list(filter(lambda x: x not in permutation, range(circuit_size))) + permutation - ) - - # decompose permutation into sequence of transpositions - transpositions = arithmetic.transpositions(permutation) - for trans in transpositions: - qc.swap(trans[0], trans[1]) - - # pylint: disable=cyclic-import - from ..primitive_ops.circuit_op import CircuitOp - - return CircuitOp(qc.reverse_ops()) @ new_self @ CircuitOp(qc) - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - new_self = cast(ListOp, new_self) - - if front: - return other.compose(new_self) - # Avoid circular dependency - # pylint: disable=cyclic-import - from .composed_op import ComposedOp - - return ComposedOp([new_self, other]) - - def power(self, exponent: int) -> OperatorBase: - if not isinstance(exponent, int) or exponent <= 0: - raise TypeError("power can only take positive int arguments") - - # Avoid circular dependency - # pylint: disable=cyclic-import - from .composed_op import ComposedOp - - return ComposedOp([self] * exponent) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) - - # Combination function must be able to handle classical values. - # Note: this can end up, when we have list operators containing other list operators, as a - # ragged array and numpy 1.19 raises a deprecation warning unless this is explicitly - # done as object type now - was implicit before. - mat = self.combo_fn( - np.asarray( - [op.to_matrix(massive=massive) * self.coeff for op in self.oplist], dtype=object - ) - ) - return np.asarray(mat, dtype=complex) - - def to_spmatrix(self) -> Union[spmatrix, List[spmatrix]]: - """Returns SciPy sparse matrix representation of the Operator. - - Returns: - CSR sparse matrix representation of the Operator, or List thereof. - """ - - # Combination function must be able to handle classical values - return self.combo_fn([op.to_spmatrix() for op in self.oplist]) * self.coeff - - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - """ - Evaluate the Operator's underlying function, either on a binary string or another Operator. - A square binary Operator can be defined as a function taking a binary function to another - binary function. This method returns the value of that function for a given StateFn or - binary string. For example, ``op.eval('0110').eval('1110')`` can be seen as querying the - Operator's matrix representation by row 6 and column 14, and will return the complex - value at those "indices." Similarly for a StateFn, ``op.eval('1011')`` will return the - complex value at row 11 of the vector representation of the StateFn, as all StateFns are - defined to be evaluated from Zero implicitly (i.e. it is as if ``.eval('0000')`` is already - called implicitly to always "indexing" from column 0). - - ListOp's eval recursively evaluates each Operator in ``oplist``, - and combines the results using the recombination function ``combo_fn``. - - Args: - front: The bitstring, dict of bitstrings (with values being coefficients), or - StateFn to evaluated by the Operator's underlying function. - - Returns: - The output of the ``oplist`` Operators' evaluation function, combined with the - ``combo_fn``. If either self or front contain proper ``ListOps`` (not ListOp - subclasses), the result is an n-dimensional list of complex or StateFn results, - resulting from the recursive evaluation by each OperatorBase in the ListOps. - - Raises: - NotImplementedError: Raised if called for a subclass which is not distributive. - TypeError: Operators with mixed hierarchies, such as a ListOp containing both - PrimitiveOps and ListOps, are not supported. - NotImplementedError: Attempting to call ListOp's eval from a non-distributive subclass. - - """ - # pylint: disable=cyclic-import - from ..state_fns.dict_state_fn import DictStateFn - from ..state_fns.vector_state_fn import VectorStateFn - from ..state_fns.sparse_vector_state_fn import SparseVectorStateFn - - # The below code only works for distributive ListOps, e.g. ListOp and SummedOp - if not self.distributive: - raise NotImplementedError( - "ListOp's eval function is only defined for distributive ListOps." - ) - - evals = [op.eval(front) for op in self.oplist] - - # Handle application of combo_fn for DictStateFn resp VectorStateFn operators - if self._combo_fn is not None: # If not using default. - if ( - all(isinstance(op, DictStateFn) for op in evals) - or all(isinstance(op, VectorStateFn) for op in evals) - or all(isinstance(op, SparseVectorStateFn) for op in evals) - ): - if not all( - op.is_measurement == evals[0].is_measurement for op in evals # type: ignore - ): - raise NotImplementedError( - "Combo_fn not yet supported for mixed measurement " - "and non-measurement StateFns" - ) - result = self.combo_fn(evals) - if isinstance(result, list): - multiplied = self.coeff * np.array(result) - return multiplied.tolist() - return self.coeff * result - - if all(isinstance(op, OperatorBase) for op in evals): - return self.__class__(evals) # type: ignore - elif any(isinstance(op, OperatorBase) for op in evals): - raise TypeError("Cannot handle mixed scalar and Operator eval results.") - else: - result = self.combo_fn(evals) - if isinstance(result, list): - multiplied = self.coeff * np.array(result) - return multiplied.tolist() - return self.coeff * result - - def exp_i(self) -> OperatorBase: - """Return an ``OperatorBase`` equivalent to an exponentiation of self * -i, e^(-i*op).""" - # pylint: disable=unidiomatic-typecheck - if type(self) == ListOp: - return ListOp( - [op.exp_i() for op in self.oplist], **self._state(abelian=False) # type: ignore - ) - - # pylint: disable=cyclic-import - from ..evolutions.evolved_op import EvolvedOp - - return EvolvedOp(self) - - def log_i(self, massive: bool = False) -> OperatorBase: - """Return a ``MatrixOp`` equivalent to log(H)/-i for this operator H. This - function is the effective inverse of exp_i, equivalent to finding the Hermitian - Operator which produces self when exponentiated. For proper ListOps, applies ``log_i`` - to all ops in oplist. - """ - if self.__class__.__name__ == ListOp.__name__: - return ListOp( - [op.log_i(massive=massive) for op in self.oplist], # type: ignore - **self._state(abelian=False), - ) - - return self.to_matrix_op(massive=massive).log_i(massive=massive) - - def __str__(self) -> str: - content_string = ",\n".join([str(op) for op in self.oplist]) - main_string = "{}([\n{}\n])".format( - self.__class__.__name__, self._indent(content_string, indentation=self.INDENTATION) - ) - if self.abelian: - main_string = "Abelian" + main_string - if self.coeff != 1.0: - main_string = f"{self.coeff} * " + main_string - return main_string - - def __repr__(self) -> str: - return "{}({}, coeff={}, abelian={})".format( - self.__class__.__name__, repr(self.oplist), self.coeff, self.abelian - ) - - @property - def parameters(self): - params = set() - for op in self.oplist: - params.update(op.parameters) - if isinstance(self.coeff, ParameterExpression): - params.update(self.coeff.parameters) - return params - - def assign_parameters(self, param_dict: dict) -> OperatorBase: - param_value = self.coeff - if isinstance(self.coeff, ParameterExpression): - unrolled_dict = self._unroll_param_dict(param_dict) - if isinstance(unrolled_dict, list): - return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) - if self.coeff.parameters <= set(unrolled_dict.keys()): - binds = {param: unrolled_dict[param] for param in self.coeff.parameters} - param_value = float(self.coeff.bind(binds)) - return self.traverse(lambda x: x.assign_parameters(param_dict), coeff=param_value) - - def reduce(self) -> OperatorBase: - reduced_ops = [op.reduce() for op in self.oplist] - if self.__class__ == ListOp: - return ListOp(reduced_ops, **self._state()) - return self.__class__(reduced_ops, coeff=self.coeff, abelian=self.abelian) - - def to_matrix_op(self, massive: bool = False) -> "ListOp": - """Returns an equivalent Operator composed of only NumPy-based primitives, such as - ``MatrixOp`` and ``VectorStateFn``.""" - if self.__class__ == ListOp: - return cast( - ListOp, - ListOp( - [op.to_matrix_op(massive=massive) for op in self.oplist], **self._state() - ).reduce(), - ) - return cast( - ListOp, - self.__class__( - [op.to_matrix_op(massive=massive) for op in self.oplist], - coeff=self.coeff, - abelian=self.abelian, - ).reduce(), - ) - - def to_circuit_op(self) -> OperatorBase: - """Returns an equivalent Operator composed of only QuantumCircuit-based primitives, - such as ``CircuitOp`` and ``CircuitStateFn``.""" - # pylint: disable=cyclic-import - from ..state_fns.operator_state_fn import OperatorStateFn - - if self.__class__ == ListOp: - return ListOp( - [ - op.to_circuit_op() if not isinstance(op, OperatorStateFn) else op - for op in self.oplist - ], - **self._state(), - ).reduce() - return self.__class__( - [ - op.to_circuit_op() if not isinstance(op, OperatorStateFn) else op - for op in self.oplist - ], - coeff=self.coeff, - abelian=self.abelian, - ).reduce() - - def to_pauli_op(self, massive: bool = False) -> "ListOp": - """Returns an equivalent Operator composed of only Pauli-based primitives, - such as ``PauliOp``.""" - # pylint: disable=cyclic-import - from ..state_fns.state_fn import StateFn - - if self.__class__ == ListOp: - return ListOp( - [ - op.to_pauli_op(massive=massive) # type: ignore - if not isinstance(op, StateFn) - else op - for op in self.oplist - ], - **self._state(), - ).reduce() - return self.__class__( - [ - op.to_pauli_op(massive=massive) # type: ignore - if not isinstance(op, StateFn) - else op - for op in self.oplist - ], - coeff=self.coeff, - abelian=self.abelian, - ).reduce() - - def _is_empty(self): - return len(self.oplist) == 0 - - # Array operations: - - def __getitem__(self, offset: Union[int, slice]) -> OperatorBase: - """Allows array-indexing style access to the Operators in ``oplist``. - - Args: - offset: The index of ``oplist`` desired. - - Returns: - The ``OperatorBase`` at index ``offset`` of ``oplist``, - or another ListOp with the same properties as this one if offset is a slice. - """ - if isinstance(offset, int): - return self.oplist[offset] - - if self.__class__ == ListOp: - return ListOp(oplist=self._oplist[offset], **self._state()) - - return self.__class__(oplist=self._oplist[offset], coeff=self._coeff, abelian=self._abelian) - - def __iter__(self) -> Iterator: - """Returns an iterator over the operators in ``oplist``. - - Returns: - An iterator over the operators in ``oplist`` - """ - return iter(self.oplist) - - def __len__(self) -> int: - """Length of ``oplist``. - - Returns: - An int equal to the length of ``oplist``. - """ - return len(self.oplist) diff --git a/qiskit/opflow/list_ops/summed_op.py b/qiskit/opflow/list_ops/summed_op.py deleted file mode 100644 index 52f7faa15083..000000000000 --- a/qiskit/opflow/list_ops/summed_op.py +++ /dev/null @@ -1,250 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""SummedOp Class""" - -from typing import List, Union, cast, Dict - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import ParameterExpression -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.utils.deprecation import deprecate_func - - -class SummedOp(ListOp): - """Deprecated: A class for lazily representing sums of Operators. Often Operators cannot be - efficiently added to one another, but may be manipulated further so that they can be - later. This class holds logic to indicate that the Operators in ``oplist`` are meant to - be added together, and therefore if they reach a point in which they can be, such as after - evaluation or conversion to matrices, they can be reduced by addition.""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - oplist: List[OperatorBase], - coeff: Union[complex, ParameterExpression] = 1.0, - abelian: bool = False, - ) -> None: - """ - Args: - oplist: The Operators being summed. - coeff: A coefficient multiplying the operator - abelian: Indicates whether the Operators in ``oplist`` are known to mutually commute. - """ - super().__init__(oplist, combo_fn=lambda x: np.sum(x, axis=0), coeff=coeff, abelian=abelian) - - @property - def num_qubits(self) -> int: - return self.oplist[0].num_qubits - - @property - def distributive(self) -> bool: - return True - - @property - def settings(self) -> Dict: - """Return settings.""" - return {"oplist": self._oplist, "coeff": self._coeff, "abelian": self._abelian} - - def add(self, other: OperatorBase) -> "SummedOp": - """Return Operator addition of ``self`` and ``other``, overloaded by ``+``. - - Note: - This appends ``other`` to ``self.oplist`` without checking ``other`` is already - included or not. If you want to simplify them, please use :meth:`simplify`. - - Args: - other: An ``OperatorBase`` with the same number of qubits as self, and in the same - 'Operator', 'State function', or 'Measurement' category as self (i.e. the same type - of underlying function). - - Returns: - A ``SummedOp`` equivalent to the sum of self and other. - """ - self_new_ops = ( - self.oplist if self.coeff == 1 else [op.mul(self.coeff) for op in self.oplist] - ) - if isinstance(other, SummedOp): - other_new_ops = ( - other.oplist if other.coeff == 1 else [op.mul(other.coeff) for op in other.oplist] - ) - else: - other_new_ops = [other] - return SummedOp(self_new_ops + other_new_ops) - - def collapse_summands(self) -> "SummedOp": - """Return Operator by simplifying duplicate operators. - - E.g., ``SummedOp([2 * X ^ Y, X ^ Y]).collapse_summands() -> SummedOp([3 * X ^ Y])``. - - Returns: - A simplified ``SummedOp`` equivalent to self. - """ - # pylint: disable=cyclic-import - from ..primitive_ops.primitive_op import PrimitiveOp - - oplist = [] # type: List[OperatorBase] - coeffs = [] # type: List[Union[int, float, complex, ParameterExpression]] - for op in self.oplist: - if isinstance(op, PrimitiveOp): - new_op = PrimitiveOp(op.primitive) - new_coeff = op.coeff * self.coeff - if new_op in oplist: - index = oplist.index(new_op) - coeffs[index] += new_coeff - else: - oplist.append(new_op) - coeffs.append(new_coeff) - else: - if op in oplist: - index = oplist.index(op) - coeffs[index] += self.coeff - else: - oplist.append(op) - coeffs.append(self.coeff) - return SummedOp([op * coeff for op, coeff in zip(oplist, coeffs)]) - - # TODO be smarter about the fact that any two ops in oplist could be evaluated for sum. - def reduce(self) -> OperatorBase: - """Try collapsing list or trees of sums. - - Tries to sum up duplicate operators and reduces the operators - in the sum. - - Returns: - A collapsed version of self, if possible. - """ - if len(self.oplist) == 0: - return SummedOp([], coeff=self.coeff, abelian=self.abelian) - - # reduce constituents - reduced_ops = sum(op.reduce() for op in self.oplist) * self.coeff - - # group duplicate operators - if isinstance(reduced_ops, SummedOp): - reduced_ops = reduced_ops.collapse_summands() - - # pylint: disable=cyclic-import - from ..primitive_ops.pauli_sum_op import PauliSumOp - - if isinstance(reduced_ops, PauliSumOp): - reduced_ops = reduced_ops.reduce() - - if isinstance(reduced_ops, SummedOp) and len(reduced_ops.oplist) == 1: - return reduced_ops.oplist[0] - else: - return cast(OperatorBase, reduced_ops) - - def to_circuit(self) -> QuantumCircuit: - """Returns the quantum circuit, representing the SummedOp. In the first step, - the SummedOp is converted to MatrixOp. This is straightforward for most operators, - but it is not supported for operators containing parameterized PrimitiveOps (in that case, - OpflowError is raised). In the next step, the MatrixOp representation of SummedOp is - converted to circuit. In most cases, if the summands themselves are unitary operators, - the SummedOp itself is non-unitary and can not be converted to circuit. In that case, - ExtensionError is raised in the underlying modules. - - Returns: - The circuit representation of the summed operator. - - Raises: - OpflowError: if SummedOp can not be converted to MatrixOp (e.g. SummedOp is composed of - parameterized PrimitiveOps). - """ - # pylint: disable=cyclic-import - from ..primitive_ops.matrix_op import MatrixOp - - matrix_op = self.to_matrix_op() - if isinstance(matrix_op, MatrixOp): - return matrix_op.to_circuit() - raise OpflowError( - "The SummedOp can not be converted to circuit, because to_matrix_op did " - "not return a MatrixOp." - ) - - def to_matrix_op(self, massive: bool = False) -> "SummedOp": - """Returns an equivalent Operator composed of only NumPy-based primitives, such as - ``MatrixOp`` and ``VectorStateFn``.""" - accum = self.oplist[0].to_matrix_op(massive=massive) - for i in range(1, len(self.oplist)): - accum += self.oplist[i].to_matrix_op(massive=massive) - - return cast(SummedOp, accum * self.coeff) - - def to_pauli_op(self, massive: bool = False) -> "SummedOp": - # pylint: disable=cyclic-import - from ..state_fns.state_fn import StateFn - - pauli_sum = SummedOp( - [ - op.to_pauli_op(massive=massive) # type: ignore - if not isinstance(op, StateFn) - else op - for op in self.oplist - ], - coeff=self.coeff, - abelian=self.abelian, - ).reduce() - if isinstance(pauli_sum, SummedOp): - return pauli_sum - return pauli_sum.to_pauli_op() # type: ignore - - def equals(self, other: OperatorBase) -> bool: - """Check if other is equal to self. - - Note: - This is not a mathematical check for equality. - If ``self`` and ``other`` implement the same operation but differ - in the representation (e.g. different type of summands) - ``equals`` will evaluate to ``False``. - - Args: - other: The other operator to check for equality. - - Returns: - True, if other and self are equal, otherwise False. - - Examples: - >>> from qiskit.opflow import X, Z - >>> 2 * X == X + X - True - >>> X + Z == Z + X - True - """ - self_reduced, other_reduced = self.reduce(), other.reduce() - if not isinstance(other_reduced, type(self_reduced)): - return False - - # check if reduced op is still a SummedOp - if not isinstance(self_reduced, SummedOp): - return self_reduced == other_reduced - - self_reduced = cast(SummedOp, self_reduced) - other_reduced = cast(SummedOp, other_reduced) - if len(self_reduced.oplist) != len(other_reduced.oplist): - return False - - # absorb coeffs into the operators - if self_reduced.coeff != 1: - self_reduced = SummedOp([op * self_reduced.coeff for op in self_reduced.oplist]) - if other_reduced.coeff != 1: - other_reduced = SummedOp([op * other_reduced.coeff for op in other_reduced.oplist]) - - # compare independent of order - return all(any(i == j for j in other_reduced) for i in self_reduced) diff --git a/qiskit/opflow/list_ops/tensored_op.py b/qiskit/opflow/list_ops/tensored_op.py deleted file mode 100644 index e3eb30cfaa41..000000000000 --- a/qiskit/opflow/list_ops/tensored_op.py +++ /dev/null @@ -1,129 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""TensoredOp Class""" - -from functools import partial, reduce -from typing import List, Union, cast, Dict - -import numpy as np - -from qiskit.circuit import ParameterExpression, QuantumCircuit -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.quantum_info import Statevector -from qiskit.utils.deprecation import deprecate_func - - -class TensoredOp(ListOp): - """Deprecated: A class for lazily representing tensor products of Operators. Often Operators - cannot be efficiently tensored to one another, but may be manipulated further so that they can be - later. This class holds logic to indicate that the Operators in ``oplist`` are meant to - be tensored together, and therefore if they reach a point in which they can be, such as after - conversion to QuantumCircuits, they can be reduced by tensor product.""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - oplist: List[OperatorBase], - coeff: Union[complex, ParameterExpression] = 1.0, - abelian: bool = False, - ) -> None: - """ - Args: - oplist: The Operators being tensored. - coeff: A coefficient multiplying the operator - abelian: Indicates whether the Operators in ``oplist`` are known to mutually commute. - """ - super().__init__(oplist, combo_fn=partial(reduce, np.kron), coeff=coeff, abelian=abelian) - - @property - def num_qubits(self) -> int: - return sum(op.num_qubits for op in self.oplist) - - @property - def distributive(self) -> bool: - return False - - @property - def settings(self) -> Dict: - """Return settings.""" - return {"oplist": self._oplist, "coeff": self._coeff, "abelian": self._abelian} - - def _expand_dim(self, num_qubits: int) -> "TensoredOp": - """Appends I ^ num_qubits to ``oplist``. Choice of PauliOp as - identity is arbitrary and can be substituted for other PrimitiveOp identity. - - Returns: - TensoredOp expanded with identity operator. - """ - # pylint: disable=cyclic-import - from ..operator_globals import I - - return TensoredOp(self.oplist + [I ^ num_qubits], coeff=self.coeff) - - def tensor(self, other: OperatorBase) -> OperatorBase: - if isinstance(other, TensoredOp): - return TensoredOp(self.oplist + other.oplist, coeff=self.coeff * other.coeff) - return TensoredOp(self.oplist + [other], coeff=self.coeff) - - # TODO eval should partial trace the input into smaller StateFns each of size - # op.num_qubits for each op in oplist. Right now just works through matmul. - def eval( - self, front: Union[str, dict, np.ndarray, OperatorBase, Statevector] = None - ) -> Union[OperatorBase, complex]: - if self._is_empty(): - return 0.0 - return cast(Union[OperatorBase, complex], self.to_matrix_op().eval(front=front)) - - # Try collapsing list or trees of tensor products. - # TODO do this smarter - def reduce(self) -> OperatorBase: - reduced_ops = [op.reduce() for op in self.oplist] - if self._is_empty(): - return self.__class__([], coeff=self.coeff, abelian=self.abelian) - reduced_ops = reduce(lambda x, y: x.tensor(y), reduced_ops) * self.coeff - if isinstance(reduced_ops, ListOp) and len(reduced_ops.oplist) == 1: - return reduced_ops.oplist[0] - else: - return cast(OperatorBase, reduced_ops) - - def to_circuit(self) -> QuantumCircuit: - """Returns the quantum circuit, representing the tensored operator. - - Returns: - The circuit representation of the tensored operator. - - Raises: - OpflowError: for operators where a single underlying circuit can not be produced. - """ - circuit_op = self.to_circuit_op() - # pylint: disable=cyclic-import - from ..state_fns.circuit_state_fn import CircuitStateFn - from ..primitive_ops.primitive_op import PrimitiveOp - - if isinstance(circuit_op, (PrimitiveOp, CircuitStateFn)): - return circuit_op.to_circuit() - raise OpflowError( - "Conversion to_circuit supported only for operators, where a single " - "underlying circuit can be produced." - ) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) - - mat = self.coeff * reduce(np.kron, [np.asarray(op.to_matrix()) for op in self.oplist]) - return np.asarray(mat, dtype=complex) diff --git a/qiskit/opflow/mixins/__init__.py b/qiskit/opflow/mixins/__init__.py deleted file mode 100644 index 705400f844d5..000000000000 --- a/qiskit/opflow/mixins/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -OpFlow Mixins -""" - -from .star_algebra import StarAlgebraMixin -from .tensor import TensorMixin diff --git a/qiskit/opflow/mixins/star_algebra.py b/qiskit/opflow/mixins/star_algebra.py deleted file mode 100644 index 642086d5ba04..000000000000 --- a/qiskit/opflow/mixins/star_algebra.py +++ /dev/null @@ -1,132 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The star algebra mixin abstract base class.""" - -from abc import ABC, abstractmethod -from numbers import Integral - -from qiskit.quantum_info.operators.mixins import MultiplyMixin -from qiskit.utils.deprecation import deprecate_func - - -class StarAlgebraMixin(MultiplyMixin, ABC): - """Deprecated: The star algebra mixin class. - Star algebra is an algebra with an adjoint. - - This class overrides: - - ``*``, ``__mul__``, `__rmul__`, -> :meth:`mul` - - ``/``, ``__truediv__``, -> :meth:`mul` - - ``__neg__`` -> :meth:``mul` - - ``+``, ``__add__``, ``__radd__`` -> :meth:`add` - - ``-``, ``__sub__``, `__rsub__`, -> :meth:a`add` - - ``@``, ``__matmul__`` -> :meth:`compose` - - ``**``, ``__pow__`` -> :meth:`power` - - ``~``, ``__invert__`` -> :meth:`adjoint` - - The following abstract methods must be implemented by subclasses: - - :meth:`mul(self, other)` - - :meth:`add(self, other)` - - :meth:`compose(self, other)` - - :meth:`adjoint(self)` - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - pass - - # Scalar multiplication - - @abstractmethod - def mul(self, other: complex): - """Return scalar multiplication of self and other, overloaded by `*`.""" - - def __mul__(self, other: complex): - return self.mul(other) - - def _multiply(self, other: complex): - return self.mul(other) - - # Addition, substitution - - @abstractmethod - def add(self, other): - """Return Operator addition of self and other, overloaded by `+`.""" - - def __add__(self, other): - # Hack to be able to use sum(list_of_ops) nicely because - # sum adds 0 to the first element of the list. - if other == 0: - return self - - return self.add(other) - - def __radd__(self, other): - # Hack to be able to use sum(list_of_ops) nicely because - # sum adds 0 to the first element of the list. - if other == 0: - return self - return self.add(other) - - def __sub__(self, other): - return self.add(-other) - - def __rsub__(self, other): - return self.neg().add(other) - - # Operator multiplication - - @abstractmethod - def compose(self, other): - """Overloads the matrix multiplication operator `@` for self and other. - `Compose` computes operator composition between self and other (linear algebra-style: - A@B(x) = A(B(x))). - """ - - def power(self, exponent: int): - r"""Return Operator composed with self multiple times, overloaded by ``**``.""" - if not isinstance(exponent, Integral): - raise TypeError( - f"Unsupported operand type(s) for **: '{type(self).__name__}' and " - f"'{type(exponent).__name__}'" - ) - - if exponent < 1: - raise ValueError("The input `exponent` must be a positive integer.") - - res = self - for _ in range(1, exponent): - res = res.compose(self) - return res - - def __matmul__(self, other): - return self.compose(other) - - def __pow__(self, exponent: int): - return self.power(exponent) - - # Adjoint - - @abstractmethod - def adjoint(self): - """Returns the complex conjugate transpose (dagger) of self.adjoint - - Returns: - An operator equivalent to self's adjoint. - """ - - def __invert__(self): - """Overload unary `~` to return Operator adjoint.""" - return self.adjoint() diff --git a/qiskit/opflow/mixins/tensor.py b/qiskit/opflow/mixins/tensor.py deleted file mode 100644 index 3535db439f09..000000000000 --- a/qiskit/opflow/mixins/tensor.py +++ /dev/null @@ -1,57 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The tensor mixin abstract base class.""" - -from abc import ABC, abstractmethod -from numbers import Integral -from qiskit.utils.deprecation import deprecate_func - - -class TensorMixin(ABC): - """Deprecated: The mixin class for tensor operations. - - This class overrides: - - ``^``, ``__xor__``, `__rxor__` -> :meth:`tensor` between two operators and - :meth:`tensorpower` with integer. - The following abstract methods must be implemented by subclasses: - - :meth:``tensor(self, other)`` - - :meth:``tensorpower(self, other: int)`` - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - pass - - def __xor__(self, other): - if isinstance(other, Integral): - return self.tensorpower(other) - else: - return self.tensor(other) - - def __rxor__(self, other): - # a hack to make (I^0)^Z work as intended. - if other == 1: - return self - else: - return other.tensor(self) - - @abstractmethod - def tensor(self, other): - r"""Return tensor product between self and other, overloaded by ``^``.""" - - @abstractmethod - def tensorpower(self, other: int): - r"""Return tensor product with self multiple times, overloaded by ``^``.""" diff --git a/qiskit/opflow/operator_base.py b/qiskit/opflow/operator_base.py deleted file mode 100644 index e79741a5dac3..000000000000 --- a/qiskit/opflow/operator_base.py +++ /dev/null @@ -1,515 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""OperatorBase Class""" - -import itertools -import warnings -from abc import ABC, abstractmethod -from copy import deepcopy -from typing import Dict, List, Optional, Set, Tuple, Union, cast - -import numpy as np -from scipy.sparse import csr_matrix, spmatrix - -from qiskit.circuit import ParameterExpression, ParameterVector -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.mixins import StarAlgebraMixin, TensorMixin -from qiskit.quantum_info import Statevector -from qiskit.utils import algorithm_globals -from qiskit.utils.deprecation import deprecate_func - - -class OperatorBase(StarAlgebraMixin, TensorMixin, ABC): - """Deprecated: A base class for all Operators: PrimitiveOps, StateFns, ListOps, etc. Operators are - defined as functions which take one complex binary function to another. These complex binary - functions are represented by StateFns, which are themselves a special class of Operators - taking only the ``Zero`` StateFn to the complex binary function they represent. - - Operators can be used to construct complicated functions and computation, and serve as the - building blocks for algorithms. - - """ - - # Indentation used in string representation of list operators - # Can be changed to use another indentation than two whitespaces - INDENTATION = " " - - _count = itertools.count() - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self) -> None: - super().__init__() - self._instance_id = next(self._count) - - @property - @abstractmethod - def settings(self) -> Dict: - """Return settings of this object in a dictionary. - - You can, for example, use this ``settings`` dictionary to serialize the - object in JSON format, if the JSON encoder you use supports all types in - the dictionary. - - Returns: - Object settings in a dictionary. - """ - raise NotImplementedError - - @property - def instance_id(self) -> int: - """Return the unique instance id.""" - return self._instance_id - - @property - @abstractmethod - def num_qubits(self) -> int: - r"""The number of qubits over which the Operator is defined. If - ``op.num_qubits == 5``, then ``op.eval('1' * 5)`` will be valid, but - ``op.eval('11')`` will not. - - Returns: - The number of qubits accepted by the Operator's underlying function. - """ - raise NotImplementedError - - @abstractmethod - def primitive_strings(self) -> Set[str]: - r"""Return a set of strings describing the primitives contained in the Operator. For - example, ``{'QuantumCircuit', 'Pauli'}``. For hierarchical Operators, such as ``ListOps``, - this can help illuminate the primitives represented in the various recursive levels, - and therefore which conversions can be applied. - - Returns: - A set of strings describing the primitives contained within the Operator. - """ - raise NotImplementedError - - @abstractmethod - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, "OperatorBase", Statevector] - ] = None, - ) -> Union["OperatorBase", complex]: - r""" - Evaluate the Operator's underlying function, either on a binary string or another Operator. - A square binary Operator can be defined as a function taking a binary function to another - binary function. This method returns the value of that function for a given StateFn or - binary string. For example, ``op.eval('0110').eval('1110')`` can be seen as querying the - Operator's matrix representation by row 6 and column 14, and will return the complex - value at those "indices." Similarly for a StateFn, ``op.eval('1011')`` will return the - complex value at row 11 of the vector representation of the StateFn, as all StateFns are - defined to be evaluated from Zero implicitly (i.e. it is as if ``.eval('0000')`` is already - called implicitly to always "indexing" from column 0). - - If ``front`` is None, the matrix-representation of the operator is returned. - - Args: - front: The bitstring, dict of bitstrings (with values being coefficients), or - StateFn to evaluated by the Operator's underlying function, or None. - - Returns: - The output of the Operator's evaluation function. If self is a ``StateFn``, the result - is a float or complex. If self is an Operator (``PrimitiveOp, ComposedOp, SummedOp, - EvolvedOp,`` etc.), the result is a StateFn. - If ``front`` is None, the matrix-representation of the operator is returned, which - is a ``MatrixOp`` for the operators and a ``VectorStateFn`` for state-functions. - If either self or front contain proper - ``ListOps`` (not ListOp subclasses), the result is an n-dimensional list of complex - or StateFn results, resulting from the recursive evaluation by each OperatorBase - in the ListOps. - - """ - raise NotImplementedError - - @abstractmethod - def reduce(self): - r"""Try collapsing the Operator structure, usually after some type of conversion, - e.g. trying to add Operators in a SummedOp or delete needless IGates in a CircuitOp. - If no reduction is available, just returns self. - - Returns: - The reduced ``OperatorBase``. - """ - raise NotImplementedError - - @abstractmethod - def to_matrix(self, massive: bool = False) -> np.ndarray: - r"""Return NumPy representation of the Operator. Represents the evaluation of - the Operator's underlying function on every combination of basis binary strings. - Warn if more than 16 qubits to force having to set ``massive=True`` if such a - large vector is desired. - - Returns: - The NumPy ``ndarray`` equivalent to this Operator. - """ - raise NotImplementedError - - @abstractmethod - def to_matrix_op(self, massive: bool = False) -> "OperatorBase": - """Returns a ``MatrixOp`` equivalent to this Operator.""" - raise NotImplementedError - - @abstractmethod - def to_circuit_op(self) -> "OperatorBase": - """Returns a ``CircuitOp`` equivalent to this Operator.""" - raise NotImplementedError - - def to_spmatrix(self) -> spmatrix: - r"""Return SciPy sparse matrix representation of the Operator. Represents the evaluation of - the Operator's underlying function on every combination of basis binary strings. - - Returns: - The SciPy ``spmatrix`` equivalent to this Operator. - """ - return csr_matrix(self.to_matrix()) - - def is_hermitian(self) -> bool: - """Return True if the operator is hermitian. - - Returns: Boolean value - """ - return (self.to_spmatrix() != self.to_spmatrix().getH()).nnz == 0 - - @staticmethod - def _indent(lines: str, indentation: str = INDENTATION) -> str: - """Indented representation to allow pretty representation of nested operators.""" - indented_str = indentation + lines.replace("\n", f"\n{indentation}") - if indented_str.endswith(f"\n{indentation}"): - indented_str = indented_str[: -len(indentation)] - return indented_str - - # Addition / Subtraction - - @abstractmethod - def add(self, other: "OperatorBase") -> "OperatorBase": - r"""Return Operator addition of self and other, overloaded by ``+``. - - Args: - other: An ``OperatorBase`` with the same number of qubits as self, and in the same - 'Operator', 'State function', or 'Measurement' category as self (i.e. the same type - of underlying function). - - Returns: - An ``OperatorBase`` equivalent to the sum of self and other. - """ - raise NotImplementedError - - # Negation - - def neg(self) -> "OperatorBase": - r"""Return the Operator's negation, effectively just multiplying by -1.0, - overloaded by ``-``. - - Returns: - An ``OperatorBase`` equivalent to the negation of self. - """ - return self.mul(-1.0) - - # Adjoint - - @abstractmethod - def adjoint(self) -> "OperatorBase": - r"""Return a new Operator equal to the Operator's adjoint (conjugate transpose), - overloaded by ``~``. For StateFns, this also turns the StateFn into a measurement. - - Returns: - An ``OperatorBase`` equivalent to the adjoint of self. - """ - raise NotImplementedError - - # Equality - - def __eq__(self, other: object) -> bool: - r"""Overload ``==`` operation to evaluate equality between Operators. - - Args: - other: The ``OperatorBase`` to compare to self. - - Returns: - A bool equal to the equality of self and other. - """ - if not isinstance(other, OperatorBase): - return NotImplemented - return self.equals(cast(OperatorBase, other)) - - @abstractmethod - def equals(self, other: "OperatorBase") -> bool: - r""" - Evaluate Equality between Operators, overloaded by ``==``. Only returns True if self and - other are of the same representation (e.g. a DictStateFn and CircuitStateFn will never be - equal, even if their vector representations are equal), their underlying primitives are - equal (this means for ListOps, OperatorStateFns, or EvolvedOps the equality is evaluated - recursively downwards), and their coefficients are equal. - - Args: - other: The ``OperatorBase`` to compare to self. - - Returns: - A bool equal to the equality of self and other. - - """ - raise NotImplementedError - - # Scalar Multiplication - - @abstractmethod - def mul(self, scalar: Union[complex, ParameterExpression]) -> "OperatorBase": - r""" - Returns the scalar multiplication of the Operator, overloaded by ``*``, including - support for Terra's ``Parameters``, which can be bound to values later (via - ``bind_parameters``). - - Args: - scalar: The real or complex scalar by which to multiply the Operator, - or the ``ParameterExpression`` to serve as a placeholder for a scalar factor. - - Returns: - An ``OperatorBase`` equivalent to product of self and scalar. - """ - raise NotImplementedError - - @abstractmethod - def tensor(self, other: "OperatorBase") -> "OperatorBase": - r"""Return tensor product between self and other, overloaded by ``^``. - Note: You must be conscious of Qiskit's big-endian bit printing convention. - Meaning, X.tensor(Y) produces an X on qubit 0 and an Y on qubit 1, or X⨂Y, - but would produce a QuantumCircuit which looks like - - -[Y]- - -[X]- - - Because Terra prints circuits and results with qubit 0 at the end of the string - or circuit. - - Args: - other: The ``OperatorBase`` to tensor product with self. - - Returns: - An ``OperatorBase`` equivalent to the tensor product of self and other. - """ - raise NotImplementedError - - @abstractmethod - def tensorpower(self, other: int) -> Union["OperatorBase", int]: - r"""Return tensor product with self multiple times, overloaded by ``^``. - - Args: - other: The int number of times to tensor product self with itself via ``tensorpower``. - - Returns: - An ``OperatorBase`` equivalent to the tensorpower of self by other. - """ - raise NotImplementedError - - @property - @abstractmethod - def parameters(self): - r"""Return a set of Parameter objects contained in the Operator.""" - raise NotImplementedError - - # Utility functions for parameter binding - - @abstractmethod - def assign_parameters( - self, - param_dict: Dict[ - ParameterExpression, - Union[complex, ParameterExpression, List[Union[complex, ParameterExpression]]], - ], - ) -> "OperatorBase": - """Binds scalar values to any Terra ``Parameters`` in the coefficients or primitives of - the Operator, or substitutes one ``Parameter`` for another. This method differs from - Terra's ``assign_parameters`` in that it also supports lists of values to assign for a - give ``Parameter``, in which case self will be copied for each parameterization in the - binding list(s), and all the copies will be returned in an ``OpList``. If lists of - parameterizations are used, every ``Parameter`` in the param_dict must have the same - length list of parameterizations. - - Args: - param_dict: The dictionary of ``Parameters`` to replace, and values or lists of - values by which to replace them. - - Returns: - The ``OperatorBase`` with the ``Parameters`` in self replaced by the - values or ``Parameters`` in param_dict. If param_dict contains parameterization lists, - this ``OperatorBase`` is an ``OpList``. - """ - raise NotImplementedError - - @abstractmethod - def _expand_dim(self, num_qubits: int) -> "OperatorBase": - """Expands the operator with identity operator of dimension 2**num_qubits. - - Returns: - Operator corresponding to self.tensor(identity_operator), where dimension of identity - operator is 2 ** num_qubits. - """ - raise NotImplementedError - - @abstractmethod - def permute(self, permutation: List[int]) -> "OperatorBase": - """Permutes the qubits of the operator. - - Args: - permutation: A list defining where each qubit should be permuted. The qubit at index - j should be permuted to position permutation[j]. - - Returns: - A new OperatorBase containing the permuted operator. - - Raises: - OpflowError: if indices do not define a new index for each qubit. - """ - raise NotImplementedError - - def bind_parameters( - self, - param_dict: Dict[ - ParameterExpression, - Union[complex, ParameterExpression, List[Union[complex, ParameterExpression]]], - ], - ) -> "OperatorBase": - r""" - Same as assign_parameters, but maintained for consistency with QuantumCircuit in - Terra (which has both assign_parameters and bind_parameters). - """ - return self.assign_parameters(param_dict) - - # Mostly copied from terra, but with list unrolling added: - @staticmethod - def _unroll_param_dict( - value_dict: Dict[Union[ParameterExpression, ParameterVector], Union[complex, List[complex]]] - ) -> Union[Dict[ParameterExpression, complex], List[Dict[ParameterExpression, complex]]]: - """Unrolls the ParameterVectors in a param_dict into separate Parameters, and unrolls - parameterization value lists into separate param_dicts without list nesting.""" - unrolled_value_dict = {} - for (param, value) in value_dict.items(): - if isinstance(param, ParameterExpression): - unrolled_value_dict[param] = value - if isinstance(param, ParameterVector) and isinstance(value, (list, np.ndarray)): - if not len(param) == len(value): - raise ValueError( - "ParameterVector {} has length {}, which differs from value list {} of " - "len {}".format(param, len(param), value, len(value)) - ) - unrolled_value_dict.update(zip(param, value)) - if isinstance(list(unrolled_value_dict.values())[0], list): - # check that all are same length - unrolled_value_dict_list = [] - try: - for i in range(len(list(unrolled_value_dict.values())[0])): # type: ignore - unrolled_value_dict_list.append( - OperatorBase._get_param_dict_for_index( - unrolled_value_dict, i # type: ignore - ) - ) - return unrolled_value_dict_list - except IndexError as ex: - raise OpflowError("Parameter binding lists must all be the same length.") from ex - return unrolled_value_dict # type: ignore - - @staticmethod - def _get_param_dict_for_index(unrolled_dict: Dict[ParameterExpression, List[complex]], i: int): - """Gets a single non-list-nested param_dict for a given list index from a nested one.""" - return {k: v[i] for (k, v) in unrolled_dict.items()} - - def _expand_shorter_operator_and_permute( - self, other: "OperatorBase", permutation: Optional[List[int]] = None - ) -> Tuple["OperatorBase", "OperatorBase"]: - if permutation is not None: - other = other.permute(permutation) - new_self = self - if not self.num_qubits == other.num_qubits: - # pylint: disable=cyclic-import - from .operator_globals import Zero - - if other == Zero: - # Zero is special - we'll expand it to the correct qubit number. - other = Zero.__class__("0" * self.num_qubits) - elif other.num_qubits < self.num_qubits: - other = other._expand_dim(self.num_qubits - other.num_qubits) - elif other.num_qubits > self.num_qubits: - new_self = self._expand_dim(other.num_qubits - self.num_qubits) - return new_self, other - - def copy(self) -> "OperatorBase": - """Return a deep copy of the Operator.""" - return deepcopy(self) - - # Composition - - @abstractmethod - def compose( - self, other: "OperatorBase", permutation: Optional[List[int]] = None, front: bool = False - ) -> "OperatorBase": - r"""Return Operator Composition between self and other (linear algebra-style: - A@B(x) = A(B(x))), overloaded by ``@``. - - Note: You must be conscious of Quantum Circuit vs. Linear Algebra ordering - conventions. Meaning, X.compose(Y) - produces an X∘Y on qubit 0, but would produce a QuantumCircuit which looks like - - -[Y]-[X]- - - Because Terra prints circuits with the initial state at the left side of the circuit. - - Args: - other: The ``OperatorBase`` with which to compose self. - permutation: ``List[int]`` which defines permutation on other operator. - front: If front==True, return ``other.compose(self)``. - - Returns: - An ``OperatorBase`` equivalent to the function composition of self and other. - """ - raise NotImplementedError - - @staticmethod - def _check_massive(method: str, matrix: bool, num_qubits: int, massive: bool) -> None: - """ - Checks if matrix or vector generated will be too large. - - Args: - method: Name of the calling method - matrix: True if object is matrix, otherwise vector - num_qubits: number of qubits - massive: True if it is ok to proceed with large matrix - - Raises: - ValueError: Massive is False and number of qubits is greater than 16 - """ - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - if num_qubits > 16 and not massive and not algorithm_globals.massive: - dim = 2**num_qubits - if matrix: - obj_type = "matrix" - dimensions = f"{dim}x{dim}" - else: - obj_type = "vector" - dimensions = f"{dim}" - raise ValueError( - f"'{method}' will return an exponentially large {obj_type}, " - f"in this case '{dimensions}' elements. " - "Set algorithm_globals.massive=True or the method argument massive=True " - "if you want to proceed." - ) - - # Printing - - @abstractmethod - def __str__(self) -> str: - raise NotImplementedError diff --git a/qiskit/opflow/operator_globals.py b/qiskit/opflow/operator_globals.py deleted file mode 100644 index ac0c624c7287..000000000000 --- a/qiskit/opflow/operator_globals.py +++ /dev/null @@ -1,79 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Operator Globals -""" - -import warnings - -from qiskit.quantum_info import Pauli -from qiskit.circuit.library import CXGate, SGate, TGate, HGate, SwapGate, CZGate - -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.primitive_ops.circuit_op import CircuitOp -from qiskit.opflow.state_fns.dict_state_fn import DictStateFn -from qiskit.utils.deprecation import deprecate_func - -# Digits of precision when returning values from eval functions. Without rounding, 1e-17 or 1e-32 -# values often show up in place of 0, etc. -# Note: care needs to be taken in rounding otherwise some behavior may not be as expected. E.g -# evolution is used in QAOA variational form and difference when optimizing may be small - round -# the outcome too much and a small difference may become none and the optimizer gets stuck where -# otherwise it would not. -EVAL_SIG_DIGITS = 18 - -# Immutable convenience objects - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", -) -def make_immutable(obj): - r"""Deprecate\: Delete the __setattr__ property to make the object mostly immutable.""" - - # TODO figure out how to get correct error message - # def throw_immutability_exception(self, *args): - # raise OpflowError('Operator convenience globals are immutable.') - - obj.__setattr__ = None - return obj - - -# All the deprecation warnings triggered by these object creations correctly blame `qiskit.opflow` -# and so are not shown to users by default. However, since they are eagerly triggered at `import -# qiskit.opflow`, they obscure the one "true" warning of the import when downstream testing code is -# running with all warnings showing. The true warning that really needs attention becomes easy to -# overlook because there's so many that the downstream code didn't explicitly call. -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"qiskit\.opflow\.") - - # 1-Qubit Paulis - X = make_immutable(PauliOp(Pauli("X"))) - Y = make_immutable(PauliOp(Pauli("Y"))) - Z = make_immutable(PauliOp(Pauli("Z"))) - I = make_immutable(PauliOp(Pauli("I"))) - - # Clifford+T, and some other common non-parameterized gates - CX = make_immutable(CircuitOp(CXGate())) - S = make_immutable(CircuitOp(SGate())) - H = make_immutable(CircuitOp(HGate())) - T = make_immutable(CircuitOp(TGate())) - Swap = make_immutable(CircuitOp(SwapGate())) - CZ = make_immutable(CircuitOp(CZGate())) - - # 1-Qubit states - Zero = make_immutable(DictStateFn("0")) - One = make_immutable(DictStateFn("1")) - Plus = make_immutable(H.compose(Zero)) - Minus = make_immutable(H.compose(X).compose(Zero)) diff --git a/qiskit/opflow/primitive_ops/__init__.py b/qiskit/opflow/primitive_ops/__init__.py deleted file mode 100644 index 7e5cc72fad6b..000000000000 --- a/qiskit/opflow/primitive_ops/__init__.py +++ /dev/null @@ -1,79 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Primitive Operators (:mod:`qiskit.opflow.primitive_ops`) -======================================================== - -.. currentmodule:: qiskit.opflow.primitive_ops - -.. deprecated:: 0.24.0 - - The :mod:`qiskit.opflow` module is deprecated and will be removed no earlier - than 3 months after the release date. For code migration guidelines, - visit https://qisk.it/opflow_migration. - -Operators are defined to be functions which take State functions to State functions. - -PrimitiveOps are the classes for representing basic Operators, backed by computational -Operator primitives from Terra. These classes (and inheritors) primarily serve to allow the -underlying primitives to "flow" - i.e. interoperability and adherence to the Operator -formalism - while the core computational logic mostly remains in the underlying primitives. -For example, we would not produce an interface in Terra in which -``QuantumCircuit1 + QuantumCircuit2`` equaled the Operator sum of the circuit -unitaries, rather than simply appending the circuits. However, within the Operator -flow summing the unitaries is the expected behavior. - -Note: - All mathematical methods are not in-place, meaning that they return a - new object, but the underlying primitives are not copied. - -Primitive Operators -------------------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - PrimitiveOp - CircuitOp - MatrixOp - PauliOp - PauliSumOp - TaperedPauliSumOp - -Symmetries ----------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - Z2Symmetries -""" - -from .primitive_op import PrimitiveOp -from .pauli_op import PauliOp -from .matrix_op import MatrixOp -from .circuit_op import CircuitOp -from .pauli_sum_op import PauliSumOp -from .tapered_pauli_sum_op import TaperedPauliSumOp, Z2Symmetries - -__all__ = [ - "PrimitiveOp", - "PauliOp", - "MatrixOp", - "CircuitOp", - "PauliSumOp", - "TaperedPauliSumOp", - "Z2Symmetries", -] diff --git a/qiskit/opflow/primitive_ops/circuit_op.py b/qiskit/opflow/primitive_ops/circuit_op.py deleted file mode 100644 index 1a6104a4cac3..000000000000 --- a/qiskit/opflow/primitive_ops/circuit_op.py +++ /dev/null @@ -1,251 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""CircuitOp Class""" - -from typing import Dict, List, Optional, Set, Union, cast -import numpy as np - -import qiskit -from qiskit import QuantumCircuit -from qiskit.circuit import Instruction, ParameterExpression -from qiskit.circuit.library import IGate -from qiskit.opflow.list_ops.tensored_op import TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.quantum_info import Statevector -from qiskit.utils.deprecation import deprecate_func - - -class CircuitOp(PrimitiveOp): - """Deprecated: Class for Operators backed by Terra's ``QuantumCircuit`` module.""" - - primitive: QuantumCircuit - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: Union[Instruction, QuantumCircuit], - coeff: Union[complex, ParameterExpression] = 1.0, - ) -> None: - """ - Args: - primitive: The QuantumCircuit which defines the - behavior of the underlying function. - coeff: A coefficient multiplying the primitive - - Raises: - TypeError: Unsupported primitive, or primitive has ClassicalRegisters. - """ - if isinstance(primitive, Instruction): - qc = QuantumCircuit(primitive.num_qubits) - qc.append(primitive, qargs=range(primitive.num_qubits)) - primitive = qc - - if not isinstance(primitive, QuantumCircuit): - raise TypeError( - "CircuitOp can only be instantiated with " - "QuantumCircuit, not {}".format(type(primitive)) - ) - - if len(primitive.clbits) != 0: - raise TypeError("CircuitOp does not support QuantumCircuits with ClassicalRegisters.") - - super().__init__(primitive, coeff) - self._coeff = coeff - - def primitive_strings(self) -> Set[str]: - return {"QuantumCircuit"} - - @property - def num_qubits(self) -> int: - return self.primitive.num_qubits - - def add(self, other: OperatorBase) -> OperatorBase: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over operators with different numbers of qubits, {} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - if isinstance(other, CircuitOp) and self.primitive == other.primitive: - return CircuitOp(self.primitive, coeff=self.coeff + other.coeff) - - # Covers all else. - # pylint: disable=cyclic-import - from ..list_ops.summed_op import SummedOp - - return SummedOp([self, other]) - - def adjoint(self) -> "CircuitOp": - return CircuitOp(self.primitive.inverse(), coeff=self.coeff.conjugate()) - - def equals(self, other: OperatorBase) -> bool: - if not isinstance(other, CircuitOp) or not self.coeff == other.coeff: - return False - - return self.primitive == other.primitive - - def tensor(self, other: OperatorBase) -> Union["CircuitOp", TensoredOp]: - # pylint: disable=cyclic-import - from .pauli_op import PauliOp - from .matrix_op import MatrixOp - - if isinstance(other, (PauliOp, CircuitOp, MatrixOp)): - other = other.to_circuit_op() - - if isinstance(other, CircuitOp): - new_qc = QuantumCircuit(self.num_qubits + other.num_qubits) - # NOTE!!! REVERSING QISKIT ENDIANNESS HERE - new_qc.append( - other.to_instruction(), qargs=new_qc.qubits[0 : other.primitive.num_qubits] - ) - new_qc.append(self.to_instruction(), qargs=new_qc.qubits[other.primitive.num_qubits :]) - new_qc = new_qc.decompose() - return CircuitOp(new_qc, coeff=self.coeff * other.coeff) - - return TensoredOp([self, other]) - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - new_self = cast(CircuitOp, new_self) - - if front: - return other.compose(new_self) - # pylint: disable=cyclic-import - from ..operator_globals import Zero - from ..state_fns import CircuitStateFn - from .pauli_op import PauliOp - from .matrix_op import MatrixOp - - if other == Zero ^ new_self.num_qubits: - return CircuitStateFn(new_self.primitive, coeff=new_self.coeff) - - if isinstance(other, (PauliOp, CircuitOp, MatrixOp)): - other = other.to_circuit_op() - - if isinstance(other, (CircuitOp, CircuitStateFn)): - new_qc = other.primitive.compose(new_self.primitive) - if isinstance(other, CircuitStateFn): - return CircuitStateFn( - new_qc, is_measurement=other.is_measurement, coeff=new_self.coeff * other.coeff - ) - else: - return CircuitOp(new_qc, coeff=new_self.coeff * other.coeff) - - return super(CircuitOp, new_self).compose(other) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) - unitary = qiskit.quantum_info.Operator(self.to_circuit()).data - return unitary * self.coeff - - def __str__(self) -> str: - qc = self.to_circuit() - prim_str = str(qc.draw(output="text")) - if self.coeff == 1.0: - return prim_str - else: - return f"{self.coeff} * {prim_str}" - - def assign_parameters(self, param_dict: dict) -> OperatorBase: - param_value = self.coeff - qc = self.primitive - if isinstance(self.coeff, ParameterExpression) or self.primitive.parameters: - unrolled_dict = self._unroll_param_dict(param_dict) - if isinstance(unrolled_dict, list): - from ..list_ops.list_op import ListOp - - return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) - if isinstance(self.coeff, ParameterExpression) and self.coeff.parameters <= set( - unrolled_dict.keys() - ): - param_instersection = set(unrolled_dict.keys()) & self.coeff.parameters - binds = {param: unrolled_dict[param] for param in param_instersection} - param_value = float(self.coeff.bind(binds)) - # & is set intersection, check if any parameters in unrolled are present in circuit - # This is different from bind_parameters in Terra because they check for set equality - if set(unrolled_dict.keys()) & self.primitive.parameters: - # Only bind the params found in the circuit - param_instersection = set(unrolled_dict.keys()) & self.primitive.parameters - binds = {param: unrolled_dict[param] for param in param_instersection} - qc = self.to_circuit().assign_parameters(binds) - return self.__class__(qc, coeff=param_value) - - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - from ..state_fns import CircuitStateFn - from ..list_ops import ListOp - from .pauli_op import PauliOp - from .matrix_op import MatrixOp - - if isinstance(front, ListOp) and front.distributive: - return front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - # Composable with circuit - if isinstance(front, (PauliOp, CircuitOp, MatrixOp, CircuitStateFn)): - return self.compose(front) - - return self.to_matrix_op().eval(front) - - def to_circuit(self) -> QuantumCircuit: - return self.primitive - - def to_circuit_op(self) -> "CircuitOp": - return self - - def to_instruction(self) -> Instruction: - return self.primitive.to_instruction() - - # Warning - modifying immutable object!! - def reduce(self) -> OperatorBase: - if self.primitive.data is not None: - # Need to do this from the end because we're deleting items! - for i in reversed(range(len(self.primitive.data))): - gate = self.primitive.data[i].operation - # Check if Identity or empty instruction (need to check that type is exactly - # Instruction because some gates have lazy gate.definition population) - # pylint: disable=unidiomatic-typecheck - if isinstance(gate, IGate) or ( - type(gate) == Instruction and gate.definition.data == [] - ): - del self.primitive.data[i] - return self - - def _expand_dim(self, num_qubits: int) -> "CircuitOp": - return self.permute(list(range(num_qubits, num_qubits + self.num_qubits))) - - def permute(self, permutation: List[int]) -> "CircuitOp": - r""" - Permute the qubits of the circuit. - - Args: - permutation: A list defining where each qubit should be permuted. The qubit at index - j of the circuit should be permuted to position permutation[j]. - - Returns: - A new CircuitOp containing the permuted circuit. - """ - new_qc = QuantumCircuit(max(permutation) + 1).compose(self.primitive, qubits=permutation) - return CircuitOp(new_qc, coeff=self.coeff) diff --git a/qiskit/opflow/primitive_ops/matrix_op.py b/qiskit/opflow/primitive_ops/matrix_op.py deleted file mode 100644 index 5afe0ac54570..000000000000 --- a/qiskit/opflow/primitive_ops/matrix_op.py +++ /dev/null @@ -1,236 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""MatrixOp Class""" - -from typing import Dict, List, Optional, Set, Union, cast, get_type_hints -import numpy as np -from scipy.sparse import spmatrix - -from qiskit import QuantumCircuit -from qiskit.circuit import Instruction, ParameterExpression -from qiskit.circuit.library.hamiltonian_gate import HamiltonianGate -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.list_ops.tensored_op import TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.circuit_op import CircuitOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.quantum_info import Operator, Statevector -from qiskit.utils import arithmetic -from qiskit.utils.deprecation import deprecate_func - - -class MatrixOp(PrimitiveOp): - """Deprecated: Class for Operators represented by matrices, - backed by Terra's ``Operator`` module.""" - - primitive: Operator - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: Union[list, np.ndarray, spmatrix, Operator], - coeff: Union[complex, ParameterExpression] = 1.0, - ) -> None: - """ - Args: - primitive: The matrix-like object which defines the behavior of the underlying function. - coeff: A coefficient multiplying the primitive - - Raises: - TypeError: invalid parameters. - ValueError: invalid parameters. - """ - primitive_orig = primitive - if isinstance(primitive, spmatrix): - primitive = primitive.toarray() - - if isinstance(primitive, (list, np.ndarray)): - primitive = Operator(primitive) - - if not isinstance(primitive, Operator): - type_hints = get_type_hints(MatrixOp.__init__).get("primitive") - valid_cls = [cls.__name__ for cls in type_hints.__args__] - raise TypeError( - f"MatrixOp can only be instantiated with {valid_cls}, " - f"not '{primitive_orig.__class__.__name__}'" - ) - - if primitive.input_dims() != primitive.output_dims(): - raise ValueError("Cannot handle non-square matrices yet.") - - super().__init__(primitive, coeff=coeff) - - def primitive_strings(self) -> Set[str]: - return {"Matrix"} - - @property - def num_qubits(self) -> int: - return len(self.primitive.input_dims()) - - def add(self, other: OperatorBase) -> Union["MatrixOp", SummedOp]: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over operators with different numbers of qubits, {} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - if isinstance(other, MatrixOp) and self.primitive == other.primitive: - return MatrixOp(self.primitive, coeff=self.coeff + other.coeff) - - # Terra's Operator cannot handle ParameterExpressions - if ( - isinstance(other, MatrixOp) - and not isinstance(self.coeff, ParameterExpression) - and not isinstance(other.coeff, ParameterExpression) - ): - return MatrixOp((self.coeff * self.primitive) + (other.coeff * other.primitive)) - - # Covers Paulis, Circuits, and all else. - return SummedOp([self, other]) - - def adjoint(self) -> "MatrixOp": - return MatrixOp(self.primitive.adjoint(), coeff=self.coeff.conjugate()) - - def equals(self, other: OperatorBase) -> bool: - if not isinstance(other, MatrixOp): - return False - if isinstance(self.coeff, ParameterExpression) ^ isinstance( - other.coeff, ParameterExpression - ): - return False - if isinstance(self.coeff, ParameterExpression) and isinstance( - other.coeff, ParameterExpression - ): - return self.coeff == other.coeff and self.primitive == other.primitive - return self.coeff * self.primitive == other.coeff * other.primitive - - def _expand_dim(self, num_qubits: int) -> "MatrixOp": - identity = np.identity(2**num_qubits, dtype=complex) - return MatrixOp(self.primitive.tensor(Operator(identity)), coeff=self.coeff) - - def tensor(self, other: OperatorBase) -> Union["MatrixOp", TensoredOp]: - if isinstance(other, MatrixOp): - return MatrixOp(self.primitive.tensor(other.primitive), coeff=self.coeff * other.coeff) - - return TensoredOp([self, other]) - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - new_self = cast(MatrixOp, new_self) - - if front: - return other.compose(new_self) - if isinstance(other, MatrixOp): - return MatrixOp( - new_self.primitive.compose(other.primitive, front=True), - coeff=new_self.coeff * other.coeff, - ) - - return super(MatrixOp, new_self).compose(other) - - def permute(self, permutation: Optional[List[int]] = None) -> OperatorBase: - """Creates a new MatrixOp that acts on the permuted qubits. - - Args: - permutation: A list defining where each qubit should be permuted. The qubit at index - j should be permuted to position permutation[j]. - - Returns: - A new MatrixOp representing the permuted operator. - - Raises: - OpflowError: if indices do not define a new index for each qubit. - """ - new_self = self - new_matrix_size = max(permutation) + 1 - - if self.num_qubits != len(permutation): - raise OpflowError("New index must be defined for each qubit of the operator.") - if self.num_qubits < new_matrix_size: - # pad the operator with identities - new_self = self._expand_dim(new_matrix_size - self.num_qubits) - qc = QuantumCircuit(new_matrix_size) - - # extend the indices to match the size of the new matrix - permutation = ( - list(filter(lambda x: x not in permutation, range(new_matrix_size))) + permutation - ) - - # decompose permutation into sequence of transpositions - transpositions = arithmetic.transpositions(permutation) - for trans in transpositions: - qc.swap(trans[0], trans[1]) - matrix = CircuitOp(qc).to_matrix() - return MatrixOp(matrix.transpose()) @ new_self @ MatrixOp(matrix) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - return self.primitive.data * self.coeff - - def __str__(self) -> str: - prim_str = str(self.primitive) - if self.coeff == 1.0: - return prim_str - else: - return f"{self.coeff} * {prim_str}" - - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - # For other ops' eval we return self.to_matrix_op() here, but that's unnecessary here. - if front is None: - return self - - # pylint: disable=cyclic-import - from ..list_ops import ListOp - from ..state_fns import StateFn, VectorStateFn, OperatorStateFn - - new_front = None - - # For now, always do this. If it's not performant, we can be more granular. - if not isinstance(front, OperatorBase): - front = StateFn(front, is_measurement=False) - - if isinstance(front, ListOp) and front.distributive: - new_front = front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - elif isinstance(front, OperatorStateFn): - new_front = OperatorStateFn(self.adjoint().compose(front.to_matrix_op()).compose(self)) - - elif isinstance(front, OperatorBase): - new_front = VectorStateFn(self.to_matrix() @ front.to_matrix()) - - return new_front - - def exp_i(self) -> OperatorBase: - """Return a ``CircuitOp`` equivalent to e^-iH for this operator H""" - return CircuitOp(HamiltonianGate(self.primitive, time=self.coeff)) - - # Op Conversions - - def to_matrix_op(self, massive: bool = False) -> "MatrixOp": - return self - - def to_instruction(self) -> Instruction: - return (self.coeff * self.primitive).to_instruction() diff --git a/qiskit/opflow/primitive_ops/pauli_op.py b/qiskit/opflow/primitive_ops/pauli_op.py deleted file mode 100644 index 623608754671..000000000000 --- a/qiskit/opflow/primitive_ops/pauli_op.py +++ /dev/null @@ -1,355 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""PauliOp Class""" - -from math import pi -from typing import Dict, List, Optional, Set, Union, cast -import numpy as np -from scipy.sparse import spmatrix - -from qiskit import QuantumCircuit -from qiskit.circuit import Instruction, ParameterExpression -from qiskit.circuit.library import RXGate, RYGate, RZGate, XGate, YGate, ZGate -from qiskit.circuit.library.generalized_gates import PauliGate -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.list_ops.tensored_op import TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.quantum_info import Pauli, SparsePauliOp, Statevector -from qiskit.utils.deprecation import deprecate_func - - -class PauliOp(PrimitiveOp): - """Deprecated: Class for Operators backed by Terra's ``Pauli`` module.""" - - primitive: Pauli - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__(self, primitive: Pauli, coeff: Union[complex, ParameterExpression] = 1.0) -> None: - """ - Args: - primitive: The Pauli which defines the behavior of the underlying function. - coeff: A coefficient multiplying the primitive. - - Raises: - TypeError: invalid parameters. - """ - if not isinstance(primitive, Pauli): - raise TypeError(f"PauliOp can only be instantiated with Paulis, not {type(primitive)}") - - super().__init__(primitive, coeff=coeff) - - def primitive_strings(self) -> Set[str]: - return {"Pauli"} - - @property - def num_qubits(self) -> int: - return len(self.primitive) - - def add(self, other: OperatorBase) -> OperatorBase: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over operators with different numbers of qubits, {} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - if isinstance(other, PauliOp) and self.primitive == other.primitive: - return PauliOp(self.primitive, coeff=self.coeff + other.coeff) - - # pylint: disable=cyclic-import - from .pauli_sum_op import PauliSumOp - - if ( - isinstance(other, PauliOp) - and isinstance(self.coeff, (int, float, complex)) - and isinstance(other.coeff, (int, float, complex)) - ): - return PauliSumOp( - SparsePauliOp(self.primitive, coeffs=[self.coeff]) - + SparsePauliOp(other.primitive, coeffs=[other.coeff]) - ) - - if isinstance(other, PauliSumOp) and isinstance(self.coeff, (int, float, complex)): - return PauliSumOp(SparsePauliOp(self.primitive, coeffs=[self.coeff])) + other - - return SummedOp([self, other]) - - def adjoint(self) -> "PauliOp": - return PauliOp(self.primitive.adjoint(), coeff=self.coeff.conjugate()) - - def equals(self, other: OperatorBase) -> bool: - if isinstance(other, PauliOp) and self.coeff == other.coeff: - return self.primitive == other.primitive - - # pylint: disable=cyclic-import - from .pauli_sum_op import PauliSumOp - - if isinstance(other, PauliSumOp): - return other == self - - return False - - def _expand_dim(self, num_qubits: int) -> "PauliOp": - return PauliOp(Pauli("I" * num_qubits).expand(self.primitive), coeff=self.coeff) - - def tensor(self, other: OperatorBase) -> OperatorBase: - # Both Paulis - if isinstance(other, PauliOp): - return PauliOp(self.primitive.tensor(other.primitive), coeff=self.coeff * other.coeff) - - # pylint: disable=cyclic-import - from .pauli_sum_op import PauliSumOp - - if isinstance(other, PauliSumOp): - new_primitive = SparsePauliOp(self.primitive).tensor(other.primitive) - return PauliSumOp(new_primitive, coeff=self.coeff * other.coeff) - - from .circuit_op import CircuitOp - - if isinstance(other, CircuitOp): - return self.to_circuit_op().tensor(other) - - return TensoredOp([self, other]) - - def permute(self, permutation: List[int]) -> "PauliOp": - """Permutes the sequence of Pauli matrices. - - Args: - permutation: A list defining where each Pauli should be permuted. The Pauli at index - j of the primitive should be permuted to position permutation[j]. - - Returns: - A new PauliOp representing the permuted operator. For operator (X ^ Y ^ Z) and - indices=[1,2,4], it returns (X ^ I ^ Y ^ Z ^ I). - - Raises: - OpflowError: if indices do not define a new index for each qubit. - """ - pauli_string = self.primitive.__str__() - length = max(permutation) + 1 # size of list must be +1 larger then its max index - new_pauli_list = ["I"] * length - if len(permutation) != self.num_qubits: - raise OpflowError( - "List of indices to permute must have the same size as Pauli Operator" - ) - for i, index in enumerate(permutation): - new_pauli_list[-index - 1] = pauli_string[-i - 1] - return PauliOp(Pauli("".join(new_pauli_list)), self.coeff) - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - new_self = cast(PauliOp, new_self) - - if front: - return other.compose(new_self) - - # Both Paulis - if isinstance(other, PauliOp): - product = new_self.primitive.dot(other.primitive) - return PrimitiveOp(product, coeff=new_self.coeff * other.coeff) - - # pylint: disable=cyclic-import - from .pauli_sum_op import PauliSumOp - - if isinstance(other, PauliSumOp): - return PauliSumOp( - SparsePauliOp(new_self.primitive).dot(other.primitive), - coeff=new_self.coeff * other.coeff, - ) - - # pylint: disable=cyclic-import - from ..state_fns.circuit_state_fn import CircuitStateFn - from .circuit_op import CircuitOp - - if isinstance(other, (CircuitOp, CircuitStateFn)): - return new_self.to_circuit_op().compose(other) - - return super(PauliOp, new_self).compose(other) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) - return self.primitive.to_matrix() * self.coeff - - def to_spmatrix(self) -> spmatrix: - """Returns SciPy sparse matrix representation of the Operator. - - Returns: - CSR sparse matrix representation of the Operator. - - Raises: - ValueError: invalid parameters. - """ - return self.primitive.to_matrix(sparse=True) * self.coeff - - def __str__(self) -> str: - prim_str = str(self.primitive) - if self.coeff == 1.0: - return prim_str - else: - return f"{self.coeff} * {prim_str}" - - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - if front is None: - return self.to_matrix_op() - - # pylint: disable=cyclic-import - from ..list_ops.list_op import ListOp - from ..state_fns.circuit_state_fn import CircuitStateFn - from ..state_fns.dict_state_fn import DictStateFn - from ..state_fns.state_fn import StateFn - from .circuit_op import CircuitOp - - new_front = None - - # For now, always do this. If it's not performant, we can be more granular. - if not isinstance(front, OperatorBase): - front = StateFn(front, is_measurement=False) - - if isinstance(front, ListOp) and front.distributive: - new_front = front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - else: - - if self.num_qubits != front.num_qubits: - raise ValueError( - "eval does not support operands with differing numbers of qubits, " - "{} and {}, respectively.".format(self.num_qubits, front.num_qubits) - ) - - if isinstance(front, DictStateFn): - - new_dict: Dict[str, complex] = {} - corrected_x_bits = self.primitive.x[::-1] - corrected_z_bits = self.primitive.z[::-1] - - for bstr, v in front.primitive.items(): - bitstr = np.fromiter(bstr, dtype=int).astype(bool) - new_b_str = np.logical_xor(bitstr, corrected_x_bits) - new_str = "".join(map(str, 1 * new_b_str)) - z_factor = np.prod(1 - 2 * np.logical_and(bitstr, corrected_z_bits)) - y_factor = np.prod( - np.sqrt(1 - 2 * np.logical_and(corrected_x_bits, corrected_z_bits) + 0j) - ) - new_dict[new_str] = (v * z_factor * y_factor) + new_dict.get(new_str, 0) - # The coefficient consists of: - # 1. the coefficient of *this* PauliOp (self) - # 2. the coefficient of the evaluated DictStateFn (front) - # 3. AND acquires the phase of the internal primitive. This is necessary to - # ensure that (X @ Z) and (-iY) return the same result. - new_front = StateFn( - new_dict, coeff=self.coeff * front.coeff * (-1j) ** self.primitive.phase - ) - - elif isinstance(front, StateFn) and front.is_measurement: - raise ValueError("Operator composed with a measurement is undefined.") - - # Composable types with PauliOp - elif isinstance(front, (PauliOp, CircuitOp, CircuitStateFn)): - new_front = self.compose(front) - - # Covers VectorStateFn and OperatorStateFn - elif isinstance(front, StateFn): - new_front = self.to_matrix_op().eval(front.to_matrix_op()) - - return new_front - - def exp_i(self) -> OperatorBase: - """Return a ``CircuitOp`` equivalent to e^-iH for this operator H.""" - # if only one qubit is significant, we can perform the evolution - corrected_x = self.primitive.x[::-1] - corrected_z = self.primitive.z[::-1] - sig_qubits = np.logical_or(corrected_x, corrected_z) - if np.sum(sig_qubits) == 0: - # e^I is just a global phase, but we can keep track of it! Should we? - # For now, just return identity - return PauliOp(self.primitive) - if np.sum(sig_qubits) == 1: - sig_qubit_index = sig_qubits.tolist().index(True) - coeff = ( - np.real(self.coeff) - if not isinstance(self.coeff, ParameterExpression) - else self.coeff - ) - - from .circuit_op import CircuitOp - - # Y rotation - if corrected_x[sig_qubit_index] and corrected_z[sig_qubit_index]: - rot_op = CircuitOp(RYGate(2 * coeff)) - # Z rotation - elif corrected_z[sig_qubit_index]: - rot_op = CircuitOp(RZGate(2 * coeff)) - # X rotation - elif corrected_x[sig_qubit_index]: - rot_op = CircuitOp(RXGate(2 * coeff)) - - # pylint: disable=cyclic-import - from ..operator_globals import I - - left_pad = I.tensorpower(sig_qubit_index) - right_pad = I.tensorpower(self.num_qubits - sig_qubit_index - 1) - # Need to use overloaded operators here in case left_pad == I^0 - return left_pad ^ rot_op ^ right_pad - else: - from ..evolutions.evolved_op import EvolvedOp - - return EvolvedOp(self) - - def to_circuit(self) -> QuantumCircuit: - - pauli = self.primitive.to_label()[-self.num_qubits :] - phase = self.primitive.phase - - qc = QuantumCircuit(self.num_qubits) - if pauli == "I" * self.num_qubits: - qc.global_phase = -phase * pi / 2 - return qc - - if self.num_qubits == 1: - if pauli != "I": - gate = {"X": XGate, "Y": YGate, "Z": ZGate}[pauli] - qc.append(gate(), [0]) - else: - gate = PauliGate(pauli) - qc.append(gate, range(self.num_qubits)) - - if not phase: - return qc - - qc.global_phase = -phase * pi / 2 - return qc - - def to_instruction(self) -> Instruction: - # TODO should we just do the following because performance of adding and deleting IGates - # doesn't matter? - # (Reduce removes extra IGates). - # return PrimitiveOp(self.primitive.to_instruction(), coeff=self.coeff).reduce() - - return self.primitive.to_instruction() - - def to_pauli_op(self, massive: bool = False) -> "PauliOp": - return self diff --git a/qiskit/opflow/primitive_ops/pauli_sum_op.py b/qiskit/opflow/primitive_ops/pauli_sum_op.py deleted file mode 100644 index b1bb9b7242a9..000000000000 --- a/qiskit/opflow/primitive_ops/pauli_sum_op.py +++ /dev/null @@ -1,463 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""PauliSumOp Class""" - -from collections import defaultdict -from typing import Dict, List, Optional, Set, Tuple, Union, cast -import numpy as np -from scipy.sparse import spmatrix - -from qiskit.circuit import Instruction, ParameterExpression -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.list_ops.tensored_op import TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp -from qiskit.quantum_info import Pauli, SparsePauliOp, Statevector -from qiskit.quantum_info.operators.custom_iterator import CustomIterator -from qiskit.utils.deprecation import deprecate_func - - -class PauliSumOp(PrimitiveOp): - """Deprecated: Class for Operators backed by Terra's ``SparsePauliOp`` class.""" - - primitive: SparsePauliOp - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: SparsePauliOp, - coeff: Union[complex, ParameterExpression] = 1.0, - grouping_type: str = "None", - ) -> None: - """ - Args: - primitive: The SparsePauliOp which defines the behavior of the underlying function. - coeff: A coefficient multiplying the primitive. - grouping_type: The type of grouping. If None, the operator is not grouped. - - Raises: - TypeError: invalid parameters. - """ - if not isinstance(primitive, SparsePauliOp): - raise TypeError( - f"PauliSumOp can only be instantiated with SparsePauliOp, not {type(primitive)}" - ) - - super().__init__(primitive, coeff=coeff) - self._grouping_type = grouping_type - - def primitive_strings(self) -> Set[str]: - return {"SparsePauliOp"} - - @property - def grouping_type(self) -> str: - """ - Returns: Type of Grouping - """ - return self._grouping_type - - @property - def num_qubits(self) -> int: - return self.primitive.num_qubits - - @property - def coeffs(self): - """Return the Pauli coefficients.""" - return self.coeff * self.primitive.coeffs - - @property - def settings(self) -> Dict: - """Return operator settings.""" - data = super().settings - data.update({"grouping_type": self._grouping_type}) - return data - - def matrix_iter(self, sparse=False): - """Return a matrix representation iterator. - - This is a lazy iterator that converts each term in the PauliSumOp - into a matrix as it is used. To convert to a single matrix use the - :meth:`to_matrix` method. - - Args: - sparse (bool): optionally return sparse CSR matrices if True, - otherwise return Numpy array matrices - (Default: False) - - Returns: - MatrixIterator: matrix iterator object for the PauliSumOp. - """ - - class MatrixIterator(CustomIterator): - """Matrix representation iteration and item access.""" - - def __repr__(self): - return f"" - - def __getitem__(self, key): - sumopcoeff = self.obj.coeff * self.obj.primitive.coeffs[key] - return sumopcoeff * self.obj.primitive.paulis[key].to_matrix(sparse=sparse) - - return MatrixIterator(self) - - def add(self, other: OperatorBase) -> OperatorBase: - if not self.num_qubits == other.num_qubits: - raise ValueError( - f"Sum of operators with different numbers of qubits, {self.num_qubits} and " - f"{other.num_qubits}, is not well defined" - ) - - if ( - isinstance(other, PauliSumOp) - and not isinstance(self.coeff, ParameterExpression) - and not isinstance(other.coeff, ParameterExpression) - ): - return PauliSumOp(self.coeff * self.primitive + other.coeff * other.primitive, coeff=1) - - if ( - isinstance(other, PauliOp) - and not isinstance(self.coeff, ParameterExpression) - and not isinstance(other.coeff, ParameterExpression) - ): - return PauliSumOp( - self.coeff * self.primitive + other.coeff * SparsePauliOp(other.primitive) - ) - - return SummedOp([self, other]) - - def mul(self, scalar: Union[complex, ParameterExpression]) -> OperatorBase: - if isinstance(scalar, (int, float, complex)) and scalar != 0: - return PauliSumOp(scalar * self.primitive, coeff=self.coeff) - - return PauliSumOp(self.primitive, coeff=self.coeff * scalar) - - def adjoint(self) -> "PauliSumOp": - return PauliSumOp(self.primitive.adjoint(), coeff=self.coeff.conjugate()) - - def equals(self, other: OperatorBase) -> bool: - self_reduced, other_reduced = self.reduce(), other.reduce() - - if isinstance(other_reduced, PauliOp): - other_reduced = PauliSumOp( - SparsePauliOp(other_reduced.primitive, coeffs=[other_reduced.coeff]) - ) - - if not isinstance(other_reduced, PauliSumOp): - return False - - if isinstance(self_reduced.coeff, ParameterExpression) or isinstance( - other_reduced.coeff, ParameterExpression - ): - return self_reduced.coeff == other_reduced.coeff and self_reduced.primitive.equiv( - other_reduced.primitive - ) - return len(self_reduced) == len(other_reduced) and self_reduced.primitive.equiv( - other_reduced.primitive - ) - - def _expand_dim(self, num_qubits: int) -> "PauliSumOp": - return PauliSumOp( - self.primitive.tensor(SparsePauliOp(Pauli("I" * num_qubits))), - coeff=self.coeff, - ) - - def tensor(self, other: OperatorBase) -> Union["PauliSumOp", TensoredOp]: - if isinstance(other, PauliSumOp): - return PauliSumOp( - self.primitive.tensor(other.primitive), - coeff=self.coeff * other.coeff, - ) - if isinstance(other, PauliOp): - return PauliSumOp( - self.primitive.tensor(other.primitive), - coeff=self.coeff * other.coeff, - ) - - return TensoredOp([self, other]) - - def permute(self, permutation: List[int]) -> "PauliSumOp": - """Permutes the sequence of ``PauliSumOp``. - - Args: - permutation: A list defining where each Pauli should be permuted. The Pauli at index - j of the primitive should be permuted to position permutation[j]. - - Returns: - A new PauliSumOp representing the permuted operator. For operator (X ^ Y ^ Z) and - indices=[1,2,4], it returns (X ^ I ^ Y ^ Z ^ I). - - Raises: - OpflowError: if indices do not define a new index for each qubit. - """ - set_perm = set(permutation) - if len(set_perm) != len(permutation) or any(index < 0 for index in set_perm): - raise OpflowError(f"List {permutation} is not a permutation.") - - if len(permutation) != self.num_qubits: - raise OpflowError( - "List of indices to permute must have the same size as Pauli Operator" - ) - length = max(permutation) + 1 - - if length > self.num_qubits: - spop = self.primitive.tensor(SparsePauliOp(Pauli("I" * (length - self.num_qubits)))) - else: - spop = self.primitive.copy() - - permutation = [i for i in range(length) if i not in permutation] + permutation - permu_arr = np.arange(length)[np.argsort(permutation)] - spop.paulis.x = spop.paulis.x[:, permu_arr] - spop.paulis.z = spop.paulis.z[:, permu_arr] - return PauliSumOp(spop, self.coeff) - - def compose( - self, - other: OperatorBase, - permutation: Optional[List[int]] = None, - front: bool = False, - ) -> OperatorBase: - - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - new_self = cast(PauliSumOp, new_self) - - if front: - return other.compose(new_self) - # If self is identity, just return other. - if not np.any(np.logical_or(new_self.primitive.paulis.x, new_self.primitive.paulis.z)): - return other * new_self.coeff * sum(new_self.primitive.coeffs) - - # Both PauliSumOps - if isinstance(other, PauliSumOp): - return PauliSumOp( - new_self.primitive.dot(other.primitive), - coeff=new_self.coeff * other.coeff, - ) - if isinstance(other, PauliOp): - other_primitive = SparsePauliOp(other.primitive) - return PauliSumOp( - new_self.primitive.dot(other_primitive), - coeff=new_self.coeff * other.coeff, - ) - - # pylint: disable=cyclic-import - from ..state_fns.circuit_state_fn import CircuitStateFn - from .circuit_op import CircuitOp - - if isinstance(other, (CircuitOp, CircuitStateFn)): - pauli_op = cast(Union[PauliOp, SummedOp], new_self.to_pauli_op()) - return pauli_op.to_circuit_op().compose(other) - - return super(PauliSumOp, new_self).compose(other) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) - if isinstance(self.coeff, ParameterExpression): - return (self.primitive.to_matrix(sparse=True)).toarray() * self.coeff - return (self.primitive.to_matrix(sparse=True) * self.coeff).toarray() - - def __str__(self) -> str: - def format_sign(x): - return x.real if np.isreal(x) else x - - def format_number(x): - x = format_sign(x) - if isinstance(x, (int, float)) and x < 0: - return f"- {-x}" - return f"+ {x}" - - indent = "" if self.coeff == 1 else " " - prim_list = self.primitive.to_list() - if prim_list: - first = prim_list[0] - if isinstance(first[1], (int, float)) and first[1] < 0: - main_string = indent + f"- {-first[1].real} * {first[0]}" - else: - main_string = indent + f"{format_sign(first[1])} * {first[0]}" - - main_string += "".join([f"\n{indent}{format_number(c)} * {p}" for p, c in prim_list[1:]]) - return f"{main_string}" if self.coeff == 1 else f"{self.coeff} * (\n{main_string}\n)" - - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - if front is None: - return self.to_matrix_op() - - # pylint: disable=cyclic-import - from ..list_ops.list_op import ListOp - from ..state_fns.circuit_state_fn import CircuitStateFn - from ..state_fns.dict_state_fn import DictStateFn - from ..state_fns.state_fn import StateFn - from .circuit_op import CircuitOp - - # For now, always do this. If it's not performant, we can be more granular. - if not isinstance(front, OperatorBase): - front = StateFn(front, is_measurement=False) - - if isinstance(front, ListOp) and front.distributive: - return front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - else: - - if self.num_qubits != front.num_qubits: - raise ValueError( - "eval does not support operands with differing numbers of qubits, " - "{} and {}, respectively.".format(self.num_qubits, front.num_qubits) - ) - - if isinstance(front, DictStateFn): - new_dict: Dict[str, int] = defaultdict(int) - corrected_x_bits = self.primitive.paulis.x[:, ::-1] - corrected_z_bits = self.primitive.paulis.z[:, ::-1] - coeffs = self.primitive.coeffs - for bstr, v in front.primitive.items(): - bitstr = np.fromiter(bstr, dtype=int).astype(bool) - new_b_str = np.logical_xor(bitstr, corrected_x_bits) - new_str = ["".join([str(b) for b in bs]) for bs in new_b_str.astype(int)] - z_factor = np.prod(1 - 2 * np.logical_and(bitstr, corrected_z_bits), axis=1) - y_factor = np.prod( - np.sqrt(1 - 2 * np.logical_and(corrected_x_bits, corrected_z_bits) + 0j), - axis=1, - ) - for i, n_str in enumerate(new_str): - new_dict[n_str] += v * z_factor[i] * y_factor[i] * coeffs[i] - return DictStateFn(new_dict, coeff=self.coeff * front.coeff) - - elif isinstance(front, StateFn) and front.is_measurement: - raise ValueError("Operator composed with a measurement is undefined.") - - # Composable types with PauliOp - elif isinstance(front, (PauliSumOp, PauliOp, CircuitOp, CircuitStateFn)): - return self.compose(front).eval() - - # Covers VectorStateFn and OperatorStateFn - front = cast(StateFn, front) - return self.to_matrix_op().eval(front.to_matrix_op()) - - def exp_i(self) -> OperatorBase: - """Return a ``CircuitOp`` equivalent to e^-iH for this operator H.""" - # TODO: optimize for some special cases - from ..evolutions.evolved_op import EvolvedOp - - return EvolvedOp(self) - - def to_instruction(self) -> Instruction: - return self.to_matrix_op().to_circuit().to_instruction() # type: ignore - - def to_pauli_op(self, massive: bool = False) -> Union[PauliOp, SummedOp]: - def to_native(x): - return x.item() if isinstance(x, np.generic) else x - - if len(self.primitive) == 1: - return PauliOp( - Pauli((self.primitive.paulis.z[0], self.primitive.paulis.x[0])), - to_native(np.real_if_close(self.primitive.coeffs[0])) * self.coeff, - ) - coeffs = np.real_if_close(self.primitive.coeffs) - return SummedOp( - [ - PauliOp(pauli, to_native(coeff)) - for pauli, coeff in zip(self.primitive.paulis, coeffs) - ], - coeff=self.coeff, - ) - - def __getitem__(self, offset: Union[int, slice]) -> "PauliSumOp": - """Allows array-indexing style access to the ``PauliSumOp``. - - Args: - offset: The index of ``PauliSumOp``. - - Returns: - The ``PauliSumOp`` at index ``offset``, - """ - return PauliSumOp(self.primitive[offset], self.coeff) - - def __iter__(self): - for i in range(len(self)): - yield self[i] - - def __len__(self) -> int: - """Length of ``SparsePauliOp``. - - Returns: - An int equal to the length of SparsePauliOp. - """ - return len(self.primitive) - - def reduce(self, atol: Optional[float] = None, rtol: Optional[float] = None) -> "PauliSumOp": - """Simplify the primitive ``SparsePauliOp``. - - Args: - atol: Absolute tolerance for checking if coefficients are zero (Default: 1e-8). - rtol: Relative tolerance for checking if coefficients are zero (Default: 1e-5). - - Returns: - The simplified ``PauliSumOp``. - """ - if isinstance(self.coeff, (int, float, complex)): - primitive = self.coeff * self.primitive - return PauliSumOp(primitive.simplify(atol=atol, rtol=rtol)) - return PauliSumOp(self.primitive.simplify(atol=atol, rtol=rtol), self.coeff) - - def to_spmatrix(self) -> spmatrix: - """Returns SciPy sparse matrix representation of the ``PauliSumOp``. - - Returns: - CSR sparse matrix representation of the ``PauliSumOp``. - - Raises: - ValueError: invalid parameters. - """ - return self.primitive.to_matrix(sparse=True) * self.coeff - - @classmethod - def from_list( - cls, - pauli_list: List[Tuple[str, Union[complex, ParameterExpression]]], - coeff: Union[complex, ParameterExpression] = 1.0, - dtype: type = complex, - ) -> "PauliSumOp": - """Construct from a pauli_list with the form [(pauli_str, coeffs)] - - Args: - pauli_list: A list of Tuple of pauli_str and coefficient. - coeff: A coefficient multiplying the primitive. - dtype: The dtype to use to construct the internal SparsePauliOp. - Defaults to ``complex``. - - Returns: - The PauliSumOp constructed from the pauli_list. - """ - return cls(SparsePauliOp.from_list(pauli_list, dtype=dtype), coeff=coeff) - - def is_zero(self) -> bool: - """ - Return this operator is zero operator or not. - """ - op = self.reduce() - primitive: SparsePauliOp = op.primitive - return op.coeff == 1 and len(op) == 1 and primitive.coeffs[0] == 0 - - def is_hermitian(self): - return np.isreal(self.coeffs).all() and np.all(self.primitive.paulis.phase == 0) diff --git a/qiskit/opflow/primitive_ops/primitive_op.py b/qiskit/opflow/primitive_ops/primitive_op.py deleted file mode 100644 index de3ef06c3add..000000000000 --- a/qiskit/opflow/primitive_ops/primitive_op.py +++ /dev/null @@ -1,323 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""PrimitiveOp Class""" - -from typing import Dict, List, Optional, Set, Union, cast - -import numpy as np -import scipy.linalg -from scipy.sparse import spmatrix - -from qiskit import QuantumCircuit -from qiskit.circuit import Instruction, ParameterExpression -from qiskit.opflow.operator_base import OperatorBase -from qiskit.quantum_info import Operator, Pauli, SparsePauliOp, Statevector -from qiskit.utils.deprecation import deprecate_func - - -class PrimitiveOp(OperatorBase): - r""" - Deprecated: A class for representing basic Operators, backed by Operator primitives from - Terra. This class (and inheritors) primarily serves to allow the underlying - primitives to "flow" - i.e. interoperability and adherence to the Operator formalism - - while the core computational logic mostly remains in the underlying primitives. - For example, we would not produce an interface in Terra in which - ``QuantumCircuit1 + QuantumCircuit2`` equaled the Operator sum of the circuit - unitaries, rather than simply appending the circuits. However, within the Operator - flow summing the unitaries is the expected behavior. - - Note that all mathematical methods are not in-place, meaning that they return a - new object, but the underlying primitives are not copied. - - """ - - def __init_subclass__(cls): - cls.__new__ = lambda cls, *args, **kwargs: super().__new__(cls) - - @staticmethod - # pylint: disable=unused-argument - def __new__( - cls, - primitive: Union[ - Instruction, QuantumCircuit, List, np.ndarray, spmatrix, Operator, Pauli, SparsePauliOp - ], - coeff: Union[complex, ParameterExpression] = 1.0, - ) -> "PrimitiveOp": - """A factory method to produce the correct type of PrimitiveOp subclass - based on the primitive passed in. Primitive and coeff arguments are passed into - subclass's init() as-is automatically by new(). - - Args: - primitive: The operator primitive being wrapped. - coeff: A coefficient multiplying the primitive. - - Returns: - The appropriate PrimitiveOp subclass for ``primitive``. - - Raises: - TypeError: Unsupported primitive type passed. - """ - # pylint: disable=cyclic-import - if isinstance(primitive, (Instruction, QuantumCircuit)): - from .circuit_op import CircuitOp - - return super().__new__(CircuitOp) - - if isinstance(primitive, (list, np.ndarray, spmatrix, Operator)): - from .matrix_op import MatrixOp - - return super().__new__(MatrixOp) - - if isinstance(primitive, Pauli): - from .pauli_op import PauliOp - - return super().__new__(PauliOp) - - if isinstance(primitive, SparsePauliOp): - from .pauli_sum_op import PauliSumOp - - return super().__new__(PauliSumOp) - - raise TypeError( - "Unsupported primitive type {} passed into PrimitiveOp " - "factory constructor".format(type(primitive)) - ) - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: Union[QuantumCircuit, Operator, Pauli, SparsePauliOp, OperatorBase], - coeff: Union[complex, ParameterExpression] = 1.0, - ) -> None: - """ - Args: - primitive: The operator primitive being wrapped. - coeff: A coefficient multiplying the primitive. - """ - super().__init__() - self._primitive = primitive - self._coeff = coeff - - @property - def primitive(self) -> Union[QuantumCircuit, Operator, Pauli, SparsePauliOp, OperatorBase]: - """The primitive defining the underlying function of the Operator. - - Returns: - The primitive object. - """ - return self._primitive - - @property - def coeff(self) -> Union[complex, ParameterExpression]: - """ - The scalar coefficient multiplying the Operator. - - Returns: - The coefficient. - """ - return self._coeff - - @property - def num_qubits(self) -> int: - raise NotImplementedError - - @property - def settings(self) -> Dict: - """Return operator settings.""" - return {"primitive": self._primitive, "coeff": self._coeff} - - def primitive_strings(self) -> Set[str]: - raise NotImplementedError - - def add(self, other: OperatorBase) -> OperatorBase: - raise NotImplementedError - - def adjoint(self) -> OperatorBase: - raise NotImplementedError - - def equals(self, other: OperatorBase) -> bool: - raise NotImplementedError - - def mul(self, scalar: Union[complex, ParameterExpression]) -> OperatorBase: - if not isinstance(scalar, (int, float, complex, ParameterExpression)): - raise ValueError( - "Operators can only be scalar multiplied by float or complex, not " - "{} of type {}.".format(scalar, type(scalar)) - ) - # Need to return self.__class__ in case the object is one of the inherited OpPrimitives - return self.__class__(self.primitive, coeff=self.coeff * scalar) - - def tensor(self, other: OperatorBase) -> OperatorBase: - raise NotImplementedError - - def tensorpower(self, other: int) -> Union[OperatorBase, int]: - # Hack to make Z^(I^0) work as intended. - if other == 0: - return 1 - if not isinstance(other, int) or other < 0: - raise TypeError("Tensorpower can only take positive int arguments") - temp = PrimitiveOp(self.primitive, coeff=self.coeff) # type: OperatorBase - for _ in range(other - 1): - temp = temp.tensor(self) - return temp - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - # pylint: disable=cyclic-import - from ..list_ops.composed_op import ComposedOp - - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - if isinstance(other, ComposedOp): - comp_with_first = new_self.compose(other.oplist[0]) - if not isinstance(comp_with_first, ComposedOp): - new_oplist = [comp_with_first] + other.oplist[1:] - return ComposedOp(new_oplist, coeff=other.coeff) - return ComposedOp([new_self] + other.oplist, coeff=other.coeff) - - return ComposedOp([new_self, other]) - - def _expand_dim(self, num_qubits: int) -> OperatorBase: - raise NotImplementedError - - def permute(self, permutation: List[int]) -> OperatorBase: - raise NotImplementedError - - def exp_i(self) -> OperatorBase: - """Return Operator exponentiation, equaling e^(-i * op)""" - # pylint: disable=cyclic-import - from ..evolutions.evolved_op import EvolvedOp - - return EvolvedOp(self) - - def log_i(self, massive: bool = False) -> OperatorBase: - """Return a ``MatrixOp`` equivalent to log(H)/-i for this operator H. This - function is the effective inverse of exp_i, equivalent to finding the Hermitian - Operator which produces self when exponentiated.""" - # pylint: disable=cyclic-import - from ..operator_globals import EVAL_SIG_DIGITS - from .matrix_op import MatrixOp - - return MatrixOp( - np.around( - scipy.linalg.logm(self.to_matrix(massive=massive)) / -1j, decimals=EVAL_SIG_DIGITS - ) - ) - - def __str__(self) -> str: - raise NotImplementedError - - def __repr__(self) -> str: - return f"{type(self).__name__}({repr(self.primitive)}, coeff={self.coeff})" - - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - raise NotImplementedError - - @property - def parameters(self): - params = set() - if isinstance(self.primitive, (OperatorBase, QuantumCircuit)): - params.update(self.primitive.parameters) - if isinstance(self.coeff, ParameterExpression): - params.update(self.coeff.parameters) - return params - - def assign_parameters(self, param_dict: dict) -> OperatorBase: - param_value = self.coeff - if isinstance(self.coeff, ParameterExpression): - unrolled_dict = self._unroll_param_dict(param_dict) - if isinstance(unrolled_dict, list): - # pylint: disable=cyclic-import - from ..list_ops.list_op import ListOp - - return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) - if self.coeff.parameters <= set(unrolled_dict.keys()): - binds = {param: unrolled_dict[param] for param in self.coeff.parameters} - param_value = complex(self.coeff.bind(binds)) - if abs(param_value.imag) == 0: - param_value = param_value.real - return self.__class__(self.primitive, coeff=param_value) - - # Nothing to collapse here. - def reduce(self) -> OperatorBase: - return self - - def to_matrix(self, massive: bool = False) -> np.ndarray: - raise NotImplementedError - - def to_matrix_op(self, massive: bool = False) -> OperatorBase: - """Returns a ``MatrixOp`` equivalent to this Operator.""" - coeff = self.coeff - op = self.copy() - op._coeff = 1 - prim_mat = op.to_matrix(massive=massive) - from .matrix_op import MatrixOp - - return MatrixOp(prim_mat, coeff=coeff) - - def to_instruction(self) -> Instruction: - """Returns an ``Instruction`` equivalent to this Operator.""" - raise NotImplementedError - - def to_circuit(self) -> QuantumCircuit: - """Returns a ``QuantumCircuit`` equivalent to this Operator.""" - qc = QuantumCircuit(self.num_qubits) - qc.append(self.to_instruction(), qargs=range(self.primitive.num_qubits)) - return qc.decompose() - - def to_circuit_op(self) -> OperatorBase: - """Returns a ``CircuitOp`` equivalent to this Operator.""" - from .circuit_op import CircuitOp - - if self.coeff == 0: - return CircuitOp(QuantumCircuit(self.num_qubits), coeff=0) - return CircuitOp(self.to_circuit(), coeff=self.coeff) - - def to_pauli_op(self, massive: bool = False) -> OperatorBase: - """Returns a sum of ``PauliOp`` s equivalent to this Operator.""" - # pylint: disable=cyclic-import - from .matrix_op import MatrixOp - - mat_op = cast(MatrixOp, self.to_matrix_op(massive=massive)) - sparse_pauli = SparsePauliOp.from_operator(mat_op.primitive) - if not sparse_pauli.to_list(): - from ..operator_globals import I - - return (I ^ self.num_qubits) * 0.0 - from .pauli_op import PauliOp - - if len(sparse_pauli) == 1: - label, coeff = sparse_pauli.to_list()[0] - coeff = coeff.real if np.isreal(coeff) else coeff - return PauliOp(Pauli(label), coeff * self.coeff) - - from ..list_ops.summed_op import SummedOp - - return SummedOp( - [ - PrimitiveOp( - Pauli(label), - coeff.real if coeff == coeff.real else coeff, - ) - for (label, coeff) in sparse_pauli.to_list() - ], - self.coeff, - ) diff --git a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py deleted file mode 100644 index 9411afc71f26..000000000000 --- a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py +++ /dev/null @@ -1,588 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""TaperedPauliSumOp Class and Z2Symmetries""" - -import itertools -import logging -from copy import deepcopy -from typing import Dict, List, Optional, Union, cast - -import numpy as np - -from qiskit.circuit import ParameterExpression -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.utils import commutator -from qiskit.quantum_info import Pauli, SparsePauliOp -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - - -class TaperedPauliSumOp(PauliSumOp): - """Deprecated: Class for PauliSumOp after tapering""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: SparsePauliOp, - z2_symmetries: "Z2Symmetries", - coeff: Union[complex, ParameterExpression] = 1.0, - ) -> None: - """ - Args: - primitive: The SparsePauliOp which defines the behavior of the underlying function. - z2_symmetries: Z2 symmetries which the Operator has. - coeff: A coefficient multiplying the primitive. - - Raises: - TypeError: invalid parameters. - """ - super().__init__(primitive, coeff) - if not isinstance(z2_symmetries, Z2Symmetries): - raise TypeError( - f"Argument parameter z2_symmetries must be Z2Symmetries, not {type(z2_symmetries)}" - ) - self._z2_symmetries = z2_symmetries - - @property - def z2_symmetries(self) -> "Z2Symmetries": - """ - Z2 symmetries which the Operator has. - - Returns: - The Z2 Symmetries. - """ - return self._z2_symmetries - - @property - def settings(self) -> Dict: - """Return operator settings.""" - return { - "primitive": self._primitive, - "z2_symmetries": self._z2_symmetries, - "coeff": self._coeff, - } - - def assign_parameters(self, param_dict: dict) -> OperatorBase: - pauli_sum = PauliSumOp(self.primitive, self.coeff) - return pauli_sum.assign_parameters(param_dict) - - -class Z2Symmetries: - """Deprecated: Z2 Symmetries""" - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - symmetries: List[Pauli], - sq_paulis: List[Pauli], - sq_list: List[int], - tapering_values: Optional[List[int]] = None, - tol: float = 1e-14, - ): - """ - Args: - symmetries: the list of Pauli objects representing the Z_2 symmetries - sq_paulis: the list of single - qubit Pauli objects to construct the - Clifford operators - sq_list: the list of support of the single-qubit Pauli objects used to build - the Clifford operators - tapering_values: values determines the sector. - tol: Tolerance threshold for ignoring real and complex parts of a coefficient. - - Raises: - OpflowError: Invalid paulis - """ - if len(symmetries) != len(sq_paulis): - raise OpflowError( - "Number of Z2 symmetries has to be the same as number of single-qubit pauli x." - ) - - if len(sq_paulis) != len(sq_list): - raise OpflowError( - "Number of single-qubit pauli x has to be the same as length of single-qubit list." - ) - - if tapering_values is not None: - if len(sq_list) != len(tapering_values): - raise OpflowError( - "The length of single-qubit list has " - "to be the same as length of tapering values." - ) - - self._symmetries = symmetries - self._sq_paulis = sq_paulis - self._sq_list = sq_list - self._tapering_values = tapering_values - self._tol = tol - - @property - def tol(self): - """Tolerance threshold for ignoring real and complex parts of a coefficient.""" - return self._tol - - @tol.setter - def tol(self, value): - """Set the tolerance threshold for ignoring real and complex parts of a coefficient.""" - self._tol = value - - @property - def symmetries(self): - """return symmetries""" - return self._symmetries - - @property - def sq_paulis(self): - """returns sq paulis""" - return self._sq_paulis - - @property - def cliffords(self) -> List[PauliSumOp]: - """ - Get clifford operators, build based on symmetries and single-qubit X. - Returns: - a list of unitaries used to diagonalize the Hamiltonian. - """ - cliffords = [ - (PauliOp(pauli_symm) + PauliOp(sq_pauli)) / np.sqrt(2) - for pauli_symm, sq_pauli in zip(self._symmetries, self._sq_paulis) - ] - return cliffords - - @property - def sq_list(self): - """returns sq list""" - return self._sq_list - - @property - def tapering_values(self): - """returns tapering values""" - return self._tapering_values - - @tapering_values.setter - def tapering_values(self, new_value): - """set tapering values""" - self._tapering_values = new_value - - @property - def settings(self) -> Dict: - """Return operator settings.""" - return { - "symmetries": self._symmetries, - "sq_paulis": self._sq_paulis, - "sq_list": self._sq_list, - "tapering_values": self._tapering_values, - } - - def __str__(self): - ret = ["Z2 symmetries:"] - ret.append("Symmetries:") - for symmetry in self._symmetries: - ret.append(symmetry.to_label()) - ret.append("Single-Qubit Pauli X:") - for x in self._sq_paulis: - ret.append(x.to_label()) - ret.append("Cliffords:") - for c in self.cliffords: - ret.append(str(c)) - ret.append("Qubit index:") - ret.append(str(self._sq_list)) - ret.append("Tapering values:") - if self._tapering_values is None: - possible_values = [ - str(list(coeff)) for coeff in itertools.product([1, -1], repeat=len(self._sq_list)) - ] - possible_values = ", ".join(x for x in possible_values) - ret.append(" - Possible values: " + possible_values) - else: - ret.append(str(self._tapering_values)) - - ret = "\n".join(ret) - return ret - - def copy(self) -> "Z2Symmetries": - """ - Get a copy of self. - Returns: - copy - """ - return deepcopy(self) - - def is_empty(self) -> bool: - """ - Check the z2_symmetries is empty or not. - Returns: - Empty or not - """ - return self._symmetries == [] or self._sq_paulis == [] or self._sq_list == [] - - # pylint: disable=invalid-name - @classmethod - def find_Z2_symmetries(cls, operator: PauliSumOp) -> "Z2Symmetries": - """ - Finds Z2 Pauli-type symmetries of an Operator. - - Returns: - a z2_symmetries object contains symmetries, single-qubit X, single-qubit list. - """ - pauli_symmetries = [] - sq_paulis = [] - sq_list = [] - - stacked_paulis = [] - - if operator.is_zero(): - logger.info("Operator is empty.") - return cls([], [], [], None) - - for pauli in operator: - stacked_paulis.append( - np.concatenate( - (pauli.primitive.paulis.x[0], pauli.primitive.paulis.z[0]), axis=0 - ).astype(int) - ) - - stacked_matrix = np.array(np.stack(stacked_paulis)) - symmetries = _kernel_F2(stacked_matrix) - - if not symmetries: - logger.info("No symmetry is found.") - return cls([], [], [], None) - - stacked_symmetries = np.stack(symmetries) - symm_shape = stacked_symmetries.shape - - for row in range(symm_shape[0]): - - pauli_symmetries.append( - Pauli( - ( - stacked_symmetries[row, : symm_shape[1] // 2], - stacked_symmetries[row, symm_shape[1] // 2 :], - ) - ) - ) - - stacked_symm_del = np.delete(stacked_symmetries, row, axis=0) - for col in range(symm_shape[1] // 2): - # case symmetries other than one at (row) have Z or I on col qubit - Z_or_I = True - for symm_idx in range(symm_shape[0] - 1): - if not ( - stacked_symm_del[symm_idx, col] == 0 - and stacked_symm_del[symm_idx, col + symm_shape[1] // 2] in (0, 1) - ): - Z_or_I = False - if Z_or_I: - if ( - stacked_symmetries[row, col] == 1 - and stacked_symmetries[row, col + symm_shape[1] // 2] == 0 - ) or ( - stacked_symmetries[row, col] == 1 - and stacked_symmetries[row, col + symm_shape[1] // 2] == 1 - ): - sq_paulis.append( - Pauli((np.zeros(symm_shape[1] // 2), np.zeros(symm_shape[1] // 2))) - ) - sq_paulis[row].z[col] = False - sq_paulis[row].x[col] = True - sq_list.append(col) - break - - # case symmetries other than one at (row) have X or I on col qubit - X_or_I = True - for symm_idx in range(symm_shape[0] - 1): - if not ( - stacked_symm_del[symm_idx, col] in (0, 1) - and stacked_symm_del[symm_idx, col + symm_shape[1] // 2] == 0 - ): - X_or_I = False - if X_or_I: - if ( - stacked_symmetries[row, col] == 0 - and stacked_symmetries[row, col + symm_shape[1] // 2] == 1 - ) or ( - stacked_symmetries[row, col] == 1 - and stacked_symmetries[row, col + symm_shape[1] // 2] == 1 - ): - sq_paulis.append( - Pauli((np.zeros(symm_shape[1] // 2), np.zeros(symm_shape[1] // 2))) - ) - sq_paulis[row].z[col] = True - sq_paulis[row].x[col] = False - sq_list.append(col) - break - - # case symmetries other than one at (row) have Y or I on col qubit - Y_or_I = True - for symm_idx in range(symm_shape[0] - 1): - if not ( - ( - stacked_symm_del[symm_idx, col] == 1 - and stacked_symm_del[symm_idx, col + symm_shape[1] // 2] == 1 - ) - or ( - stacked_symm_del[symm_idx, col] == 0 - and stacked_symm_del[symm_idx, col + symm_shape[1] // 2] == 0 - ) - ): - Y_or_I = False - if Y_or_I: - if ( - stacked_symmetries[row, col] == 0 - and stacked_symmetries[row, col + symm_shape[1] // 2] == 1 - ) or ( - stacked_symmetries[row, col] == 1 - and stacked_symmetries[row, col + symm_shape[1] // 2] == 0 - ): - sq_paulis.append( - Pauli((np.zeros(symm_shape[1] // 2), np.zeros(symm_shape[1] // 2))) - ) - sq_paulis[row].z[col] = True - sq_paulis[row].x[col] = True - sq_list.append(col) - break - - return cls(pauli_symmetries, sq_paulis, sq_list, None) - - def convert_clifford(self, operator: PauliSumOp) -> OperatorBase: - """This method operates the first part of the tapering. - It converts the operator by composing it with the clifford unitaries defined in the current - symmetry. - - Args: - operator: to-be-tapered operator - - Returns: - :class:`PauliSumOp` corresponding to the converted operator. - - Raises: - OpflowError: Z2 symmetries, single qubit pauli and single qubit list cannot be empty - - """ - - if not self._symmetries or not self._sq_paulis or not self._sq_list: - raise OpflowError( - "Z2 symmetries, single qubit pauli and single qubit list cannot be empty." - ) - - if not operator.is_zero(): - for clifford in self.cliffords: - operator = cast(PauliSumOp, clifford @ operator @ clifford) - operator = operator.reduce(atol=0) - - return operator - - def taper_clifford(self, operator: PauliSumOp) -> OperatorBase: - """This method operates the second part of the tapering. - This function assumes that the input operators have already been transformed using - :meth:`convert_clifford`. The redundant qubits due to the symmetries are dropped and - replaced by their two possible eigenvalues. - The `tapering_values` will be stored into the resulted operator for a record. - - Args: - operator: Partially tapered operator resulting from a call to :meth:`convert_clifford` - - Returns: - If tapering_values is None: [:class:`PauliSumOp`]; otherwise, :class:`PauliSumOp` - - Raises: - OpflowError: Z2 symmetries, single qubit pauli and single qubit list cannot be empty - - """ - - if not self._symmetries or not self._sq_paulis or not self._sq_list: - raise OpflowError( - "Z2 symmetries, single qubit pauli and single qubit list cannot be empty." - ) - # If the operator is zero then we can skip the following. We still need to taper the - # operator to reduce its size i.e. the number of qubits so for example 0*"IIII" could - # taper to 0*"II" when symmetries remove two qubits. - if self._tapering_values is None: - tapered_ops_list = [ - self._taper(operator, list(coeff)) - for coeff in itertools.product([1, -1], repeat=len(self._sq_list)) - ] - tapered_ops: OperatorBase = ListOp(tapered_ops_list) - else: - tapered_ops = self._taper(operator, self._tapering_values) - - return tapered_ops - - def taper(self, operator: PauliSumOp) -> OperatorBase: - """ - Taper an operator based on the z2_symmetries info and sector defined by `tapering_values`. - The `tapering_values` will be stored into the resulted operator for a record. - - The tapering is a two-step algorithm which first converts the operator into a - :class:`PauliSumOp` with same eigenvalues but where some qubits are only acted upon - with the Pauli operators I or X. - The number M of these redundant qubits is equal to the number M of identified symmetries. - - The second step of the reduction consists in replacing these qubits with the possible - eigenvalues of the corresponding Pauli X, giving 2^M new operators with M less qubits. - If an eigenvalue sector was previously identified for the solution, then this reduces to - 1 new operator with M less qubits. - - Args: - operator: the to-be-tapered operator - - Returns: - If tapering_values is None: [:class:`PauliSumOp`]; otherwise, :class:`PauliSumOp` - - Raises: - OpflowError: Z2 symmetries, single qubit pauli and single qubit list cannot be empty - - """ - - if not self._symmetries or not self._sq_paulis or not self._sq_list: - raise OpflowError( - "Z2 symmetries, single qubit pauli and single qubit list cannot be empty." - ) - - converted_ops = self.convert_clifford(operator) - tapered_ops = self.taper_clifford(converted_ops) - - return tapered_ops - - def _taper(self, op: PauliSumOp, curr_tapering_values: List[int]) -> OperatorBase: - pauli_list = [] - for pauli_term in op: - coeff_out = pauli_term.primitive.coeffs[0] - for idx, qubit_idx in enumerate(self._sq_list): - if ( - pauli_term.primitive.paulis.z[0, qubit_idx] - or pauli_term.primitive.paulis.x[0, qubit_idx] - ): - coeff_out = curr_tapering_values[idx] * coeff_out - z_temp = np.delete(pauli_term.primitive.paulis.z[0].copy(), np.asarray(self._sq_list)) - x_temp = np.delete(pauli_term.primitive.paulis.x[0].copy(), np.asarray(self._sq_list)) - pauli_list.append((Pauli((z_temp, x_temp)).to_label(), coeff_out)) - - spo = SparsePauliOp.from_list(pauli_list).simplify(atol=0.0) - spo = spo.chop(self.tol) - z2_symmetries = self.copy() - z2_symmetries.tapering_values = curr_tapering_values - - return TaperedPauliSumOp(spo, z2_symmetries) - - def consistent_tapering(self, operator: PauliSumOp) -> OperatorBase: - """ - Tapering the `operator` with the same manner of how this tapered operator - is created. i.e., using the same Cliffords and tapering values. - - Args: - operator: the to-be-tapered operator - - Returns: - The tapered operator - - Raises: - OpflowError: The given operator does not commute with the symmetry - """ - for symmetry in self._symmetries: - commutator_op = cast(PauliSumOp, commutator(operator, PauliOp(symmetry))) - if not commutator_op.is_zero(): - raise OpflowError( - "The given operator does not commute with the symmetry, can not taper it." - ) - - return self.taper(operator) - - def __eq__(self, other: object) -> bool: - """ - Overload `==` operation to evaluate equality between Z2Symmetries. - - Args: - other: The `Z2Symmetries` to compare to self. - - Returns: - A bool equal to the equality of self and other. - """ - if not isinstance(other, Z2Symmetries): - return False - - return ( - self.symmetries == other.symmetries - and self.sq_paulis == other.sq_paulis - and self.sq_list == other.sq_list - and self.tapering_values == other.tapering_values - ) - - -def _kernel_F2(matrix_in) -> List[np.ndarray]: # pylint: disable=invalid-name - """ - Computes the kernel of a binary matrix on the binary finite field - Args: - matrix_in (numpy.ndarray): binary matrix - Returns: - The list of kernel vectors - """ - size = matrix_in.shape - kernel = [] - matrix_in_id = np.vstack((matrix_in, np.identity(size[1]))) - matrix_in_id_ech = (_row_echelon_F2(matrix_in_id.transpose())).transpose() - - for col in range(size[1]): - if np.array_equal( - matrix_in_id_ech[0 : size[0], col], np.zeros(size[0]) - ) and not np.array_equal(matrix_in_id_ech[size[0] :, col], np.zeros(size[1])): - kernel.append(matrix_in_id_ech[size[0] :, col]) - - return kernel - - -def _row_echelon_F2(matrix_in) -> np.ndarray: # pylint: disable=invalid-name - """ - Computes the row Echelon form of a binary matrix on the binary finite field - Args: - matrix_in (numpy.ndarray): binary matrix - Returns: - Matrix_in in Echelon row form - """ - size = matrix_in.shape - - for i in range(size[0]): - pivot_index = 0 - for j in range(size[1]): - if matrix_in[i, j] == 1: - pivot_index = j - break - for k in range(size[0]): - if k != i and matrix_in[k, pivot_index] == 1: - matrix_in[k, :] = np.mod(matrix_in[k, :] + matrix_in[i, :], 2) - - matrix_out_temp = deepcopy(matrix_in) - indices = [] - matrix_out = np.zeros(size) - - for i in range(size[0] - 1): - if np.array_equal(matrix_out_temp[i, :], np.zeros(size[1])): - indices.append(i) - for row in np.sort(indices)[::-1]: - matrix_out_temp = np.delete(matrix_out_temp, (row), axis=0) - - matrix_out[0 : size[0] - len(indices), :] = matrix_out_temp - matrix_out = matrix_out.astype(int) - - return matrix_out diff --git a/qiskit/opflow/state_fns/__init__.py b/qiskit/opflow/state_fns/__init__.py deleted file mode 100644 index 69b7d960bc20..000000000000 --- a/qiskit/opflow/state_fns/__init__.py +++ /dev/null @@ -1,78 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -State Functions (:mod:`qiskit.opflow.state_fns`) -================================================ - -.. deprecated:: 0.24.0 - - The :mod:`qiskit.opflow` module is deprecated and will be removed no earlier - than 3 months after the release date. For code migration guidelines, - visit https://qisk.it/opflow_migration. - -State functions are defined to be complex functions over a single binary -string (as compared to an operator, which is defined as a function over two binary strings, -or a function taking a binary function to another binary function). This function may be -called by the eval() method. - -Measurements are defined to be functionals over StateFns, taking them to real values. -Generally, this real value is interpreted to represent the probability of some classical -state (binary string) being observed from a probabilistic or quantum system represented -by a StateFn. This leads to the equivalent definition, which is that a measurement m is -a function over binary strings producing StateFns, such that the probability of measuring -a given binary string b from a system with StateFn f is equal to the inner -product between f and m(b). - -Note: - All mathematical methods between StateFns are not in-place, meaning that they return a - new object, but the underlying primitives are not copied. - -Note: - State functions here are not restricted to wave functions, as there is - no requirement of normalization. - -.. currentmodule:: qiskit.opflow.state_fns - -State Functions ---------------- - -.. autosummary:: - :toctree: ../stubs/ - :template: autosummary/class_no_inherited_members.rst - - StateFn - CircuitStateFn - DictStateFn - VectorStateFn - SparseVectorStateFn - OperatorStateFn - CVaRMeasurement - -""" - -from .state_fn import StateFn -from .dict_state_fn import DictStateFn -from .operator_state_fn import OperatorStateFn -from .vector_state_fn import VectorStateFn -from .sparse_vector_state_fn import SparseVectorStateFn -from .circuit_state_fn import CircuitStateFn -from .cvar_measurement import CVaRMeasurement - -__all__ = [ - "StateFn", - "DictStateFn", - "VectorStateFn", - "CircuitStateFn", - "OperatorStateFn", - "CVaRMeasurement", -] diff --git a/qiskit/opflow/state_fns/circuit_state_fn.py b/qiskit/opflow/state_fns/circuit_state_fn.py deleted file mode 100644 index 3f23f89a1cee..000000000000 --- a/qiskit/opflow/state_fns/circuit_state_fn.py +++ /dev/null @@ -1,403 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""CircuitStateFn Class""" - - -from typing import Dict, List, Optional, Set, Union, cast - -import numpy as np - -from qiskit import BasicAer, ClassicalRegister, QuantumCircuit, transpile -from qiskit.circuit import Instruction, ParameterExpression -from qiskit.circuit.exceptions import CircuitError -from qiskit.circuit.library import IGate, StatePreparation -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.composed_op import ComposedOp -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.list_ops.tensored_op import TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.circuit_op import CircuitOp -from qiskit.opflow.primitive_ops.matrix_op import MatrixOp -from qiskit.opflow.primitive_ops.pauli_op import PauliOp -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.opflow.state_fns.vector_state_fn import VectorStateFn -from qiskit.quantum_info import Statevector -from qiskit.utils.deprecation import deprecate_func - - -class CircuitStateFn(StateFn): - r""" - Deprecated: A class for state functions and measurements which are defined by the action of a - QuantumCircuit starting from \|0⟩, and stored using Terra's ``QuantumCircuit`` class. - """ - primitive: QuantumCircuit - - # TODO allow normalization somehow? - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: Union[QuantumCircuit, Instruction] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False, - from_operator: bool = False, - ) -> None: - """ - Args: - primitive: The ``QuantumCircuit`` (or ``Instruction``, which will be converted) which - defines the behavior of the underlying function. - coeff: A coefficient multiplying the state function. - is_measurement: Whether the StateFn is a measurement operator. - from_operator: if True the StateFn is derived from OperatorStateFn. (Default: False) - - Raises: - TypeError: Unsupported primitive, or primitive has ClassicalRegisters. - """ - if isinstance(primitive, Instruction): - qc = QuantumCircuit(primitive.num_qubits) - qc.append(primitive, qargs=range(primitive.num_qubits)) - primitive = qc - - if not isinstance(primitive, QuantumCircuit): - raise TypeError( - "CircuitStateFn can only be instantiated " - "with QuantumCircuit, not {}".format(type(primitive)) - ) - - if len(primitive.clbits) != 0: - raise TypeError("CircuitOp does not support QuantumCircuits with ClassicalRegisters.") - - super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) - - self.from_operator = from_operator - - @staticmethod - def from_dict(density_dict: dict) -> "CircuitStateFn": - """Construct the CircuitStateFn from a dict mapping strings to probability densities. - - Args: - density_dict: The dict representing the desired state. - - Returns: - The CircuitStateFn created from the dict. - """ - # If the dict is sparse (elements <= qubits), don't go - # building a statevector to pass to Qiskit's - # initializer, just create a sum. - if len(density_dict) <= len(list(density_dict.keys())[0]): - statefn_circuits = [] - for bstr, prob in density_dict.items(): - qc = QuantumCircuit(len(bstr)) - # NOTE: Reversing endianness!! - for (index, bit) in enumerate(reversed(bstr)): - if bit == "1": - qc.x(index) - sf_circuit = CircuitStateFn(qc, coeff=prob) - statefn_circuits += [sf_circuit] - if len(statefn_circuits) == 1: - return statefn_circuits[0] - else: - return cast(CircuitStateFn, SummedOp(cast(List[OperatorBase], statefn_circuits))) - else: - sf_dict = StateFn(density_dict) - return CircuitStateFn.from_vector(sf_dict.to_matrix()) - - @staticmethod - def from_vector(statevector: np.ndarray) -> "CircuitStateFn": - """Construct the CircuitStateFn from a vector representing the statevector. - - Args: - statevector: The statevector representing the desired state. - - Returns: - The CircuitStateFn created from the vector. - """ - normalization_coeff = np.linalg.norm(statevector) - normalized_sv = statevector / normalization_coeff - return CircuitStateFn(StatePreparation(normalized_sv), coeff=normalization_coeff) - - def primitive_strings(self) -> Set[str]: - return {"QuantumCircuit"} - - @property - def settings(self) -> Dict: - """Return settings.""" - data = super().settings - data["from_operator"] = self.from_operator - return data - - @property - def num_qubits(self) -> int: - return self.primitive.num_qubits - - def add(self, other: OperatorBase) -> OperatorBase: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over operators with different numbers of qubits, " - "{} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - if isinstance(other, CircuitStateFn) and self.primitive == other.primitive: - return CircuitStateFn(self.primitive, coeff=self.coeff + other.coeff) - - # Covers all else. - return SummedOp([self, other]) - - def adjoint(self) -> "CircuitStateFn": - try: - inverse = self.primitive.inverse() - except CircuitError as missing_inverse: - raise OpflowError( - "Failed to take the inverse of the underlying circuit, the circuit " - "is likely not unitary and can therefore not be inverted." - ) from missing_inverse - - return CircuitStateFn( - inverse, coeff=self.coeff.conjugate(), is_measurement=(not self.is_measurement) - ) - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - if not self.is_measurement and not front: - raise ValueError( - "Composition with a Statefunctions in the first operand is not defined." - ) - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - new_self.from_operator = self.from_operator - - if front: - return other.compose(new_self) - - if isinstance(other, (PauliOp, CircuitOp, MatrixOp)): - op_circuit_self = CircuitOp(self.primitive) - - # Avoid reimplementing compose logic - composed_op_circs = cast(CircuitOp, op_circuit_self.compose(other.to_circuit_op())) - - # Returning CircuitStateFn - return CircuitStateFn( - composed_op_circs.primitive, - is_measurement=self.is_measurement, - coeff=self.coeff * other.coeff, - from_operator=self.from_operator, - ) - - if isinstance(other, CircuitStateFn) and self.is_measurement: - # pylint: disable=cyclic-import - from ..operator_globals import Zero - - return self.compose(CircuitOp(other.primitive)).compose( - (Zero ^ self.num_qubits) * other.coeff - ) - - return ComposedOp([new_self, other]) - - def tensor(self, other: OperatorBase) -> Union["CircuitStateFn", TensoredOp]: - r""" - Return tensor product between self and other, overloaded by ``^``. - Note: You must be conscious of Qiskit's big-endian bit printing convention. - Meaning, Plus.tensor(Zero) - produces a \|+⟩ on qubit 0 and a \|0⟩ on qubit 1, or \|+⟩⨂\|0⟩, but would produce - a QuantumCircuit like: - - \|0⟩-- - \|+⟩-- - - Because Terra prints circuits and results with qubit 0 at the end of the string or circuit. - - Args: - other: The ``OperatorBase`` to tensor product with self. - - Returns: - An ``OperatorBase`` equivalent to the tensor product of self and other. - """ - if isinstance(other, CircuitStateFn) and other.is_measurement == self.is_measurement: - # Avoid reimplementing tensor, just use CircuitOp's - c_op_self = CircuitOp(self.primitive, self.coeff) - c_op_other = CircuitOp(other.primitive, other.coeff) - c_op = c_op_self.tensor(c_op_other) - if isinstance(c_op, CircuitOp): - return CircuitStateFn( - primitive=c_op.primitive, - coeff=c_op.coeff, - is_measurement=self.is_measurement, - ) - return TensoredOp([self, other]) - - def to_density_matrix(self, massive: bool = False) -> np.ndarray: - """ - Return numpy matrix of density operator, warn if more than 16 qubits to - force the user to set - massive=True if they want such a large matrix. Generally big methods like this - should require the use of a - converter, but in this case a convenience method for quick hacking and access - to classical tools is - appropriate. - """ - OperatorBase._check_massive("to_density_matrix", True, self.num_qubits, massive) - # Rely on VectorStateFn's logic here. - return VectorStateFn(self.to_matrix(massive=massive) * self.coeff).to_density_matrix() - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) - - # Need to adjoint to get forward statevector and then reverse - if self.is_measurement: - return np.conj(self.adjoint().to_matrix(massive=massive)) - qc = self.to_circuit(meas=False) - statevector_backend = BasicAer.get_backend("statevector_simulator") - transpiled = transpile(qc, statevector_backend, optimization_level=0) - statevector = statevector_backend.run(transpiled).result().get_statevector() - from ..operator_globals import EVAL_SIG_DIGITS - - return np.round(statevector * self.coeff, decimals=EVAL_SIG_DIGITS) - - def __str__(self) -> str: - qc = cast(CircuitStateFn, self.reduce()).to_circuit() - prim_str = str(qc.draw(output="text")) - if self.coeff == 1.0: - return "{}(\n{}\n)".format( - "CircuitStateFn" if not self.is_measurement else "CircuitMeasurement", prim_str - ) - else: - return "{}(\n{}\n) * {}".format( - "CircuitStateFn" if not self.is_measurement else "CircuitMeasurement", - prim_str, - self.coeff, - ) - - def assign_parameters(self, param_dict: dict) -> Union["CircuitStateFn", ListOp]: - param_value = self.coeff - qc = self.primitive - if isinstance(self.coeff, ParameterExpression) or self.primitive.parameters: - unrolled_dict = self._unroll_param_dict(param_dict) - if isinstance(unrolled_dict, list): - return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) - if isinstance(self.coeff, ParameterExpression) and self.coeff.parameters <= set( - unrolled_dict.keys() - ): - param_instersection = set(unrolled_dict.keys()) & self.coeff.parameters - binds = {param: unrolled_dict[param] for param in param_instersection} - param_value = float(self.coeff.bind(binds)) - # & is set intersection, check if any parameters in unrolled are present in circuit - # This is different from bind_parameters in Terra because they check for set equality - if set(unrolled_dict.keys()) & self.primitive.parameters: - # Only bind the params found in the circuit - param_instersection = set(unrolled_dict.keys()) & self.primitive.parameters - binds = {param: unrolled_dict[param] for param in param_instersection} - qc = self.to_circuit().assign_parameters(binds) - return self.__class__(qc, coeff=param_value, is_measurement=self.is_measurement) - - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - if front is None: - vector_state_fn = self.to_matrix_op().eval() - return vector_state_fn - - if not self.is_measurement and isinstance(front, OperatorBase): - raise ValueError( - "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " - "sf.adjoint() first to convert to measurement." - ) - - if isinstance(front, ListOp) and front.distributive: - return front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - # Composable with circuit - if isinstance(front, (PauliOp, CircuitOp, MatrixOp, CircuitStateFn)): - new_front = self.compose(front) - return new_front.eval() - - return self.to_matrix_op().eval(front) - - def to_circuit(self, meas: bool = False) -> QuantumCircuit: - """Return QuantumCircuit representing StateFn""" - if meas: - meas_qc = self.primitive.copy() - meas_qc.add_register(ClassicalRegister(self.num_qubits)) - meas_qc.measure(qubit=range(self.num_qubits), cbit=range(self.num_qubits)) - return meas_qc - else: - return self.primitive - - def to_circuit_op(self) -> OperatorBase: - """Return ``StateFnCircuit`` corresponding to this StateFn.""" - return self - - def to_instruction(self): - """Return Instruction corresponding to primitive.""" - return self.primitive.to_instruction() - - # TODO specify backend? - def sample( - self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False - ) -> dict: - """ - Sample the state function as a normalized probability distribution. Returns dict of - bitstrings in order of probability, with values being probability. - """ - OperatorBase._check_massive("sample", False, self.num_qubits, massive) - qc = self.to_circuit(meas=True) - qasm_backend = BasicAer.get_backend("qasm_simulator") - transpiled = transpile(qc, qasm_backend, optimization_level=0) - counts = qasm_backend.run(transpiled, shots=shots).result().get_counts() - if reverse_endianness: - scaled_dict = {bstr[::-1]: (prob / shots) for (bstr, prob) in counts.items()} - else: - scaled_dict = {bstr: (prob / shots) for (bstr, prob) in counts.items()} - return dict(sorted(scaled_dict.items(), key=lambda x: x[1], reverse=True)) - - # Warning - modifying primitive!! - def reduce(self) -> "CircuitStateFn": - if self.primitive.data is not None: - # Need to do this from the end because we're deleting items! - for i in reversed(range(len(self.primitive.data))): - gate = self.primitive.data[i].operation - # Check if Identity or empty instruction (need to check that type is exactly - # Instruction because some gates have lazy gate.definition population) - # pylint: disable=unidiomatic-typecheck - if isinstance(gate, IGate) or ( - type(gate) == Instruction and gate.definition.data == [] - ): - del self.primitive.data[i] - return self - - def _expand_dim(self, num_qubits: int) -> "CircuitStateFn": - # this is equivalent to self.tensor(identity_operator), but optimized for better performance - # just like in tensor method, qiskit endianness is reversed here - return self.permute(list(range(num_qubits, num_qubits + self.num_qubits))) - - def permute(self, permutation: List[int]) -> "CircuitStateFn": - r""" - Permute the qubits of the circuit. - - Args: - permutation: A list defining where each qubit should be permuted. The qubit at index - j of the circuit should be permuted to position permutation[j]. - - Returns: - A new CircuitStateFn containing the permuted circuit. - """ - new_qc = QuantumCircuit(max(permutation) + 1).compose(self.primitive, qubits=permutation) - return CircuitStateFn(new_qc, coeff=self.coeff, is_measurement=self.is_measurement) diff --git a/qiskit/opflow/state_fns/cvar_measurement.py b/qiskit/opflow/state_fns/cvar_measurement.py deleted file mode 100644 index 858d3b87f671..000000000000 --- a/qiskit/opflow/state_fns/cvar_measurement.py +++ /dev/null @@ -1,386 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""CVaRMeasurement class.""" - - -from typing import Callable, Optional, Tuple, Union, cast, Dict - -import numpy as np - -from qiskit.circuit import ParameterExpression -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops import ListOp, SummedOp, TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops import PauliOp, PauliSumOp -from qiskit.opflow.state_fns.circuit_state_fn import CircuitStateFn -from qiskit.opflow.state_fns.dict_state_fn import DictStateFn -from qiskit.opflow.state_fns.operator_state_fn import OperatorStateFn -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.opflow.state_fns.vector_state_fn import VectorStateFn -from qiskit.quantum_info import Statevector -from qiskit.utils.deprecation import deprecate_func - - -class CVaRMeasurement(OperatorStateFn): - r"""Deprecated: A specialized measurement class to compute CVaR expectation values. - See https://arxiv.org/pdf/1907.04769.pdf for further details. - - Used in :class:`~qiskit.opflow.CVaRExpectation`, see there for more details. - """ - - primitive: OperatorBase - - # TODO allow normalization somehow? - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: OperatorBase = None, - alpha: float = 1.0, - coeff: Union[complex, ParameterExpression] = 1.0, - ) -> None: - """ - Args: - primitive: The ``OperatorBase`` which defines the diagonal operator - measurement. - coeff: A coefficient by which to multiply the state function - alpha: A real-valued parameter between 0 and 1 which specifies the - fraction of observed samples to include when computing the - objective value. alpha = 1 corresponds to a standard observable - expectation value. alpha = 0 corresponds to only using the single - sample with the lowest energy. alpha = 0.5 corresponds to ranking each - observation by lowest energy and using the best - - Raises: - ValueError: TODO remove that this raises an error - ValueError: If alpha is not in [0, 1]. - OpflowError: If the primitive is not diagonal. - """ - if primitive is None: - raise ValueError - - if not 0 <= alpha <= 1: - raise ValueError("The parameter alpha must be in [0, 1].") - self._alpha = alpha - - if not _check_is_diagonal(primitive): - raise OpflowError( - "Input operator to CVaRMeasurement must be diagonal, but is not:", str(primitive) - ) - - super().__init__(primitive, coeff=coeff, is_measurement=True) - - @property - def alpha(self) -> float: - """A real-valued parameter between 0 and 1 which specifies the - fraction of observed samples to include when computing the - objective value. alpha = 1 corresponds to a standard observable - expectation value. alpha = 0 corresponds to only using the single - sample with the lowest energy. alpha = 0.5 corresponds to ranking each - observation by lowest energy and using the best half. - - Returns: - The parameter alpha which was given at initialization - """ - return self._alpha - - @property - def settings(self) -> Dict: - """Return settings.""" - return {"primitive": self._primitive, "coeff": self._coeff, "alpha": self._alpha} - - def add(self, other: OperatorBase) -> SummedOp: - return SummedOp([self, other]) - - def adjoint(self): - """The adjoint of a CVaRMeasurement is not defined. - - Returns: - Does not return anything, raises an error. - - Raises: - OpflowError: The adjoint of a CVaRMeasurement is not defined. - """ - raise OpflowError("Adjoint of a CVaR measurement not defined") - - def mul(self, scalar: Union[complex, ParameterExpression]) -> "CVaRMeasurement": - if not isinstance(scalar, (int, float, complex, ParameterExpression)): - raise ValueError( - "Operators can only be scalar multiplied by float or complex, not " - "{} of type {}.".format(scalar, type(scalar)) - ) - - return self.__class__(self.primitive, coeff=self.coeff * scalar, alpha=self._alpha) - - def tensor(self, other: OperatorBase) -> Union["OperatorStateFn", TensoredOp]: - if isinstance(other, OperatorStateFn): - return OperatorStateFn( - self.primitive.tensor(other.primitive), coeff=self.coeff * other.coeff - ) - return TensoredOp([self, other]) - - def to_density_matrix(self, massive: bool = False): - """Not defined.""" - raise NotImplementedError - - def to_matrix_op(self, massive: bool = False): - """Not defined.""" - raise NotImplementedError - - def to_matrix(self, massive: bool = False): - """Not defined.""" - raise NotImplementedError - - def to_circuit_op(self): - """Not defined.""" - raise NotImplementedError - - def __str__(self) -> str: - return f"CVaRMeasurement({str(self.primitive)}) * {self.coeff}" - - def eval( - self, front: Union[str, dict, np.ndarray, OperatorBase, Statevector] = None - ) -> complex: - r""" - Given the energies of each sampled measurement outcome (H_i) as well as the - sampling probability of each measurement outcome (p_i, we can compute the - CVaR as H_j + 1/α*(sum_i complex: - r""" - Given the energies of each sampled measurement outcome (H_i) as well as the - sampling probability of each measurement outcome (p_i, we can compute the - variance of the CVaR estimator as - H_j^2 + 1/α * (sum_i], where H is the diagonal observable and bi - corresponds to measurement outcome i. Given this, E[X^2] = E[^2] - - Args: - front: A StateFn or primitive which specifies the results of evaluating - a quantum state. - - Returns: - The Var[CVaR] of the diagonal observable specified by self.primitive - and the sampled quantum state described by the inputs - (energies, probabilities). For index j (described above), the CVaR - is computed as H_j^2 + 1/α*(sum_i Tuple[list, list]: - r""" - In order to compute the CVaR of an observable expectation, we require - the energies of each sampled measurement outcome as well as the sampling - probability of each measurement outcome. Note that the counts for each - measurement outcome will also suffice (and this is often how the CVaR - is presented). - - Args: - front: A StateFn or a primitive which defines a StateFn. - This input holds the results of a sampled/simulated circuit. - - Returns: - Two lists of equal length. `energies` contains the energy of each - unique measurement outcome computed against the diagonal observable - stored in self.primitive. `probabilities` contains the corresponding - sampling probability for each measurement outcome in `energies`. - - Raises: - ValueError: front isn't a DictStateFn or VectorStateFn - """ - if isinstance(front, CircuitStateFn): - front = cast(StateFn, front.eval()) - - # Standardize the inputs to a dict - if isinstance(front, DictStateFn): - data = front.primitive - elif isinstance(front, VectorStateFn): - vec = front.primitive.data - # Determine how many bits are needed - key_len = int(np.ceil(np.log2(len(vec)))) - # Convert the vector primitive into a dict. The formatting here ensures - # that the proper number of leading `0` characters are added. - data = {format(index, "0" + str(key_len) + "b"): val for index, val in enumerate(vec)} - else: - raise ValueError("Unsupported input to CVaRMeasurement.eval:", type(front)) - - obs = self.primitive - outcomes = list(data.items()) - # add energy evaluation - for i, outcome in enumerate(outcomes): - key = outcome[0] - outcomes[i] += (obs.eval(key).adjoint().eval(key),) # type: ignore - - # Sort each observation based on it's energy - outcomes = sorted(outcomes, key=lambda x: x[2]) # type: ignore - - # Here probabilities are the (root) probabilities of - # observing each state. energies are the expectation - # values of each state with the provided Hamiltonian. - _, root_probabilities, energies = zip(*outcomes) - - # Square the dict values - # (since CircuitSampler takes the root...) - probabilities = [p_i * np.conj(p_i) for p_i in root_probabilities] - return list(energies), probabilities - - def compute_cvar(self, energies: list, probabilities: list) -> complex: - r""" - Given the energies of each sampled measurement outcome (H_i) as well as the - sampling probability of each measurement outcome (p_i, we can compute the - CVaR. Note that the sampling probabilities serve as an alternative to knowing - the counts of each observation and that the input energies are assumed to be - sorted in increasing order. - - Consider the outcome with index j, such that only some of the samples with - measurement outcome j will be used in computing CVaR. The CVaR calculation - can then be separated into two parts. First we sum each of the energies for - outcomes i < j, weighted by the probability of observing that outcome (i.e - the normalized counts). Second, we add the energy for outcome j, weighted by - the difference (α - \sum_i alpha: - break - - h_j = energies[j] - cvar = alpha * h_j - - if alpha == 0 or j == 0: - return self.coeff * h_j - - energies = energies[:j] - probabilities = probabilities[:j] - # Let H_i be the energy associated with outcome i - # and let the outcomes be sorted by ascending energy. - # Let p_i be the probability of observing outcome i. - # CVaR = H_j + 1/α*(sum_i OperatorBase: - r""" - Apply the convert_fn to the internal primitive if the primitive is an Operator (as in - the case of ``OperatorStateFn``). Otherwise do nothing. Used by converters. - - Args: - convert_fn: The function to apply to the internal OperatorBase. - coeff: A coefficient to multiply by after applying convert_fn. - If it is None, self.coeff is used instead. - - Returns: - The converted StateFn. - """ - if coeff is None: - coeff = self.coeff - - if isinstance(self.primitive, OperatorBase): - return self.__class__(convert_fn(self.primitive), coeff=coeff, alpha=self._alpha) - return self - - def sample(self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False): - raise NotImplementedError - - -def _check_is_diagonal(operator: OperatorBase) -> bool: - """Check whether ``operator`` is diagonal. - - Args: - operator: The operator to check for diagonality. - - Returns: - True, if the operator is diagonal, False otherwise. - - Raises: - OpflowError: If the operator is not diagonal. - """ - if isinstance(operator, PauliOp): - # every X component must be False - return not np.any(operator.primitive.x) - - # For sums (PauliSumOp and SummedOp), we cover the case of sums of diagonal paulis, but don't - # raise since there might be summand canceling the non-diagonal parts. That case is checked - # in the inefficient matrix check at the bottom. - if isinstance(operator, PauliSumOp): - if not np.any(operator.primitive.paulis.x): - return True - - elif isinstance(operator, SummedOp): - if all(isinstance(op, PauliOp) and not np.any(op.primitive.x) for op in operator.oplist): - return True - - elif isinstance(operator, ListOp): - return all(operator.traverse(_check_is_diagonal)) - - # cannot efficiently check if a operator is diagonal, converting to matrix - matrix = operator.to_matrix() - return np.all(matrix == np.diag(np.diagonal(matrix))) diff --git a/qiskit/opflow/state_fns/dict_state_fn.py b/qiskit/opflow/state_fns/dict_state_fn.py deleted file mode 100644 index 940de844748a..000000000000 --- a/qiskit/opflow/state_fns/dict_state_fn.py +++ /dev/null @@ -1,345 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""DictStateFn Class""" - -import itertools -import warnings -from typing import Dict, List, Optional, Set, Union, cast - -import numpy as np -from scipy import sparse - -from qiskit.circuit import ParameterExpression -from qiskit.opflow.exceptions import OpflowError -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.opflow.state_fns.vector_state_fn import VectorStateFn -from qiskit.quantum_info import Statevector -from qiskit.result import Result -from qiskit.utils import algorithm_globals -from qiskit.utils.deprecation import deprecate_func - - -class DictStateFn(StateFn): - """Deprecated: A class for state functions and measurements which are defined by a lookup table, - stored in a dict. - """ - - primitive: Dict[str, complex] - - # TODO allow normalization somehow? - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: Union[str, dict, Result] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False, - from_operator: bool = False, - ) -> None: - """ - Args: - primitive: The dict, single bitstring (if defining a basis sate), or Qiskit - Result, which defines the behavior of the underlying function. - coeff: A coefficient by which to multiply the state function. - is_measurement: Whether the StateFn is a measurement operator. - from_operator: if True the StateFn is derived from OperatorStateFn. (Default: False) - - Raises: - TypeError: invalid parameters. - """ - # If the initial density is a string, treat this as a density dict - # with only a single basis state. - if isinstance(primitive, str): - primitive = {primitive: 1} - - # NOTE: - # 1) This is not the same as passing in the counts dict directly, as this will - # convert the shot numbers to - # probabilities, whereas passing in the counts dict will not. - # 2) This will extract counts for both shot and statevector simulations. - # To use the statevector, - # simply pass in the statevector. - # 3) This will only extract the first result. - if isinstance(primitive, Result): - counts = primitive.get_counts() - # NOTE: Need to square root to take correct Pauli measurements! - primitive = { - bstr: (shots / sum(counts.values())) ** 0.5 for (bstr, shots) in counts.items() - } - - if not isinstance(primitive, dict): - raise TypeError( - "DictStateFn can only be instantiated with dict, " - "string, or Qiskit Result, not {}".format(type(primitive)) - ) - - super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) - self.from_operator = from_operator - - def primitive_strings(self) -> Set[str]: - return {"Dict"} - - @property - def num_qubits(self) -> int: - return len(next(iter(self.primitive))) - - @property - def settings(self) -> Dict: - """Return settings.""" - data = super().settings - data["from_operator"] = self.from_operator - return data - - def add(self, other: OperatorBase) -> OperatorBase: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over statefns with different numbers of qubits, {} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - # Right now doesn't make sense to add a StateFn to a Measurement - if isinstance(other, DictStateFn) and self.is_measurement == other.is_measurement: - # TODO add compatibility with vector and Operator? - if self.primitive == other.primitive: - return DictStateFn( - self.primitive, - coeff=self.coeff + other.coeff, - is_measurement=self.is_measurement, - ) - else: - new_dict = { - b: (v * self.coeff) + (other.primitive.get(b, 0) * other.coeff) - for (b, v) in self.primitive.items() - } - new_dict.update( - { - b: v * other.coeff - for (b, v) in other.primitive.items() - if b not in self.primitive - } - ) - return DictStateFn(new_dict, is_measurement=self._is_measurement) - # pylint: disable=cyclic-import - from ..list_ops.summed_op import SummedOp - - return SummedOp([self, other]) - - def adjoint(self) -> "DictStateFn": - return DictStateFn( - {b: np.conj(v) for (b, v) in self.primitive.items()}, - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement), - ) - - def permute(self, permutation: List[int]) -> "DictStateFn": - new_num_qubits = max(permutation) + 1 - if self.num_qubits != len(permutation): - raise OpflowError("New index must be defined for each qubit of the operator.") - - # helper function to permute the key - def perm(key): - list_key = ["0"] * new_num_qubits - for i, k in enumerate(permutation): - list_key[k] = key[i] - return "".join(list_key) - - new_dict = {perm(key): value for key, value in self.primitive.items()} - return DictStateFn(new_dict, coeff=self.coeff, is_measurement=self.is_measurement) - - def _expand_dim(self, num_qubits: int) -> "DictStateFn": - pad = "0" * num_qubits - new_dict = {key + pad: value for key, value in self.primitive.items()} - return DictStateFn(new_dict, coeff=self.coeff, is_measurement=self.is_measurement) - - def tensor(self, other: OperatorBase) -> OperatorBase: - # Both dicts - if isinstance(other, DictStateFn): - new_dict = { - k1 + k2: v1 * v2 - for ( - ( - k1, - v1, - ), - (k2, v2), - ) in itertools.product(self.primitive.items(), other.primitive.items()) - } - return StateFn( - new_dict, coeff=self.coeff * other.coeff, is_measurement=self.is_measurement - ) - # pylint: disable=cyclic-import - from ..list_ops.tensored_op import TensoredOp - - return TensoredOp([self, other]) - - def to_density_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_density_matrix", True, self.num_qubits, massive) - states = int(2**self.num_qubits) - return self.to_matrix(massive=massive) * np.eye(states) * self.coeff - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) - states = int(2**self.num_qubits) - probs = np.zeros(states) + 0.0j - for k, v in self.primitive.items(): - probs[int(k, 2)] = v - vec = probs * self.coeff - - # Reshape for measurements so np.dot still works for composition. - return vec if not self.is_measurement else vec.reshape(1, -1) - - def to_spmatrix(self) -> sparse.spmatrix: - """Same as to_matrix, but returns csr sparse matrix. - - Returns: - CSR sparse matrix representation of the State function. - - Raises: - ValueError: invalid parameters. - """ - - indices = [int(v, 2) for v in self.primitive.keys()] - vals = np.array(list(self.primitive.values())) * self.coeff - spvec = sparse.csr_matrix( - (vals, (np.zeros(len(indices), dtype=int), indices)), shape=(1, 2**self.num_qubits) - ) - return spvec if not self.is_measurement else spvec.transpose() - - def to_spmatrix_op(self) -> OperatorBase: - """Convert this state function to a ``SparseVectorStateFn``.""" - from .sparse_vector_state_fn import SparseVectorStateFn - - return SparseVectorStateFn(self.to_spmatrix(), self.coeff, self.is_measurement) - - def to_circuit_op(self) -> OperatorBase: - """Convert this state function to a ``CircuitStateFn``.""" - from .circuit_state_fn import CircuitStateFn - - csfn = CircuitStateFn.from_dict(self.primitive) * self.coeff - return csfn.adjoint() if self.is_measurement else csfn - - def __str__(self) -> str: - prim_str = str(self.primitive) - if self.coeff == 1.0: - return "{}({})".format( - "DictStateFn" if not self.is_measurement else "DictMeasurement", prim_str - ) - else: - return "{}({}) * {}".format( - "DictStateFn" if not self.is_measurement else "DictMeasurement", - prim_str, - self.coeff, - ) - - # pylint: disable=too-many-return-statements - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - if front is None: - sparse_vector_state_fn = self.to_spmatrix_op().eval() - return sparse_vector_state_fn - - if not self.is_measurement and isinstance(front, OperatorBase): - raise ValueError( - "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " - "sf.adjoint() first to convert to measurement." - ) - - if isinstance(front, ListOp) and front.distributive: - return front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - # For now, always do this. If it's not performant, we can be more granular. - if not isinstance(front, OperatorBase): - front = StateFn(front) - - # pylint: disable=cyclic-import - from ..operator_globals import EVAL_SIG_DIGITS - - # If the primitive is a lookup of bitstrings, - # we define all missing strings to have a function value of - # zero. - if isinstance(front, DictStateFn): - # If self is come from operator, it should be expanded as - # = . - front_coeff = ( - front.coeff * front.coeff.conjugate() if self.from_operator else front.coeff - ) - return np.round( - cast( - float, - sum(v * front.primitive.get(b, 0) for (b, v) in self.primitive.items()) - * self.coeff - * front_coeff, - ), - decimals=EVAL_SIG_DIGITS, - ) - - # All remaining possibilities only apply when self.is_measurement is True - - if isinstance(front, VectorStateFn): - # TODO does it need to be this way for measurement? - # return sum([v * front.primitive.data[int(b, 2)] * - # np.conj(front.primitive.data[int(b, 2)]) - return np.round( - cast( - float, - sum(v * front.primitive.data[int(b, 2)] for (b, v) in self.primitive.items()) - * self.coeff, - ), - decimals=EVAL_SIG_DIGITS, - ) - - from .circuit_state_fn import CircuitStateFn - - if isinstance(front, CircuitStateFn): - # Don't reimplement logic from CircuitStateFn - self_adjoint = cast(DictStateFn, self.adjoint()) - return np.conj(front.adjoint().eval(self_adjoint.primitive)) * self.coeff - - from .operator_state_fn import OperatorStateFn - - if isinstance(front, OperatorStateFn): - return cast(Union[OperatorBase, complex], front.adjoint().eval(self.adjoint())) - - # All other OperatorBases go here - self_adjoint = cast(DictStateFn, self.adjoint()) - adjointed_eval = cast(OperatorBase, front.adjoint().eval(self_adjoint.primitive)) - return adjointed_eval.adjoint() * self.coeff - - def sample( - self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False - ) -> Dict[str, float]: - probs = np.square(np.abs(np.array(list(self.primitive.values())))) - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - unique, counts = np.unique( - algorithm_globals.random.choice( - list(self.primitive.keys()), size=shots, p=(probs / sum(probs)) - ), - return_counts=True, - ) - counts = dict(zip(unique, counts)) - if reverse_endianness: - scaled_dict = {bstr[::-1]: (prob / shots) for (bstr, prob) in counts.items()} - else: - scaled_dict = {bstr: (prob / shots) for (bstr, prob) in counts.items()} - return dict(sorted(scaled_dict.items(), key=lambda x: x[1], reverse=True)) diff --git a/qiskit/opflow/state_fns/operator_state_fn.py b/qiskit/opflow/state_fns/operator_state_fn.py deleted file mode 100644 index a9cac679deef..000000000000 --- a/qiskit/opflow/state_fns/operator_state_fn.py +++ /dev/null @@ -1,259 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""OperatorStateFn Class""" - -from typing import List, Optional, Set, Union, cast - -import numpy as np - -from qiskit.circuit import ParameterExpression -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.list_ops.tensored_op import TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.primitive_ops.matrix_op import MatrixOp -from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.opflow.state_fns.circuit_state_fn import CircuitStateFn -from qiskit.quantum_info import Statevector -from qiskit.utils.deprecation import deprecate_func - - -class OperatorStateFn(StateFn): - r""" - Deprecated: A class for state functions and measurements which are defined by a density Operator, - stored using an ``OperatorBase``. - """ - primitive: OperatorBase - - # TODO allow normalization somehow? - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: OperatorBase, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False, - ) -> None: - """ - Args: - primitive: The ``OperatorBase`` which defines the behavior of the underlying State - function. - coeff: A coefficient by which to multiply the state function - is_measurement: Whether the StateFn is a measurement operator - """ - - super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) - - def primitive_strings(self) -> Set[str]: - return self.primitive.primitive_strings() - - @property - def num_qubits(self) -> int: - return self.primitive.num_qubits - - def add(self, other: OperatorBase) -> Union["OperatorStateFn", SummedOp]: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over statefns with different numbers of qubits, {} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - # Right now doesn't make sense to add a StateFn to a Measurement - if isinstance(other, OperatorStateFn) and self.is_measurement == other.is_measurement: - if isinstance(other.primitive, OperatorBase) and self.primitive == other.primitive: - return OperatorStateFn( - self.primitive, - coeff=self.coeff + other.coeff, - is_measurement=self.is_measurement, - ) - # Covers Statevector and custom. - elif isinstance(other, OperatorStateFn): - # Also assumes scalar multiplication is available - return OperatorStateFn( - (self.coeff * self.primitive).add(other.primitive * other.coeff), - is_measurement=self._is_measurement, - ) - - return SummedOp([self, other]) - - def adjoint(self) -> "OperatorStateFn": - return OperatorStateFn( - self.primitive.adjoint(), - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement), - ) - - def _expand_dim(self, num_qubits: int) -> "OperatorStateFn": - return OperatorStateFn( - self.primitive._expand_dim(num_qubits), - coeff=self.coeff, - is_measurement=self.is_measurement, - ) - - def permute(self, permutation: List[int]) -> "OperatorStateFn": - return OperatorStateFn( - self.primitive.permute(permutation), - coeff=self.coeff, - is_measurement=self.is_measurement, - ) - - def tensor(self, other: OperatorBase) -> Union["OperatorStateFn", TensoredOp]: - if isinstance(other, OperatorStateFn): - return OperatorStateFn( - self.primitive.tensor(other.primitive), - coeff=self.coeff * other.coeff, - is_measurement=self.is_measurement, - ) - - return TensoredOp([self, other]) - - def to_density_matrix(self, massive: bool = False) -> np.ndarray: - """Return numpy matrix of density operator, warn if more than 16 qubits - to force the user to set - massive=True if they want such a large matrix. Generally big methods like - this should require the use of a - converter, but in this case a convenience method for quick hacking and - access to classical tools is - appropriate.""" - OperatorBase._check_massive("to_density_matrix", True, self.num_qubits, massive) - return self.primitive.to_matrix() * self.coeff - - def to_matrix_op(self, massive: bool = False) -> "OperatorStateFn": - """Return a MatrixOp for this operator.""" - return OperatorStateFn( - self.primitive.to_matrix_op(massive=massive) * self.coeff, - is_measurement=self.is_measurement, - ) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - r""" - Note: this does not return a density matrix, it returns a classical matrix - containing the quantum or classical vector representing the evaluation of the state - function on each binary basis state. Do not assume this is is a normalized quantum or - classical probability vector. If we allowed this to return a density matrix, - then we would need to change the definition of composition to be ~Op @ StateFn @ Op for - those cases, whereas by this methodology we can ensure that composition always means Op - @ StateFn. - - Return numpy vector of state vector, warn if more than 16 qubits to force the user to set - massive=True if they want such a large vector. - - Args: - massive: Whether to allow large conversions, e.g. creating a matrix representing - over 16 qubits. - - Returns: - np.ndarray: Vector of state vector - - Raises: - ValueError: Invalid parameters. - """ - OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) - # Operator - return diagonal (real values, not complex), - # not rank 1 decomposition (statevector)! - mat = self.primitive.to_matrix(massive=massive) - # TODO change to weighted sum of eigenvectors' StateFns? - - # ListOp primitives can return lists of matrices (or trees for nested ListOps), - # so we need to recurse over the - # possible tree. - def diag_over_tree(op): - if isinstance(op, list): - return [diag_over_tree(o) for o in op] - else: - vec = np.diag(op) * self.coeff - # Reshape for measurements so np.dot still works for composition. - return vec if not self.is_measurement else vec.reshape(1, -1) - - return diag_over_tree(mat) - - def to_circuit_op(self): - r"""Return ``StateFnCircuit`` corresponding to this StateFn. Ignore for now because this is - undefined. TODO maybe call to_pauli_op and diagonalize here, but that could be very - inefficient, e.g. splitting one Stabilizer measurement into hundreds of 1 qubit Paulis.""" - raise NotImplementedError - - def __str__(self) -> str: - prim_str = str(self.primitive) - if self.coeff == 1.0: - return "{}({})".format( - "OperatorStateFn" if not self.is_measurement else "OperatorMeasurement", prim_str - ) - else: - return "{}({}) * {}".format( - "OperatorStateFn" if not self.is_measurement else "OperatorMeasurement", - prim_str, - self.coeff, - ) - - def eval( - self, front: Optional[Union[str, dict, np.ndarray, OperatorBase, Statevector]] = None - ) -> Union[OperatorBase, complex]: - if front is None: - matrix = cast(MatrixOp, self.primitive.to_matrix_op()).primitive.data - # pylint: disable=cyclic-import - from .vector_state_fn import VectorStateFn - - return VectorStateFn(matrix[0, :]) - - if not self.is_measurement and isinstance(front, OperatorBase): - raise ValueError( - "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " - "sf.adjoint() first to convert to measurement." - ) - - if not isinstance(front, OperatorBase): - front = StateFn(front) - - if isinstance(self.primitive, ListOp) and self.primitive.distributive: - evals = [ - OperatorStateFn(op, is_measurement=self.is_measurement).eval(front) - for op in self.primitive.oplist - ] - result = self.primitive.combo_fn(evals) - if isinstance(result, list): - multiplied = self.primitive.coeff * self.coeff * np.array(result) - return multiplied.tolist() - return result * self.coeff * self.primitive.coeff - - # pylint: disable=cyclic-import - from .vector_state_fn import VectorStateFn - - if isinstance(self.primitive, PauliSumOp) and isinstance(front, VectorStateFn): - return ( - front.primitive.expectation_value(self.primitive.primitive) - * self.coeff - * front.coeff - ) - - # Need an ListOp-specific carve-out here to make sure measurement over a ListOp doesn't - # produce two-dimensional ListOp from composing from both sides of primitive. - # Can't use isinstance because this would include subclasses. - # pylint: disable=unidiomatic-typecheck - if isinstance(front, ListOp) and type(front) == ListOp: - return front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - # If we evaluate against a circuit, evaluate it to a vector so we - # make sure to only do the expensive circuit simulation once - if isinstance(front, CircuitStateFn): - front = front.eval() - - return front.adjoint().eval(cast(OperatorBase, self.primitive.eval(front))) * self.coeff - - def sample(self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False): - raise NotImplementedError diff --git a/qiskit/opflow/state_fns/sparse_vector_state_fn.py b/qiskit/opflow/state_fns/sparse_vector_state_fn.py deleted file mode 100644 index b26c6dff9df1..000000000000 --- a/qiskit/opflow/state_fns/sparse_vector_state_fn.py +++ /dev/null @@ -1,234 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""SparseVectorStateFn class.""" - - -from typing import Dict, Optional, Set, Union - -import numpy as np -import scipy - -from qiskit.circuit import ParameterExpression -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.opflow.state_fns.vector_state_fn import VectorStateFn -from qiskit.quantum_info import Statevector -from qiskit.utils import algorithm_globals -from qiskit.utils.deprecation import deprecate_func - - -class SparseVectorStateFn(StateFn): - """Deprecated: A class for sparse state functions and measurements in vector representation. - - This class uses ``scipy.sparse.spmatrix`` for the internal representation. - """ - - primitive: scipy.sparse.spmatrix - - # TODO allow normalization somehow? - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: scipy.sparse.spmatrix, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False, - ) -> None: - """ - Args: - primitive: The underlying sparse vector. - coeff: A coefficient multiplying the state function. - is_measurement: Whether the StateFn is a measurement operator - - Raises: - ValueError: If the primitive is not a column vector. - ValueError: If the number of elements in the primitive is not a power of 2. - - """ - if primitive.shape[0] != 1: - raise ValueError("The primitive must be a row vector of shape (x, 1).") - - # check if the primitive is a statevector of 2^n elements - self._num_qubits = int(np.log2(primitive.shape[1])) - if np.log2(primitive.shape[1]) != self._num_qubits: - raise ValueError("The number of vector elements must be a power of 2.") - - super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) - - def primitive_strings(self) -> Set[str]: - return {"SparseVector"} - - @property - def num_qubits(self) -> int: - return self._num_qubits - - def add(self, other: OperatorBase) -> OperatorBase: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over statefns with different numbers of qubits, {} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - # Right now doesn't make sense to add a StateFn to a Measurement - if isinstance(other, SparseVectorStateFn) and self.is_measurement == other.is_measurement: - # Covers Statevector and custom. - added = self.coeff * self.primitive + other.coeff * other.primitive - return SparseVectorStateFn(added, is_measurement=self._is_measurement) - - return SummedOp([self, other]) - - def adjoint(self) -> "SparseVectorStateFn": - return SparseVectorStateFn( - self.primitive.conjugate(), - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement), - ) - - def equals(self, other: OperatorBase) -> bool: - if not isinstance(other, SparseVectorStateFn) or not self.coeff == other.coeff: - return False - - if self.primitive.shape != other.primitive.shape: - return False - - if self.primitive.count_nonzero() != other.primitive.count_nonzero(): - return False - - # equal if no elements are different (using != for efficiency) - return (self.primitive != other.primitive).nnz == 0 - - def to_dict_fn(self) -> StateFn: - """Convert this state function to a ``DictStateFn``. - - Returns: - A new DictStateFn equivalent to ``self``. - """ - from .dict_state_fn import DictStateFn - - num_qubits = self.num_qubits - dok = self.primitive.todok() - new_dict = {format(i[1], "b").zfill(num_qubits): v for i, v in dok.items()} - return DictStateFn(new_dict, coeff=self.coeff, is_measurement=self.is_measurement) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) - vec = self.primitive.toarray() * self.coeff - return vec if not self.is_measurement else vec.reshape(1, -1) - - def to_matrix_op(self, massive: bool = False) -> OperatorBase: - return VectorStateFn(self.to_matrix()) - - def to_spmatrix(self) -> OperatorBase: - return self - - def to_circuit_op(self) -> OperatorBase: - """Convert this state function to a ``CircuitStateFn``.""" - # pylint: disable=cyclic-import - from .circuit_state_fn import CircuitStateFn - - csfn = CircuitStateFn.from_vector(self.primitive) * self.coeff - return csfn.adjoint() if self.is_measurement else csfn - - def __str__(self) -> str: - prim_str = str(self.primitive) - if self.coeff == 1.0: - return "{}({})".format( - "SparseVectorStateFn" if not self.is_measurement else "MeasurementSparseVector", - prim_str, - ) - else: - return "{}({}) * {}".format( - "SparseVectorStateFn" if not self.is_measurement else "SparseMeasurementVector", - prim_str, - self.coeff, - ) - - # pylint: disable=too-many-return-statements - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, Statevector, OperatorBase] - ] = None, - ) -> Union[OperatorBase, complex]: - if front is None: - return self - - if not self.is_measurement and isinstance(front, OperatorBase): - raise ValueError( - "Cannot compute overlap with StateFn or Operator if not Measurement. " - "Try taking sf.adjoint() first to convert to measurement." - ) - - if isinstance(front, ListOp) and front.distributive: - return front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - if not isinstance(front, OperatorBase): - front = StateFn(front) - - # pylint: disable=cyclic-import - from ..operator_globals import EVAL_SIG_DIGITS - from .operator_state_fn import OperatorStateFn - from .circuit_state_fn import CircuitStateFn - from .dict_state_fn import DictStateFn - - if isinstance(front, DictStateFn): - return np.round( - sum( - v * self.primitive.data[int(b, 2)] * front.coeff - for (b, v) in front.primitive.items() - ) - * self.coeff, - decimals=EVAL_SIG_DIGITS, - ) - - if isinstance(front, VectorStateFn): - # Need to extract the element or np.array([1]) is returned. - return np.round( - np.dot(self.to_matrix(), front.to_matrix())[0], decimals=EVAL_SIG_DIGITS - ) - - if isinstance(front, CircuitStateFn): - # Don't reimplement logic from CircuitStateFn - return np.conj(front.adjoint().eval(self.adjoint().primitive)) * self.coeff - - if isinstance(front, OperatorStateFn): - return front.adjoint().eval(self.primitive) * self.coeff - - return front.adjoint().eval(self.adjoint().primitive).adjoint() * self.coeff # type: ignore - - def sample( - self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False - ) -> dict: - as_dict = self.to_dict_fn().primitive - all_states = sum(as_dict.keys()) - deterministic_counts = {key: value / all_states for key, value in as_dict.items()} - # Don't need to square because probabilities_dict already does. - probs = np.array(list(deterministic_counts.values())) - unique, counts = np.unique( - algorithm_globals.random.choice( - list(deterministic_counts.keys()), size=shots, p=(probs / sum(probs)) - ), - return_counts=True, - ) - counts = dict(zip(unique, counts)) - if reverse_endianness: - scaled_dict = {bstr[::-1]: (prob / shots) for (bstr, prob) in counts.items()} - else: - scaled_dict = {bstr: (prob / shots) for (bstr, prob) in counts.items()} - return dict(sorted(scaled_dict.items(), key=lambda x: x[1], reverse=True)) diff --git a/qiskit/opflow/state_fns/state_fn.py b/qiskit/opflow/state_fns/state_fn.py deleted file mode 100644 index f24592bdaf55..000000000000 --- a/qiskit/opflow/state_fns/state_fn.py +++ /dev/null @@ -1,459 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""StateFn Class""" - -from typing import Callable, Dict, List, Optional, Set, Tuple, Union - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import Instruction, ParameterExpression -from qiskit.opflow.operator_base import OperatorBase -from qiskit.quantum_info import Statevector -from qiskit.result import Result -from qiskit.utils.deprecation import deprecate_func - - -class StateFn(OperatorBase): - r""" - Deprecated: A class for representing state functions and measurements. - - State functions are defined to be complex functions over a single binary string (as - compared to an operator, which is defined as a function over two binary strings, or a - function taking a binary function to another binary function). This function may be - called by the eval() method. - - Measurements are defined to be functionals over StateFns, taking them to real values. - Generally, this real value is interpreted to represent the probability of some classical - state (binary string) being observed from a probabilistic or quantum system represented - by a StateFn. This leads to the equivalent definition, which is that a measurement m is - a function over binary strings producing StateFns, such that the probability of measuring - a given binary string b from a system with StateFn f is equal to the inner - product between f and m(b). - - NOTE: State functions here are not restricted to wave functions, as there is - no requirement of normalization. - """ - - def __init_subclass__(cls): - cls.__new__ = lambda cls, *args, **kwargs: super().__new__(cls) - - @staticmethod - # pylint: disable=unused-argument - def __new__( - cls, - primitive: Union[ - str, - dict, - Result, - list, - np.ndarray, - Statevector, - QuantumCircuit, - Instruction, - OperatorBase, - ] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False, - ) -> "StateFn": - """A factory method to produce the correct type of StateFn subclass - based on the primitive passed in. Primitive, coeff, and is_measurement arguments - are passed into subclass's init() as-is automatically by new(). - - Args: - primitive: The primitive which defines the behavior of the underlying State function. - coeff: A coefficient by which the state function is multiplied. - is_measurement: Whether the StateFn is a measurement operator - - Returns: - The appropriate StateFn subclass for ``primitive``. - - Raises: - TypeError: Unsupported primitive type passed. - """ - - # Prevents infinite recursion when subclasses are created - if cls.__name__ != StateFn.__name__: - return super().__new__(cls) - - # pylint: disable=cyclic-import - if isinstance(primitive, (str, dict, Result)): - from .dict_state_fn import DictStateFn - - return DictStateFn.__new__(DictStateFn) - - if isinstance(primitive, (list, np.ndarray, Statevector)): - from .vector_state_fn import VectorStateFn - - return VectorStateFn.__new__(VectorStateFn) - - if isinstance(primitive, (QuantumCircuit, Instruction)): - from .circuit_state_fn import CircuitStateFn - - return CircuitStateFn.__new__(CircuitStateFn) - - if isinstance(primitive, OperatorBase): - from .operator_state_fn import OperatorStateFn - - return OperatorStateFn.__new__(OperatorStateFn) - - raise TypeError( - "Unsupported primitive type {} passed into StateFn " - "factory constructor".format(type(primitive)) - ) - - # TODO allow normalization somehow? - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: Union[ - str, - dict, - Result, - list, - np.ndarray, - Statevector, - QuantumCircuit, - Instruction, - OperatorBase, - ] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False, - ) -> None: - """ - Args: - primitive: The primitive which defines the behavior of the underlying State function. - coeff: A coefficient by which the state function is multiplied. - is_measurement: Whether the StateFn is a measurement operator - """ - super().__init__() - self._primitive = primitive - self._is_measurement = is_measurement - self._coeff = coeff - - @property - def primitive(self): - """The primitive which defines the behavior of the underlying State function.""" - return self._primitive - - @property - def coeff(self) -> Union[complex, ParameterExpression]: - """A coefficient by which the state function is multiplied.""" - return self._coeff - - @property - def is_measurement(self) -> bool: - """Whether the StateFn object is a measurement Operator.""" - return self._is_measurement - - @property - def settings(self) -> Dict: - """Return settings.""" - return { - "primitive": self._primitive, - "coeff": self._coeff, - "is_measurement": self._is_measurement, - } - - def primitive_strings(self) -> Set[str]: - raise NotImplementedError - - @property - def num_qubits(self) -> int: - raise NotImplementedError - - def add(self, other: OperatorBase) -> OperatorBase: - raise NotImplementedError - - def adjoint(self) -> OperatorBase: - raise NotImplementedError - - def _expand_dim(self, num_qubits: int) -> "StateFn": - raise NotImplementedError - - def permute(self, permutation: List[int]) -> OperatorBase: - """Permute the qubits of the state function. - - Args: - permutation: A list defining where each qubit should be permuted. The qubit at index - j of the circuit should be permuted to position permutation[j]. - - Returns: - A new StateFn containing the permuted primitive. - """ - raise NotImplementedError - - def equals(self, other: OperatorBase) -> bool: - if not isinstance(other, type(self)) or not self.coeff == other.coeff: - return False - - return self.primitive == other.primitive - # Will return NotImplementedError if not supported - - def mul(self, scalar: Union[complex, ParameterExpression]) -> OperatorBase: - if not isinstance(scalar, (int, float, complex, ParameterExpression)): - raise ValueError( - "Operators can only be scalar multiplied by float or complex, not " - "{} of type {}.".format(scalar, type(scalar)) - ) - - if hasattr(self, "from_operator"): - return self.__class__( - self.primitive, - coeff=self.coeff * scalar, - is_measurement=self.is_measurement, - from_operator=self.from_operator, - ) - else: - return self.__class__( - self.primitive, coeff=self.coeff * scalar, is_measurement=self.is_measurement - ) - - def tensor(self, other: OperatorBase) -> OperatorBase: - r""" - Return tensor product between self and other, overloaded by ``^``. - Note: You must be conscious of Qiskit's big-endian bit printing - convention. Meaning, Plus.tensor(Zero) - produces a \|+⟩ on qubit 0 and a \|0⟩ on qubit 1, or \|+⟩⨂\|0⟩, but - would produce a QuantumCircuit like - - \|0⟩-- - \|+⟩-- - - Because Terra prints circuits and results with qubit 0 - at the end of the string or circuit. - - Args: - other: The ``OperatorBase`` to tensor product with self. - - Returns: - An ``OperatorBase`` equivalent to the tensor product of self and other. - """ - raise NotImplementedError - - def tensorpower(self, other: int) -> Union[OperatorBase, int]: - if not isinstance(other, int) or other <= 0: - raise TypeError("Tensorpower can only take positive int arguments") - temp = StateFn( - self.primitive, coeff=self.coeff, is_measurement=self.is_measurement - ) # type: OperatorBase - for _ in range(other - 1): - temp = temp.tensor(self) - return temp - - def _expand_shorter_operator_and_permute( - self, other: OperatorBase, permutation: Optional[List[int]] = None - ) -> Tuple[OperatorBase, OperatorBase]: - # pylint: disable=cyclic-import - from ..operator_globals import Zero - - if self == StateFn({"0": 1}, is_measurement=True): - # Zero is special - we'll expand it to the correct qubit number. - return StateFn("0" * other.num_qubits, is_measurement=True), other - elif other == Zero: - # Zero is special - we'll expand it to the correct qubit number. - return self, StateFn("0" * self.num_qubits) - - return super()._expand_shorter_operator_and_permute(other, permutation) - - def to_matrix(self, massive: bool = False) -> np.ndarray: - raise NotImplementedError - - def to_density_matrix(self, massive: bool = False) -> np.ndarray: - """Return matrix representing product of StateFn evaluated on pairs of basis states. - Overridden by child classes. - - Args: - massive: Whether to allow large conversions, e.g. creating a matrix representing - over 16 qubits. - - Returns: - The NumPy array representing the density matrix of the State function. - - Raises: - ValueError: If massive is set to False, and exponentially large computation is needed. - """ - raise NotImplementedError - - def compose( - self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False - ) -> OperatorBase: - r""" - Composition (Linear algebra-style: A@B(x) = A(B(x))) is not well defined for states - in the binary function model, but is well defined for measurements. - - Args: - other: The Operator to compose with self. - permutation: ``List[int]`` which defines permutation on other operator. - front: If front==True, return ``other.compose(self)``. - - Returns: - An Operator equivalent to the function composition of self and other. - - Raises: - ValueError: If self is not a measurement, it cannot be composed from the right. - """ - # TODO maybe allow outers later to produce density operators or projectors, but not yet. - if not self.is_measurement and not front: - raise ValueError( - "Composition with a Statefunction in the first operand is not defined." - ) - - new_self, other = self._expand_shorter_operator_and_permute(other, permutation) - - if front: - return other.compose(self) - # TODO maybe include some reduction here in the subclasses - vector and Op, op and Op, etc. - from ..primitive_ops.circuit_op import CircuitOp - - if self.primitive == {"0" * self.num_qubits: 1.0} and isinstance(other, CircuitOp): - # Returning CircuitStateFn - return StateFn( - other.primitive, is_measurement=self.is_measurement, coeff=self.coeff * other.coeff - ) - - from ..list_ops.composed_op import ComposedOp - - if isinstance(other, ComposedOp): - return ComposedOp([new_self] + other.oplist, coeff=new_self.coeff * other.coeff) - - return ComposedOp([new_self, other]) - - def power(self, exponent: int) -> OperatorBase: - """Compose with Self Multiple Times, undefined for StateFns. - - Args: - exponent: The number of times to compose self with self. - - Raises: - ValueError: This function is not defined for StateFns. - """ - raise ValueError("Composition power over Statefunctions or Measurements is not defined.") - - def __str__(self) -> str: - prim_str = str(self.primitive) - if self.coeff == 1.0: - return "{}({})".format( - "StateFunction" if not self.is_measurement else "Measurement", self.coeff - ) - else: - return "{}({}) * {}".format( - "StateFunction" if not self.is_measurement else "Measurement", self.coeff, prim_str - ) - - def __repr__(self) -> str: - return "{}({}, coeff={}, is_measurement={})".format( - self.__class__.__name__, repr(self.primitive), self.coeff, self.is_measurement - ) - - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, OperatorBase, Statevector] - ] = None, - ) -> Union[OperatorBase, complex]: - raise NotImplementedError - - @property - def parameters(self): - params = set() - if isinstance(self.primitive, (OperatorBase, QuantumCircuit)): - params.update(self.primitive.parameters) - if isinstance(self.coeff, ParameterExpression): - params.update(self.coeff.parameters) - return params - - def assign_parameters(self, param_dict: dict) -> OperatorBase: - param_value = self.coeff - if isinstance(self.coeff, ParameterExpression): - unrolled_dict = self._unroll_param_dict(param_dict) - if isinstance(unrolled_dict, list): - from ..list_ops.list_op import ListOp - - return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) - if self.coeff.parameters <= set(unrolled_dict.keys()): - binds = {param: unrolled_dict[param] for param in self.coeff.parameters} - param_value = float(self.coeff.bind(binds)) - return self.traverse(lambda x: x.assign_parameters(param_dict), coeff=param_value) - - # Try collapsing primitives where possible. Nothing to collapse here. - def reduce(self) -> OperatorBase: - return self - - def traverse( - self, convert_fn: Callable, coeff: Optional[Union[complex, ParameterExpression]] = None - ) -> OperatorBase: - r""" - Apply the convert_fn to the internal primitive if the primitive is an Operator (as in - the case of ``OperatorStateFn``). Otherwise do nothing. Used by converters. - - Args: - convert_fn: The function to apply to the internal OperatorBase. - coeff: A coefficient to multiply by after applying convert_fn. - If it is None, self.coeff is used instead. - - Returns: - The converted StateFn. - """ - if coeff is None: - coeff = self.coeff - - if isinstance(self.primitive, OperatorBase): - return StateFn( - convert_fn(self.primitive), coeff=coeff, is_measurement=self.is_measurement - ) - else: - return self - - def to_matrix_op(self, massive: bool = False) -> OperatorBase: - """Return a ``VectorStateFn`` for this ``StateFn``. - - Args: - massive: Whether to allow large conversions, e.g. creating a matrix representing - over 16 qubits. - - Returns: - A VectorStateFn equivalent to self. - """ - # pylint: disable=cyclic-import - from .vector_state_fn import VectorStateFn - - return VectorStateFn(self.to_matrix(massive=massive), is_measurement=self.is_measurement) - - def to_circuit_op(self) -> OperatorBase: - """Returns a ``CircuitOp`` equivalent to this Operator.""" - raise NotImplementedError - - # TODO to_dict_op - - def sample( - self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False - ) -> Dict[str, float]: - """Sample the state function as a normalized probability distribution. Returns dict of - bitstrings in order of probability, with values being probability. - - Args: - shots: The number of samples to take to approximate the State function. - massive: Whether to allow large conversions, e.g. creating a matrix representing - over 16 qubits. - reverse_endianness: Whether to reverse the endianness of the bitstrings in the return - dict to match Terra's big-endianness. - - Returns: - A dict containing pairs sampled strings from the State function and sampling - frequency divided by shots. - """ - raise NotImplementedError diff --git a/qiskit/opflow/state_fns/vector_state_fn.py b/qiskit/opflow/state_fns/vector_state_fn.py deleted file mode 100644 index 711d505612ca..000000000000 --- a/qiskit/opflow/state_fns/vector_state_fn.py +++ /dev/null @@ -1,259 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""VectorStateFn Class""" - -import warnings -from typing import Dict, List, Optional, Set, Union, cast - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import ParameterExpression -from qiskit.opflow.list_ops.list_op import ListOp -from qiskit.opflow.list_ops.summed_op import SummedOp -from qiskit.opflow.list_ops.tensored_op import TensoredOp -from qiskit.opflow.operator_base import OperatorBase -from qiskit.opflow.state_fns.state_fn import StateFn -from qiskit.quantum_info import Statevector -from qiskit.utils import algorithm_globals, arithmetic -from qiskit.utils.deprecation import deprecate_func - - -class VectorStateFn(StateFn): - """Deprecated: A class for state functions and measurements which are defined in vector - representation, and stored using Terra's ``Statevector`` class. - """ - - primitive: Statevector - - # TODO allow normalization somehow? - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", - ) - def __init__( - self, - primitive: Union[list, np.ndarray, Statevector] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False, - ) -> None: - """ - Args: - primitive: The ``Statevector``, NumPy array, or list, which defines the behavior of - the underlying function. - coeff: A coefficient multiplying the state function. - is_measurement: Whether the StateFn is a measurement operator - """ - # Lists and Numpy arrays representing statevectors are stored - # in Statevector objects for easier handling. - if isinstance(primitive, (np.ndarray, list)): - primitive = Statevector(primitive) - - super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) - - def primitive_strings(self) -> Set[str]: - return {"Vector"} - - @property - def num_qubits(self) -> int: - return len(self.primitive.dims()) - - def add(self, other: OperatorBase) -> OperatorBase: - if not self.num_qubits == other.num_qubits: - raise ValueError( - "Sum over statefns with different numbers of qubits, {} and {}, is not well " - "defined".format(self.num_qubits, other.num_qubits) - ) - - # Right now doesn't make sense to add a StateFn to a Measurement - if isinstance(other, VectorStateFn) and self.is_measurement == other.is_measurement: - # Covers Statevector and custom. - return VectorStateFn( - (self.coeff * self.primitive) + (other.primitive * other.coeff), - is_measurement=self._is_measurement, - ) - return SummedOp([self, other]) - - def adjoint(self) -> "VectorStateFn": - return VectorStateFn( - self.primitive.conjugate(), - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement), - ) - - def permute(self, permutation: List[int]) -> "VectorStateFn": - new_self = self - new_num_qubits = max(permutation) + 1 - - if self.num_qubits != len(permutation): - # raise OpflowError("New index must be defined for each qubit of the operator.") - pass - if self.num_qubits < new_num_qubits: - # pad the operator with identities - new_self = self._expand_dim(new_num_qubits - self.num_qubits) - qc = QuantumCircuit(new_num_qubits) - - # extend the permutation indices to match the size of the new matrix - permutation = ( - list(filter(lambda x: x not in permutation, range(new_num_qubits))) + permutation - ) - - # decompose permutation into sequence of transpositions - transpositions = arithmetic.transpositions(permutation) - for trans in transpositions: - qc.swap(trans[0], trans[1]) - - from ..primitive_ops.circuit_op import CircuitOp - - matrix = CircuitOp(qc).to_matrix() - vector = new_self.primitive.data - new_vector = cast(np.ndarray, matrix.dot(vector)) - return VectorStateFn( - primitive=new_vector, coeff=self.coeff, is_measurement=self.is_measurement - ) - - def to_dict_fn(self) -> StateFn: - """Creates the equivalent state function of type DictStateFn. - - Returns: - A new DictStateFn equivalent to ``self``. - """ - from .dict_state_fn import DictStateFn - - num_qubits = self.num_qubits - new_dict = {format(i, "b").zfill(num_qubits): v for i, v in enumerate(self.primitive.data)} - return DictStateFn(new_dict, coeff=self.coeff, is_measurement=self.is_measurement) - - def _expand_dim(self, num_qubits: int) -> "VectorStateFn": - primitive = np.zeros(2**num_qubits, dtype=complex) - return VectorStateFn( - self.primitive.tensor(primitive), coeff=self.coeff, is_measurement=self.is_measurement - ) - - def tensor(self, other: OperatorBase) -> OperatorBase: - if isinstance(other, VectorStateFn): - return StateFn( - self.primitive.tensor(other.primitive), - coeff=self.coeff * other.coeff, - is_measurement=self.is_measurement, - ) - return TensoredOp([self, other]) - - def to_density_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_density_matrix", True, self.num_qubits, massive) - return self.primitive.to_operator().data * self.coeff - - def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) - vec = self.primitive.data * self.coeff - return vec if not self.is_measurement else vec.reshape(1, -1) - - def to_matrix_op(self, massive: bool = False) -> OperatorBase: - return self - - def to_circuit_op(self) -> OperatorBase: - """Return ``StateFnCircuit`` corresponding to this StateFn.""" - # pylint: disable=cyclic-import - from .circuit_state_fn import CircuitStateFn - - csfn = CircuitStateFn.from_vector(self.primitive.data) * self.coeff - return csfn.adjoint() if self.is_measurement else csfn - - def __str__(self) -> str: - prim_str = str(self.primitive) - if self.coeff == 1.0: - return "{}({})".format( - "VectorStateFn" if not self.is_measurement else "MeasurementVector", prim_str - ) - else: - return "{}({}) * {}".format( - "VectorStateFn" if not self.is_measurement else "MeasurementVector", - prim_str, - self.coeff, - ) - - # pylint: disable=too-many-return-statements - def eval( - self, - front: Optional[ - Union[str, Dict[str, complex], np.ndarray, Statevector, OperatorBase] - ] = None, - ) -> Union[OperatorBase, complex]: - if front is None: # this object is already a VectorStateFn - return self - - if not self.is_measurement and isinstance(front, OperatorBase): - raise ValueError( - "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " - "sf.adjoint() first to convert to measurement." - ) - - if isinstance(front, ListOp) and front.distributive: - return front.combo_fn( - [self.eval(front.coeff * front_elem) for front_elem in front.oplist] - ) - - if not isinstance(front, OperatorBase): - front = StateFn(front) - - # pylint: disable=cyclic-import - from ..operator_globals import EVAL_SIG_DIGITS - from .operator_state_fn import OperatorStateFn - from .circuit_state_fn import CircuitStateFn - from .dict_state_fn import DictStateFn - - if isinstance(front, DictStateFn): - return np.round( - sum( - v * self.primitive.data[int(b, 2)] * front.coeff - for (b, v) in front.primitive.items() - ) - * self.coeff, - decimals=EVAL_SIG_DIGITS, - ) - - if isinstance(front, VectorStateFn): - # Need to extract the element or np.array([1]) is returned. - return np.round( - np.dot(self.to_matrix(), front.to_matrix())[0], decimals=EVAL_SIG_DIGITS - ) - - if isinstance(front, CircuitStateFn): - # Don't reimplement logic from CircuitStateFn - return np.conj(front.adjoint().eval(self.adjoint().primitive)) * self.coeff - - if isinstance(front, OperatorStateFn): - return front.adjoint().eval(self.primitive) * self.coeff - - return front.adjoint().eval(self.adjoint().primitive).adjoint() * self.coeff # type: ignore - - def sample( - self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False - ) -> dict: - deterministic_counts = self.primitive.probabilities_dict() - # Don't need to square because probabilities_dict already does. - probs = np.array(list(deterministic_counts.values())) - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - unique, counts = np.unique( - algorithm_globals.random.choice( - list(deterministic_counts.keys()), size=shots, p=(probs / sum(probs)) - ), - return_counts=True, - ) - counts = dict(zip(unique, counts)) - if reverse_endianness: - scaled_dict = {bstr[::-1]: (prob / shots) for (bstr, prob) in counts.items()} - else: - scaled_dict = {bstr: (prob / shots) for (bstr, prob) in counts.items()} - return dict(sorted(scaled_dict.items(), key=lambda x: x[1], reverse=True)) diff --git a/qiskit/opflow/utils.py b/qiskit/opflow/utils.py deleted file mode 100644 index 0979fc0bc3e7..000000000000 --- a/qiskit/opflow/utils.py +++ /dev/null @@ -1,116 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Utility functions for OperatorFlow""" - -from qiskit.opflow.operator_base import OperatorBase -from qiskit.utils.deprecation import deprecate_func - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", -) -def commutator(op_a: OperatorBase, op_b: OperatorBase) -> OperatorBase: - r""" - Deprecated: Compute commutator of `op_a` and `op_b`. - - .. math:: - - AB - BA. - - Args: - op_a: Operator A - op_b: Operator B - Returns: - OperatorBase: the commutator - """ - return (op_a @ op_b - op_b @ op_a).reduce() - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", -) -def anti_commutator(op_a: OperatorBase, op_b: OperatorBase) -> OperatorBase: - r""" - Deprecated: Compute anti-commutator of `op_a` and `op_b`. - - .. math:: - - AB + BA. - - Args: - op_a: Operator A - op_b: Operator B - Returns: - OperatorBase: the anti-commutator - """ - return (op_a @ op_b + op_b @ op_a).reduce() - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/opflow_migration.", -) -def double_commutator( - op_a: OperatorBase, - op_b: OperatorBase, - op_c: OperatorBase, - sign: bool = False, -) -> OperatorBase: - r""" - Deprecated: Compute symmetric double commutator of `op_a`, `op_b` and `op_c`. - See McWeeny chapter 13.6 Equation of motion methods (page 479) - - If `sign` is `False`, it returns - - .. math:: - - [[A, B], C]/2 + [A, [B, C]]/2 - = (2ABC + 2CBA - BAC - CAB - ACB - BCA)/2. - - If `sign` is `True`, it returns - - .. math:: - \lbrace[A, B], C\rbrace/2 + \lbrace A, [B, C]\rbrace/2 - = (2ABC - 2CBA - BAC + CAB - ACB + BCA)/2. - - Args: - op_a: Operator A - op_b: Operator B - op_c: Operator C - sign: False anti-commutes, True commutes - Returns: - OperatorBase: the double commutator - """ - sign_num = 1 if sign else -1 - - op_ab = op_a @ op_b - op_ba = op_b @ op_a - op_ac = op_a @ op_c - op_ca = op_c @ op_a - - op_abc = op_ab @ op_c - op_cba = op_c @ op_ba - op_bac = op_ba @ op_c - op_cab = op_c @ op_ab - op_acb = op_ac @ op_b - op_bca = op_b @ op_ca - - res = ( - op_abc - - sign_num * op_cba - + 0.5 * (-op_bac + sign_num * op_cab - op_acb + sign_num * op_bca) - ) - - return res.reduce() diff --git a/qiskit/passmanager/base_tasks.py b/qiskit/passmanager/base_tasks.py index 84a944d78042..64ebafa225da 100644 --- a/qiskit/passmanager/base_tasks.py +++ b/qiskit/passmanager/base_tasks.py @@ -19,7 +19,7 @@ from collections.abc import Iterable, Callable, Generator from typing import Any -from .compilation_status import RunState, PassManagerState +from .compilation_status import RunState, PassManagerState, PropertySet logger = logging.getLogger(__name__) @@ -62,6 +62,7 @@ class GenericPass(Task, ABC): """ def __init__(self): + self.property_set = PropertySet() self.requires: Iterable[Task] = [] def name(self) -> str: @@ -77,6 +78,7 @@ def execute( # Overriding this method is not safe. # Pass subclass must keep current implementation. # Especially, task execution may break when method signature is modified. + self.property_set = state.property_set if self.requires: # pylint: disable=cyclic-import diff --git a/qiskit/passmanager/flow_controllers.py b/qiskit/passmanager/flow_controllers.py index bf831b94b35c..eb06202e8673 100644 --- a/qiskit/passmanager/flow_controllers.py +++ b/qiskit/passmanager/flow_controllers.py @@ -133,6 +133,8 @@ def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerStat state = yield task if not self.do_while(state.property_set): return + # Remove stored tasks from the completed task collection for next loop + state.workflow_status.completed_passes.difference_update(self.tasks) raise PassManagerError("Maximum iteration reached. max_iteration=%i" % max_iteration) diff --git a/qiskit/passmanager/passmanager.py b/qiskit/passmanager/passmanager.py index fb9c8b523ba6..74d5feb91088 100644 --- a/qiskit/passmanager/passmanager.py +++ b/qiskit/passmanager/passmanager.py @@ -15,7 +15,7 @@ import logging from abc import ABC, abstractmethod -from collections.abc import Callable, Sequence, Iterable +from collections.abc import Callable, Iterable from itertools import chain from typing import Any @@ -169,14 +169,16 @@ def _passmanager_backend( def run( self, - in_programs: Any, + in_programs: Any | list[Any], callback: Callable = None, **kwargs, ) -> Any: - """Run all the passes on the specified ``circuits``. + """Run all the passes on the specified ``in_programs``. Args: in_programs: Input programs to transform via all the registered passes. + A single input object cannot be a Python builtin list object. + A list object is considered as multiple input objects to optimize. callback: A callback function that will be called after each pass execution. The function will be called with 4 keyword arguments:: @@ -212,7 +214,7 @@ def callback_func(**kwargs): return in_programs is_list = True - if not isinstance(in_programs, Sequence): + if not isinstance(in_programs, list): in_programs = [in_programs] is_list = False diff --git a/qiskit/primitives/backend_estimator.py b/qiskit/primitives/backend_estimator.py index 2ffe5bb7a989..eb97a3b95259 100644 --- a/qiskit/primitives/backend_estimator.py +++ b/qiskit/primitives/backend_estimator.py @@ -15,7 +15,6 @@ from __future__ import annotations -import typing from collections.abc import Sequence from itertools import accumulate @@ -41,9 +40,6 @@ from .primitive_job import PrimitiveJob from .utils import _circuit_key, _observable_key, init_observable -if typing.TYPE_CHECKING: - from qiskit.opflow import PauliSumOp - def _run_circuits( circuits: QuantumCircuit | list[QuantumCircuit], @@ -98,8 +94,7 @@ class BackendEstimator(BaseEstimator[PrimitiveJob[EstimatorResult]]): (or :class:`~.BackendV1`) object in the :class:`~.BaseEstimator` API. It facilitates using backends that do not provide a native :class:`~.BaseEstimator` implementation in places that work with - :class:`~.BaseEstimator`, such as algorithms in :mod:`qiskit.algorithms` - including :class:`~.qiskit.algorithms.minimum_eigensolvers.VQE`. However, + :class:`~.BaseEstimator`. However, if you're using a provider that has a native implementation of :class:`~.BaseEstimator`, it is a better choice to leverage that native implementation as it will likely include additional optimizations and be @@ -129,6 +124,9 @@ def __init__( will be directly executed when this object is called. """ super().__init__(options=options) + self._circuits = [] + self._parameters = [] + self._observables = [] self._abelian_grouping = abelian_grouping @@ -198,25 +196,18 @@ def _transpile(self): # 1. transpile a common circuit if self._skip_transpilation: transpiled_circuit = common_circuit.copy() - perm_pattern = list(range(common_circuit.num_qubits)) + final_index_layout = list(range(common_circuit.num_qubits)) else: transpiled_circuit = transpile( common_circuit, self.backend, **self.transpile_options.__dict__ ) if transpiled_circuit.layout is not None: - layout = transpiled_circuit.layout - virtual_bit_map = layout.initial_layout.get_virtual_bits() - perm_pattern = [virtual_bit_map[v] for v in common_circuit.qubits] - if layout.final_layout is not None: - final_mapping = dict( - enumerate(layout.final_layout.get_virtual_bits().values()) - ) - perm_pattern = [final_mapping[i] for i in perm_pattern] + final_index_layout = transpiled_circuit.layout.final_index_layout() else: - perm_pattern = list(range(transpiled_circuit.num_qubits)) + final_index_layout = list(range(transpiled_circuit.num_qubits)) # 2. transpile diff circuits - passmanager = _passmanager_for_measurement_circuits(perm_pattern, self.backend) + passmanager = _passmanager_for_measurement_circuits(final_index_layout, self.backend) diff_circuits = passmanager.run(diff_circuits) # 3. combine transpiled_circuits = [] @@ -273,7 +264,7 @@ def _call( def _run( self, circuits: tuple[QuantumCircuit, ...], - observables: tuple[BaseOperator | PauliSumOp, ...], + observables: tuple[BaseOperator, ...], parameter_values: tuple[tuple[float, ...], ...], **run_options, ): diff --git a/qiskit/primitives/backend_sampler.py b/qiskit/primitives/backend_sampler.py index 6510b981b68a..140a3091f34a 100644 --- a/qiskit/primitives/backend_sampler.py +++ b/qiskit/primitives/backend_sampler.py @@ -38,8 +38,7 @@ class BackendSampler(BaseSampler[PrimitiveJob[SamplerResult]]): any measurement mitigation, it just computes the probability distribution from the counts. It facilitates using backends that do not provide a native :class:`~.BaseSampler` implementation in places that work with - :class:`~.BaseSampler`, such as algorithms in :mod:`qiskit.algorithms` - including :class:`~.qiskit.algorithms.minimum_eigensolvers.SamplingVQE`. + :class:`~.BaseSampler`. However, if you're using a provider that has a native implementation of :class:`~.BaseSampler`, it is a better choice to leverage that native implementation as it will likely include additional optimizations and be @@ -69,6 +68,8 @@ def __init__( """ super().__init__(options=options) + self._circuits = [] + self._parameters = [] self._backend = backend self._transpile_options = Options() self._bound_pass_manager = bound_pass_manager diff --git a/qiskit/primitives/base/base_estimator.py b/qiskit/primitives/base/base_estimator.py index 1dec010d7b12..93b497f02049 100644 --- a/qiskit/primitives/base/base_estimator.py +++ b/qiskit/primitives/base/base_estimator.py @@ -80,23 +80,21 @@ from __future__ import annotations +import warnings from abc import abstractmethod from collections.abc import Sequence from copy import copy from typing import Generic, TypeVar -import typing +from qiskit.utils.deprecation import deprecate_func from qiskit.circuit import QuantumCircuit from qiskit.circuit.parametertable import ParameterView from qiskit.providers import JobV1 as Job from qiskit.quantum_info.operators import SparsePauliOp from qiskit.quantum_info.operators.base_operator import BaseOperator -from ..utils import init_observable from .base_primitive import BasePrimitive - -if typing.TYPE_CHECKING: - from qiskit.opflow import PauliSumOp +from . import validation T = TypeVar("T", bound=Job) @@ -121,15 +119,33 @@ def __init__( Args: options: Default options. """ - self._circuits = [] - self._observables = [] - self._parameters = [] super().__init__(options) + def __getattr__(self, name: str) -> any: + # Work around to enable deprecation of the init attributes in BaseEstimator incase + # existing subclasses depend on them (which some do) + dep_defaults = { + "_circuits": [], + "_observables": [], + "_parameters": [], + } + if name not in dep_defaults: + raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") + + warnings.warn( + f"The init attribute `{name}` in BaseEstimator is deprecated as of Qiskit 0.46." + " To continue to use this attribute in a subclass and avoid this warning the" + " subclass should initialize it itself.", + DeprecationWarning, + stacklevel=2, + ) + setattr(self, name, dep_defaults[name]) + return getattr(self, name) + def run( self, circuits: Sequence[QuantumCircuit] | QuantumCircuit, - observables: Sequence[BaseOperator | PauliSumOp | str] | BaseOperator | PauliSumOp | str, + observables: Sequence[BaseOperator | str] | BaseOperator | str, parameter_values: Sequence[Sequence[float]] | Sequence[float] | float | None = None, **run_options, ) -> T: @@ -169,18 +185,11 @@ def run( TypeError: Invalid argument type given. ValueError: Invalid argument values given. """ - # Singular validation - circuits = self._validate_circuits(circuits) - observables = self._validate_observables(observables) - parameter_values = self._validate_parameter_values( - parameter_values, - default=[()] * len(circuits), + # Validation + circuits, observables, parameter_values = validation._validate_estimator_args( + circuits, observables, parameter_values ) - # Cross-validation - self._cross_validate_circuits_parameter_values(circuits, parameter_values) - self._cross_validate_circuits_observables(circuits, observables) - # Options run_opts = copy(self.options) run_opts.update_options(**run_options) @@ -200,36 +209,24 @@ def _run( parameter_values: tuple[tuple[float, ...], ...], **run_options, ) -> T: - raise NotImplementedError("The subclass of BaseEstimator must implment `_run` method.") + raise NotImplementedError("The subclass of BaseEstimator must implement `_run` method.") @staticmethod + @deprecate_func(since="0.46.0") def _validate_observables( - observables: Sequence[BaseOperator | PauliSumOp | str] | BaseOperator | PauliSumOp | str, + observables: Sequence[BaseOperator | str] | BaseOperator | str, ) -> tuple[SparsePauliOp, ...]: - if isinstance(observables, str) or not isinstance(observables, Sequence): - observables = (observables,) - if len(observables) == 0: - raise ValueError("No observables were provided.") - return tuple(init_observable(obs) for obs in observables) + return validation._validate_observables(observables) @staticmethod + @deprecate_func(since="0.46.0") def _cross_validate_circuits_observables( - circuits: tuple[QuantumCircuit, ...], observables: tuple[BaseOperator | PauliSumOp, ...] + circuits: tuple[QuantumCircuit, ...], observables: tuple[BaseOperator, ...] ) -> None: - if len(circuits) != len(observables): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of observables ({len(observables)})." - ) - for i, (circuit, observable) in enumerate(zip(circuits, observables)): - if circuit.num_qubits != observable.num_qubits: - raise ValueError( - f"The number of qubits of the {i}-th circuit ({circuit.num_qubits}) does " - f"not match the number of qubits of the {i}-th observable " - f"({observable.num_qubits})." - ) + return validation._cross_validate_circuits_observables(circuits, observables) @property + @deprecate_func(since="0.46.0", is_property=True) def circuits(self) -> tuple[QuantumCircuit, ...]: """Quantum circuits that represents quantum states. @@ -239,6 +236,7 @@ def circuits(self) -> tuple[QuantumCircuit, ...]: return tuple(self._circuits) @property + @deprecate_func(since="0.46.0", is_property=True) def observables(self) -> tuple[SparsePauliOp, ...]: """Observables to be estimated. @@ -248,6 +246,7 @@ def observables(self) -> tuple[SparsePauliOp, ...]: return tuple(self._observables) @property + @deprecate_func(since="0.46.0", is_property=True) def parameters(self) -> tuple[ParameterView, ...]: """Parameters of the quantum circuits. diff --git a/qiskit/primitives/base/base_primitive.py b/qiskit/primitives/base/base_primitive.py index a2f8dacdb828..c161ca8094fa 100644 --- a/qiskit/primitives/base/base_primitive.py +++ b/qiskit/primitives/base/base_primitive.py @@ -17,10 +17,11 @@ from abc import ABC from collections.abc import Sequence -import numpy as np - from qiskit.circuit import QuantumCircuit from qiskit.providers import Options +from qiskit.utils.deprecation import deprecate_func + +from . import validation class BasePrimitive(ABC): @@ -49,83 +50,25 @@ def set_options(self, **fields): self._run_options.update_options(**fields) @staticmethod + @deprecate_func(since="0.46.0") def _validate_circuits( circuits: Sequence[QuantumCircuit] | QuantumCircuit, ) -> tuple[QuantumCircuit, ...]: - if isinstance(circuits, QuantumCircuit): - circuits = (circuits,) - elif not isinstance(circuits, Sequence) or not all( - isinstance(cir, QuantumCircuit) for cir in circuits - ): - raise TypeError("Invalid circuits, expected Sequence[QuantumCircuit].") - elif not isinstance(circuits, tuple): - circuits = tuple(circuits) - if len(circuits) == 0: - raise ValueError("No circuits were provided.") - return circuits + return validation._validate_circuits(circuits) @staticmethod + @deprecate_func(since="0.46.0") def _validate_parameter_values( parameter_values: Sequence[Sequence[float]] | Sequence[float] | float | None, default: Sequence[Sequence[float]] | Sequence[float] | None = None, ) -> tuple[tuple[float, ...], ...]: - # Allow optional (if default) - if parameter_values is None: - if default is None: - raise ValueError("No default `parameter_values`, optional input disallowed.") - parameter_values = default - - # Support numpy ndarray - if isinstance(parameter_values, np.ndarray): - parameter_values = parameter_values.tolist() - elif isinstance(parameter_values, Sequence): - parameter_values = tuple( - vector.tolist() if isinstance(vector, np.ndarray) else vector - for vector in parameter_values - ) - - # Allow single value - if _isreal(parameter_values): - parameter_values = ((parameter_values,),) - elif isinstance(parameter_values, Sequence) and not any( - isinstance(vector, Sequence) for vector in parameter_values - ): - parameter_values = (parameter_values,) - - # Validation - if ( - not isinstance(parameter_values, Sequence) - or not all(isinstance(vector, Sequence) for vector in parameter_values) - or not all(all(_isreal(value) for value in vector) for vector in parameter_values) - ): - raise TypeError("Invalid parameter values, expected Sequence[Sequence[float]].") - - return tuple(tuple(float(value) for value in vector) for vector in parameter_values) + return validation._validate_parameter_values(parameter_values, default=default) @staticmethod + @deprecate_func(since="0.46.0") def _cross_validate_circuits_parameter_values( circuits: tuple[QuantumCircuit, ...], parameter_values: tuple[tuple[float, ...], ...] ) -> None: - if len(circuits) != len(parameter_values): - raise ValueError( - f"The number of circuits ({len(circuits)}) does not match " - f"the number of parameter value sets ({len(parameter_values)})." - ) - for i, (circuit, vector) in enumerate(zip(circuits, parameter_values)): - if len(vector) != circuit.num_parameters: - raise ValueError( - f"The number of values ({len(vector)}) does not match " - f"the number of parameters ({circuit.num_parameters}) for the {i}-th circuit." - ) - - -def _isint(obj: Sequence[Sequence[float]] | Sequence[float] | float) -> bool: - """Check if object is int.""" - int_types = (int, np.integer) - return isinstance(obj, int_types) and not isinstance(obj, bool) - - -def _isreal(obj: Sequence[Sequence[float]] | Sequence[float] | float) -> bool: - """Check if object is a real number: int or float except ``±Inf`` and ``NaN``.""" - float_types = (float, np.floating) - return _isint(obj) or isinstance(obj, float_types) and float("-Inf") < obj < float("Inf") + return validation._cross_validate_circuits_parameter_values( + circuits, parameter_values=parameter_values + ) diff --git a/qiskit/primitives/base/base_sampler.py b/qiskit/primitives/base/base_sampler.py index fb6cf557ca59..d21487261091 100644 --- a/qiskit/primitives/base/base_sampler.py +++ b/qiskit/primitives/base/base_sampler.py @@ -75,16 +75,19 @@ from __future__ import annotations +import warnings from abc import abstractmethod from collections.abc import Sequence from copy import copy from typing import Generic, TypeVar -from qiskit.circuit import ControlFlowOp, Measure, QuantumCircuit +from qiskit.utils.deprecation import deprecate_func +from qiskit.circuit import QuantumCircuit from qiskit.circuit.parametertable import ParameterView from qiskit.providers import JobV1 as Job from .base_primitive import BasePrimitive +from . import validation T = TypeVar("T", bound=Job) @@ -106,10 +109,28 @@ def __init__( Args: options: Default options. """ - self._circuits = [] - self._parameters = [] super().__init__(options) + def __getattr__(self, name: str) -> any: + # Work around to enable deprecation of the init attributes in BaseSampler incase + # existing subclasses depend on them (which some do) + dep_defaults = { + "_circuits": [], + "_parameters": [], + } + if name not in dep_defaults: + raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") + + warnings.warn( + f"The init attribute `{name}` in BaseSampler is deprecated as of Qiskit 0.46." + " To continue to use this attribute in a subclass and avoid this warning the" + " subclass should initialize it itself.", + DeprecationWarning, + stacklevel=2, + ) + setattr(self, name, dep_defaults[name]) + return getattr(self, name) + def run( self, circuits: QuantumCircuit | Sequence[QuantumCircuit], @@ -130,15 +151,8 @@ def run( Raises: ValueError: Invalid arguments are given. """ - # Singular validation - circuits = self._validate_circuits(circuits) - parameter_values = self._validate_parameter_values( - parameter_values, - default=[()] * len(circuits), - ) - - # Cross-validation - self._cross_validate_circuits_parameter_values(circuits, parameter_values) + # Validation + circuits, parameter_values = validation._validate_sampler_args(circuits, parameter_values) # Options run_opts = copy(self.options) @@ -157,29 +171,18 @@ def _run( parameter_values: tuple[tuple[float, ...], ...], **run_options, ) -> T: - raise NotImplementedError("The subclass of BaseSampler must implment `_run` method.") + raise NotImplementedError("The subclass of BaseSampler must implement `_run` method.") @classmethod + @deprecate_func(since="0.46.0") def _validate_circuits( cls, circuits: Sequence[QuantumCircuit] | QuantumCircuit, ) -> tuple[QuantumCircuit, ...]: - circuits = super()._validate_circuits(circuits) - for i, circuit in enumerate(circuits): - if circuit.num_clbits == 0: - raise ValueError( - f"The {i}-th circuit does not have any classical bit. " - "Sampler requires classical bits, plus measurements " - "on the desired qubits." - ) - if not _has_measure(circuit): - raise ValueError( - f"The {i}-th circuit does not have Measure instruction. " - "Without measurements, the circuit cannot be sampled from." - ) - return circuits + return validation._validate_circuits(circuits, requires_measure=True) @property + @deprecate_func(since="0.46.0", is_property=True) def circuits(self) -> tuple[QuantumCircuit, ...]: """Quantum circuits to be sampled. @@ -189,6 +192,7 @@ def circuits(self) -> tuple[QuantumCircuit, ...]: return tuple(self._circuits) @property + @deprecate_func(since="0.46.0", is_property=True) def parameters(self) -> tuple[ParameterView, ...]: """Parameters of quantum circuits. @@ -196,14 +200,3 @@ def parameters(self) -> tuple[ParameterView, ...]: List of the parameters in each quantum circuit. """ return tuple(self._parameters) - - -def _has_measure(circuit: QuantumCircuit) -> bool: - for instruction in reversed(circuit): - if isinstance(instruction.operation, Measure): - return True - elif isinstance(instruction.operation, ControlFlowOp): - for block in instruction.operation.blocks: - if _has_measure(block): - return True - return False diff --git a/qiskit/primitives/base/validation.py b/qiskit/primitives/base/validation.py new file mode 100644 index 000000000000..7e1bccf2f89c --- /dev/null +++ b/qiskit/primitives/base/validation.py @@ -0,0 +1,231 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Primitive validation methods. + +Note that these are not intended to be part of the public API of base primitives +but are here for backwards compatibility with deprecated functions. +""" + +from __future__ import annotations + +from collections.abc import Sequence +import typing +import numpy as np + +from qiskit.circuit import QuantumCircuit, ControlFlowOp, Measure +from qiskit.quantum_info.operators import SparsePauliOp +from qiskit.quantum_info.operators.base_operator import BaseOperator + +from ..utils import init_observable + +if typing.TYPE_CHECKING: + from qiskit.opflow import PauliSumOp + + +def _validate_estimator_args( + circuits: Sequence[QuantumCircuit] | QuantumCircuit, + observables: Sequence[BaseOperator | PauliSumOp | str] | BaseOperator | PauliSumOp | str, + parameter_values: Sequence[Sequence[float]] | Sequence[float] | float | None = None, +) -> tuple[tuple[QuantumCircuit], tuple[BaseOperator], tuple[tuple[float]]]: + """Validate run arguments for a reference Estimator. + + Args: + circuits: one or more circuit objects. + observables: one or more observable objects. + parameter_values: concrete parameters to be bound. + + Returns: + The formatted arguments ``(circuits, observables, parameter_values)``. + + Raises: + TypeError: If input arguments are invalid types. + ValueError: if input arguments are invalid values. + """ + # Singular validation + circuits = _validate_circuits(circuits) + observables = _validate_observables(observables) + parameter_values = _validate_parameter_values( + parameter_values, + default=[()] * len(circuits), + ) + + # Cross-validation + _cross_validate_circuits_parameter_values(circuits, parameter_values) + _cross_validate_circuits_observables(circuits, observables) + + return circuits, observables, parameter_values + + +def _validate_sampler_args( + circuits: Sequence[QuantumCircuit] | QuantumCircuit, + parameter_values: Sequence[Sequence[float]] | Sequence[float] | float | None = None, +) -> tuple[tuple[QuantumCircuit], tuple[BaseOperator], tuple[tuple[float]]]: + """Validate run arguments for a reference Sampler. + + Args: + circuits: one or more circuit objects. + parameter_values: concrete parameters to be bound. + + Returns: + The formatted arguments ``(circuits, parameter_values)``. + + Raises: + TypeError: If input arguments are invalid types. + ValueError: if input arguments are invalid values. + """ + # Singular validation + circuits = _validate_circuits(circuits, requires_measure=True) + parameter_values = _validate_parameter_values( + parameter_values, + default=[()] * len(circuits), + ) + + # Cross-validation + _cross_validate_circuits_parameter_values(circuits, parameter_values) + + return circuits, parameter_values + + +def _validate_circuits( + circuits: Sequence[QuantumCircuit] | QuantumCircuit, + requires_measure: bool = False, +) -> tuple[QuantumCircuit, ...]: + if isinstance(circuits, QuantumCircuit): + circuits = (circuits,) + elif not isinstance(circuits, Sequence) or not all( + isinstance(cir, QuantumCircuit) for cir in circuits + ): + raise TypeError("Invalid circuits, expected Sequence[QuantumCircuit].") + elif not isinstance(circuits, tuple): + circuits = tuple(circuits) + if len(circuits) == 0: + raise ValueError("No circuits were provided.") + + if requires_measure: + for i, circuit in enumerate(circuits): + if circuit.num_clbits == 0: + raise ValueError( + f"The {i}-th circuit does not have any classical bit. " + "Sampler requires classical bits, plus measurements " + "on the desired qubits." + ) + if not _has_measure(circuit): + raise ValueError( + f"The {i}-th circuit does not have Measure instruction. " + "Without measurements, the circuit cannot be sampled from." + ) + return circuits + + +def _validate_parameter_values( + parameter_values: Sequence[Sequence[float]] | Sequence[float] | float | None, + default: Sequence[Sequence[float]] | Sequence[float] | None = None, +) -> tuple[tuple[float, ...], ...]: + # Allow optional (if default) + if parameter_values is None: + if default is None: + raise ValueError("No default `parameter_values`, optional input disallowed.") + parameter_values = default + + # Support numpy ndarray + if isinstance(parameter_values, np.ndarray): + parameter_values = parameter_values.tolist() + elif isinstance(parameter_values, Sequence): + parameter_values = tuple( + vector.tolist() if isinstance(vector, np.ndarray) else vector + for vector in parameter_values + ) + + # Allow single value + if _isreal(parameter_values): + parameter_values = ((parameter_values,),) + elif isinstance(parameter_values, Sequence) and not any( + isinstance(vector, Sequence) for vector in parameter_values + ): + parameter_values = (parameter_values,) + + # Validation + if ( + not isinstance(parameter_values, Sequence) + or not all(isinstance(vector, Sequence) for vector in parameter_values) + or not all(all(_isreal(value) for value in vector) for vector in parameter_values) + ): + raise TypeError("Invalid parameter values, expected Sequence[Sequence[float]].") + + return tuple(tuple(float(value) for value in vector) for vector in parameter_values) + + +def _validate_observables( + observables: Sequence[BaseOperator | PauliSumOp | str] | BaseOperator | PauliSumOp | str, +) -> tuple[SparsePauliOp, ...]: + if isinstance(observables, str) or not isinstance(observables, Sequence): + observables = (observables,) + if len(observables) == 0: + raise ValueError("No observables were provided.") + return tuple(init_observable(obs) for obs in observables) + + +def _cross_validate_circuits_parameter_values( + circuits: tuple[QuantumCircuit, ...], parameter_values: tuple[tuple[float, ...], ...] +) -> None: + if len(circuits) != len(parameter_values): + raise ValueError( + f"The number of circuits ({len(circuits)}) does not match " + f"the number of parameter value sets ({len(parameter_values)})." + ) + for i, (circuit, vector) in enumerate(zip(circuits, parameter_values)): + if len(vector) != circuit.num_parameters: + raise ValueError( + f"The number of values ({len(vector)}) does not match " + f"the number of parameters ({circuit.num_parameters}) for the {i}-th circuit." + ) + + +def _cross_validate_circuits_observables( + circuits: tuple[QuantumCircuit, ...], observables: tuple[BaseOperator | PauliSumOp, ...] +) -> None: + if len(circuits) != len(observables): + raise ValueError( + f"The number of circuits ({len(circuits)}) does not match " + f"the number of observables ({len(observables)})." + ) + for i, (circuit, observable) in enumerate(zip(circuits, observables)): + if circuit.num_qubits != observable.num_qubits: + raise ValueError( + f"The number of qubits of the {i}-th circuit ({circuit.num_qubits}) does " + f"not match the number of qubits of the {i}-th observable " + f"({observable.num_qubits})." + ) + + +def _isint(obj: Sequence[Sequence[float]] | Sequence[float] | float) -> bool: + """Check if object is int.""" + int_types = (int, np.integer) + return isinstance(obj, int_types) and not isinstance(obj, bool) + + +def _isreal(obj: Sequence[Sequence[float]] | Sequence[float] | float) -> bool: + """Check if object is a real number: int or float except ``±Inf`` and ``NaN``.""" + float_types = (float, np.floating) + return _isint(obj) or isinstance(obj, float_types) and float("-Inf") < obj < float("Inf") + + +def _has_measure(circuit: QuantumCircuit) -> bool: + for instruction in reversed(circuit): + if isinstance(instruction.operation, Measure): + return True + elif isinstance(instruction.operation, ControlFlowOp): + for block in instruction.operation.blocks: + if _has_measure(block): + return True + return False diff --git a/qiskit/primitives/estimator.py b/qiskit/primitives/estimator.py index 52e519e56c29..9edfe35d7eb2 100644 --- a/qiskit/primitives/estimator.py +++ b/qiskit/primitives/estimator.py @@ -17,7 +17,6 @@ from collections.abc import Sequence from typing import Any -import typing import numpy as np @@ -35,9 +34,6 @@ init_observable, ) -if typing.TYPE_CHECKING: - from qiskit.opflow import PauliSumOp - class Estimator(BaseEstimator[PrimitiveJob[EstimatorResult]]): """ @@ -64,6 +60,9 @@ def __init__(self, *, options: dict | None = None): QiskitError: if some classical bits are not used for measurements. """ super().__init__(options=options) + self._circuits = [] + self._parameters = [] + self._observables = [] self._circuit_ids = {} self._observable_ids = {} @@ -127,7 +126,7 @@ def _call( def _run( self, circuits: tuple[QuantumCircuit, ...], - observables: tuple[BaseOperator | PauliSumOp, ...], + observables: tuple[BaseOperator, ...], parameter_values: tuple[tuple[float, ...], ...], **run_options, ): diff --git a/qiskit/primitives/sampler.py b/qiskit/primitives/sampler.py index c96f7b886c1c..9ffe42165d96 100644 --- a/qiskit/primitives/sampler.py +++ b/qiskit/primitives/sampler.py @@ -61,6 +61,8 @@ def __init__(self, *, options: dict | None = None): QiskitError: if some classical bits are not used for measurements. """ super().__init__(options=options) + self._circuits = [] + self._parameters = [] self._qargs_list = [] self._circuit_ids = {} diff --git a/qiskit/primitives/utils.py b/qiskit/primitives/utils.py index 611fbe86662c..75d25abbbe30 100644 --- a/qiskit/primitives/utils.py +++ b/qiskit/primitives/utils.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -14,22 +14,18 @@ """ from __future__ import annotations -import sys -import typing +import warnings from collections.abc import Iterable import numpy as np -from qiskit.circuit import Instruction, ParameterExpression, QuantumCircuit +from qiskit.circuit import Instruction, QuantumCircuit from qiskit.circuit.bit import Bit from qiskit.circuit.library.data_preparation import Initialize -from qiskit.quantum_info import SparsePauliOp, Statevector +from qiskit.quantum_info import SparsePauliOp, Statevector, PauliList from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.quantum_info.operators.symplectic.base_pauli import BasePauli -if typing.TYPE_CHECKING: - from qiskit.opflow import PauliSumOp - def init_circuit(state: QuantumCircuit | Statevector) -> QuantumCircuit: """Initialize state by converting the input to a quantum circuit. @@ -49,7 +45,7 @@ def init_circuit(state: QuantumCircuit | Statevector) -> QuantumCircuit: return qc -def init_observable(observable: BaseOperator | PauliSumOp | str) -> SparsePauliOp: +def init_observable(observable: BaseOperator | str) -> SparsePauliOp: """Initialize observable by converting the input to a :class:`~qiskit.quantum_info.SparsePauliOp`. Args: @@ -58,30 +54,22 @@ def init_observable(observable: BaseOperator | PauliSumOp | str) -> SparsePauliO Returns: The observable as :class:`~qiskit.quantum_info.SparsePauliOp`. - Raises: - TypeError: If the observable is a :class:`~qiskit.opflow.PauliSumOp` and has a parameterized - coefficient. """ - # This dance is to avoid importing the deprecated `qiskit.opflow` if the user hasn't already - # done so. They can't hold a `qiskit.opflow.PauliSumOp` if `qiskit.opflow` hasn't been - # imported, and we don't want unrelated Qiskit library code to be responsible for the first - # import, so the deprecation warnings will show. - if "qiskit.opflow" in sys.modules: - pauli_sum_check = sys.modules["qiskit.opflow"].PauliSumOp - else: - pauli_sum_check = () if isinstance(observable, SparsePauliOp): return observable - elif isinstance(observable, pauli_sum_check): - if isinstance(observable.coeff, ParameterExpression): - raise TypeError( - f"Observable must have numerical coefficient, not {type(observable.coeff)}." - ) - return observable.coeff * observable.primitive elif isinstance(observable, BaseOperator) and not isinstance(observable, BasePauli): return SparsePauliOp.from_operator(observable) else: + if isinstance(observable, PauliList): + warnings.warn( + "Implicit conversion from a PauliList to a SparsePauliOp with coeffs=1 in" + " estimator observable arguments is deprecated as of Qiskit 0.46 and will be" + " in Qiskit 1.0. You should explicitly convert to a SparsePauli op using" + " SparsePauliOp(pauli_list) to avoid this warning.", + DeprecationWarning, + stacklevel=2, + ) return SparsePauliOp(observable) diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index d7ec4b21b9fe..d7a52193dd15 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -144,8 +144,8 @@ backend. It also provides the :meth:`~qiskit.providers.BackendV2.run` method which can run the :class:`~qiskit.circuit.QuantumCircuit` objects and/or :class:`~qiskit.pulse.Schedule` objects. This enables users and other Qiskit -APIs, such as :func:`~qiskit.execute_function.execute` and higher level algorithms in -:mod:`qiskit.algorithms`, to get results from executing circuits on devices in a standard +APIs, such as :func:`~qiskit.execute_function.execute` to get results from +executing circuits on devices in a standard fashion regardless of how the backend is implemented. At a high level the basic steps for writing a provider are: @@ -635,7 +635,7 @@ def status(self): provider-specific :class:`~.Sampler` implementation that leverages the ``M3Mitigation`` class internally to run the circuits and return quasi-probabilities directly from mthree in the result. Doing this would -enable algorithms from :mod:`qiskit.algorithms` to get the best results with +enable algorithms to get the best results with mitigation applied directly from your backends. You can refer to the documentation in :mod:`qiskit.primitives` on how to write custom implementations. Also the built-in implementations: :class:`~.Sampler`, @@ -750,8 +750,6 @@ def status(self): to wrap a :class:`~.BackendV1` object with a :class:`~.BackendV2` interface. """ -import pkgutil - # Providers interface from qiskit.providers.provider import Provider from qiskit.providers.provider import ProviderV1 @@ -773,8 +771,3 @@ def status(self): BackendConfigurationError, ) from qiskit.providers.jobstatus import JobStatus - - -# Support for the deprecated extending this namespace. -# Remove this after 0.46.0 release -__path__ = pkgutil.extend_path(__path__, __name__) diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py index fe582540a647..1e75f4a4a8a6 100644 --- a/qiskit/providers/backend_compat.py +++ b/qiskit/providers/backend_compat.py @@ -13,185 +13,256 @@ """Backend abstract interface for providers.""" from __future__ import annotations - -from typing import List, Iterable, Any, Dict, Optional - -from qiskit.exceptions import QiskitError +import logging +from typing import List, Iterable, Any, Dict, Optional, Tuple from qiskit.providers.backend import BackendV1, BackendV2 from qiskit.providers.backend import QubitProperties -from qiskit.utils.units import apply_prefix -from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping -from qiskit.circuit.measure import Measure from qiskit.providers.models.backendconfiguration import BackendConfiguration from qiskit.providers.models.backendproperties import BackendProperties + from qiskit.providers.models.pulsedefaults import PulseDefaults from qiskit.providers.options import Options from qiskit.providers.exceptions import BackendPropertyError +logger = logging.getLogger(__name__) + def convert_to_target( configuration: BackendConfiguration, properties: BackendProperties = None, defaults: PulseDefaults = None, custom_name_mapping: Optional[Dict[str, Any]] = None, - add_delay: bool = False, - filter_faulty: bool = False, + add_delay: bool = True, + filter_faulty: bool = True, ): - """Uses configuration, properties and pulse defaults - to construct and return Target class. - - In order to convert with a ``defaults.instruction_schedule_map``, - which has a custom calibration for an operation, - the operation name must be in ``configuration.basis_gates`` and - ``custom_name_mapping`` must be supplied for the operation. - Otherwise, the operation will be dropped in the resulting ``Target`` object. - - That suggests it is recommended to add custom calibrations **after** creating a target - with this function instead of adding them to ``defaults`` in advance. For example:: - - target.add_instruction(custom_gate, {(0, 1): InstructionProperties(calibration=custom_sched)}) + """Decode transpiler target from backend data set. + + This function generates ``Target`` instance from intermediate + legacy objects such as ``BackendProperties`` and ``PulseDefaults``. + + .. note:: + Passing in legacy objects like BackendProperties as properties and PulseDefaults + as defaults will be deprecated in the future. + + Args: + configuration: Backend configuration as ``BackendConfiguration`` + properties: Backend property dictionary or ``BackendProperties`` + defaults: Backend pulse defaults dictionary or ``PulseDefaults`` + custom_name_mapping: A name mapping must be supplied for the operation + not included in Qiskit Standard Gate name mapping, otherwise the operation + will be dropped in the resulting ``Target`` object. + add_delay: If True, adds delay to the instruction set. + filter_faulty: If True, this filters the non-operational qubits. + + Returns: + A ``Target`` instance. """ + + # importing pacakges where they are needed, to avoid cyclic-import. # pylint: disable=cyclic-import from qiskit.transpiler.target import ( Target, InstructionProperties, ) + from qiskit.circuit.controlflow import ForLoopOp, IfElseOp, SwitchCaseOp, WhileLoopOp + from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping + from qiskit.circuit.parameter import Parameter + from qiskit.circuit.gate import Gate + + required = ["measure", "delay"] + + # Load Qiskit object representation + qiskit_inst_mapping = get_standard_gate_name_mapping() + if custom_name_mapping: + qiskit_inst_mapping.update(custom_name_mapping) + + qiskit_control_flow_mapping = { + "if_else": IfElseOp, + "while_loop": WhileLoopOp, + "for_loop": ForLoopOp, + "switch_case": SwitchCaseOp, + } + + in_data = {"num_qubits": configuration.n_qubits} + + # Parse global configuration properties + if hasattr(configuration, "dt"): + in_data["dt"] = configuration.dt + if hasattr(configuration, "timing_constraints"): + in_data.update(configuration.timing_constraints) + + # Create instruction property placeholder from backend configuration + basis_gates = set(getattr(configuration, "basis_gates", [])) + gate_configs = {gate.name: gate for gate in configuration.gates} + inst_name_map = {} # type: Dict[str, Instruction] + prop_name_map = {} # type: Dict[str, Dict[Tuple[int, ...], InstructionProperties]] + all_instructions = set.union(basis_gates, set(required)) + + faulty_ops = set() + faulty_qubits = [] + unsupported_instructions = [] + + # Create name to Qiskit instruction object repr mapping + for name in all_instructions: + if name in qiskit_control_flow_mapping: + continue + if name in qiskit_inst_mapping: + inst_name_map[name] = qiskit_inst_mapping[name] + elif name in gate_configs: + this_config = gate_configs[name] + params = list(map(Parameter, getattr(this_config, "parameters", []))) + coupling_map = getattr(this_config, "coupling_map", []) + inst_name_map[name] = Gate( + name=name, + num_qubits=len(coupling_map[0]) if coupling_map else 0, + params=params, + ) + else: + logger.warning( + "Definition of instruction %s is not found in the Qiskit namespace and " + "GateConfig is not provided by the BackendConfiguration payload. " + "Qiskit Gate model cannot be instantiated for this instruction and " + "this instruction is silently excluded from the Target. " + "Please add new gate class to Qiskit or provide GateConfig for this name.", + name, + ) + unsupported_instructions.append(name) + + for name in unsupported_instructions: + all_instructions.remove(name) + + # Create empty inst properties from gate configs + for name, spec in gate_configs.items(): + if hasattr(spec, "coupling_map"): + coupling_map = spec.coupling_map + prop_name_map[name] = dict.fromkeys(map(tuple, coupling_map)) + else: + prop_name_map[name] = None + + # Populate instruction properties + if properties: + qubit_properties = [ + QubitProperties( + t1=properties.qubit_property(qubit_idx)["T1"][0], + t2=properties.qubit_property(qubit_idx)["T2"][0], + frequency=properties.qubit_property(qubit_idx)["frequency"][0], + ) + for qubit_idx in range(0, configuration.num_qubits) + ] + + in_data["qubit_properties"] = qubit_properties - # Standard gates library mapping, multicontrolled gates not included since they're - # variable width - name_mapping = get_standard_gate_name_mapping() - target = None - if custom_name_mapping is not None: - name_mapping.update(custom_name_mapping) - faulty_qubits = set() - # Parse from properties if it exsits - if properties is not None: if filter_faulty: - faulty_qubits = set(properties.faulty_qubits()) - qubit_properties = qubit_props_list_from_props(properties=properties) - target = Target( - num_qubits=configuration.n_qubits, - qubit_properties=qubit_properties, - concurrent_measurements=getattr(configuration, "meas_map", None), - ) - # Parse instructions - gates: Dict[str, Any] = {} - for gate in properties.gates: - name = gate.gate - if name in name_mapping: - if name not in gates: - gates[name] = {} - else: - raise QiskitError( - f"Operation name {name} does not have a known mapping. Use " - "custom_name_mapping to map this name to an Operation object" - ) - - qubits = tuple(gate.qubits) - if filter_faulty: - if any(not properties.is_qubit_operational(qubit) for qubit in qubits): + faulty_qubits = properties.faulty_qubits() + + for name in prop_name_map.keys(): + for qubits, params in properties.gate_property(name).items(): + in_param = { + "error": params["gate_error"][0] if "gate_error" in params else None, + "duration": params["gate_length"][0] if "gate_length" in params else None, + } + inst_prop = InstructionProperties(**in_param) + + if filter_faulty and ( + (not properties.is_gate_operational(name, qubits)) + or any(not properties.is_qubit_operational(qubit) for qubit in qubits) + ): + faulty_ops.add((name, qubits)) + try: + del prop_name_map[name][qubits] + except KeyError: + pass continue - if not properties.is_gate_operational(name, gate.qubits): + + if prop_name_map[name] is None: + prop_name_map[name] = {} + + prop_name_map[name][qubits] = inst_prop + + # Measure instruction property is stored in qubit property + prop_name_map["measure"] = {} + + for qubit_idx in range(configuration.num_qubits): + if qubit_idx in faulty_qubits: + continue + qubit_prop = properties.qubit_property(qubit_idx) + in_prop = { + "duration": qubit_prop["readout_length"][0] + if "readout_length" in qubit_prop + else None, + "error": qubit_prop["readout_error"][0] if "readout_error" in qubit_prop else None, + } + prop_name_map["measure"][(qubit_idx,)] = InstructionProperties(**in_prop) + + if add_delay and "delay" not in prop_name_map: + prop_name_map["delay"] = { + (q,): None for q in range(configuration.num_qubits) if q not in faulty_qubits + } + + if defaults: + inst_sched_map = defaults.instruction_schedule_map + + for name in inst_sched_map.instructions: + for qubits in inst_sched_map.qubits_with_instruction(name): + + if not isinstance(qubits, tuple): + qubits = (qubits,) + + if ( + name not in all_instructions + or name not in prop_name_map + or qubits not in prop_name_map[name] + ): + logger.info( + "Gate calibration for instruction %s on qubits %s is found " + "in the PulseDefaults payload. However, this entry is not defined in " + "the gate mapping of Target. This calibration is ignored.", + name, + qubits, + ) continue - gate_props = {} - for param in gate.parameters: - if param.name == "gate_error": - gate_props["error"] = param.value - if param.name == "gate_length": - gate_props["duration"] = apply_prefix(param.value, param.unit) - gates[name][qubits] = InstructionProperties(**gate_props) - for gate, props in gates.items(): - inst = name_mapping[gate] - target.add_instruction(inst, props) - # Create measurement instructions: - measure_props = {} - for qubit, _ in enumerate(properties.qubits): - if filter_faulty: - if not properties.is_qubit_operational(qubit): + if (name, qubits) in faulty_ops: continue - try: - duration = properties.readout_length(qubit) - except BackendPropertyError: - duration = None - try: - error = properties.readout_error(qubit) - except BackendPropertyError: - error = None - measure_props[(qubit,)] = InstructionProperties( - duration=duration, - error=error, + + entry = inst_sched_map._get_calibration_entry(name, qubits) + + try: + prop_name_map[name][qubits].calibration = entry + except AttributeError: + logger.info( + "The PulseDefaults payload received contains an instruction %s on " + "qubits %s which is not present in the configuration or properties payload.", + name, + qubits, + ) + + # Remove 'delay' if add_delay is set to False. + if not add_delay: + if "delay" in all_instructions: + all_instructions.remove("delay") + + # Add parsed properties to target + target = Target(**in_data) + for inst_name in all_instructions: + if inst_name in qiskit_control_flow_mapping: + # Control flow operator doesn't have gate property. + target.add_instruction( + instruction=qiskit_control_flow_mapping[inst_name], + name=inst_name, ) - target.add_instruction(Measure(), measure_props) - # Parse from configuration because properties doesn't exist - else: - target = Target( - num_qubits=configuration.n_qubits, - concurrent_measurements=getattr(configuration, "meas_map", None), - ) - for gate in configuration.gates: - name = gate.name - gate_props = ( - {tuple(x): None for x in gate.coupling_map} # type: ignore[misc] - if hasattr(gate, "coupling_map") - else {None: None} + elif properties is None: + target.add_instruction( + instruction=inst_name_map[inst_name], + name=inst_name, ) - if name in name_mapping: - target.add_instruction(name_mapping[name], gate_props) - else: - raise QiskitError( - f"Operation name {name} does not have a known mapping. " - "Use custom_name_mapping to map this name to an Operation object" - ) - target.add_instruction(Measure()) - # parse global configuration properties - if hasattr(configuration, "dt"): - target.dt = configuration.dt - if hasattr(configuration, "timing_constraints"): - target.granularity = configuration.timing_constraints.get("granularity") - target.min_length = configuration.timing_constraints.get("min_length") - target.pulse_alignment = configuration.timing_constraints.get("pulse_alignment") - target.acquire_alignment = configuration.timing_constraints.get("acquire_alignment") - # If a pulse defaults exists use that as the source of truth - if defaults is not None: - inst_map = defaults.instruction_schedule_map - for inst in inst_map.instructions: - for qarg in inst_map.qubits_with_instruction(inst): - try: - qargs = tuple(qarg) - except TypeError: - qargs = (qarg,) - # Do NOT call .get method. This parses Qpbj immediately. - # This operation is computationally expensive and should be bypassed. - calibration_entry = inst_map._get_calibration_entry(inst, qargs) - if inst in target: - if inst == "measure": - for qubit in qargs: - if filter_faulty and qubit in faulty_qubits: - continue - target[inst][(qubit,)].calibration = calibration_entry - elif qargs in target[inst]: - if filter_faulty and any(qubit in faulty_qubits for qubit in qargs): - continue - target[inst][qargs].calibration = calibration_entry - combined_global_ops = set() - if configuration.basis_gates: - combined_global_ops.update(configuration.basis_gates) - for op in combined_global_ops: - if op not in target: - if op in name_mapping: - target.add_instruction(name_mapping[op], name=op) - else: - raise QiskitError( - f"Operation name '{op}' does not have a known mapping. Use " - "custom_name_mapping to map this name to an Operation object" - ) - if add_delay and "delay" not in target: - target.add_instruction( - name_mapping["delay"], - {(bit,): None for bit in range(target.num_qubits) if bit not in faulty_qubits}, - ) + else: + target.add_instruction( + instruction=inst_name_map[inst_name], + properties=prop_name_map.get(inst_name, None), + ) + return target @@ -254,8 +325,8 @@ def __init__( self, backend: BackendV1, name_mapping: Optional[Dict[str, Any]] = None, - add_delay: bool = False, - filter_faulty: bool = False, + add_delay: bool = True, + filter_faulty: bool = True, ): """Initialize a BackendV2 converter instance based on a BackendV1 instance. @@ -286,9 +357,13 @@ def __init__( ) self._options = self._backend._options self._properties = None + self._defaults = None + if hasattr(self._backend, "properties"): self._properties = self._backend.properties() - self._defaults = None + if hasattr(self._backend, "defaults"): + self._defaults = self._backend.defaults() + self._target = None self._name_mapping = name_mapping self._add_delay = add_delay @@ -301,14 +376,10 @@ def target(self): :rtype: Target """ if self._target is None: - if self._defaults is None and hasattr(self._backend, "defaults"): - self._defaults = self._backend.defaults() - if self._properties is None and hasattr(self._backend, "properties"): - self._properties = self._backend.properties() self._target = convert_to_target( - self._config, - self._properties, - self._defaults, + configuration=self._config, + properties=self._properties, + defaults=self._defaults, custom_name_mapping=self._name_mapping, add_delay=self._add_delay, filter_faulty=self._filter_faulty, diff --git a/qiskit/providers/fake_provider/fake_backend.py b/qiskit/providers/fake_provider/fake_backend.py index e1740368884c..35e993185bfe 100644 --- a/qiskit/providers/fake_provider/fake_backend.py +++ b/qiskit/providers/fake_provider/fake_backend.py @@ -177,9 +177,8 @@ def target(self) -> Target: defaults = PulseDefaults.from_dict(self._defs_dict) self._target = convert_to_target( - conf, props, defaults, add_delay=True, filter_faulty=True + configuration=conf, properties=props, defaults=defaults ) - return self._target @property diff --git a/qiskit/providers/fake_provider/fake_backend_v2.py b/qiskit/providers/fake_provider/fake_backend_v2.py index 23b0c58dadce..96cd6dff9d37 100644 --- a/qiskit/providers/fake_provider/fake_backend_v2.py +++ b/qiskit/providers/fake_provider/fake_backend_v2.py @@ -42,7 +42,7 @@ def __init__(self): None, name="FakeV2", description="A fake BackendV2 example", - online_date=datetime.datetime.utcnow(), + online_date=datetime.datetime.now(datetime.timezone.utc), backend_version="0.0.1", ) self._qubit_properties = [ @@ -116,7 +116,7 @@ def __init__(self, bidirectional=True): None, name="Fake5QV2", description="A fake BackendV2 example", - online_date=datetime.datetime.utcnow(), + online_date=datetime.datetime.now(datetime.timezone.utc), backend_version="0.0.1", ) qubit_properties = [ @@ -188,7 +188,7 @@ def __init__(self): None, name="FakeSimpleV2", description="A fake simple BackendV2 example", - online_date=datetime.datetime.utcnow(), + online_date=datetime.datetime.now(datetime.timezone.utc), backend_version="0.0.1", ) self._lam = Parameter("lambda") diff --git a/qiskit/providers/fake_provider/fake_mumbai_v2.py b/qiskit/providers/fake_provider/fake_mumbai_v2.py index 1a5f1c24d4a3..8474319dfa62 100644 --- a/qiskit/providers/fake_provider/fake_mumbai_v2.py +++ b/qiskit/providers/fake_provider/fake_mumbai_v2.py @@ -39,7 +39,7 @@ def __init__(self): super().__init__( name="FakeMumbaiFractionalCX", description="A fake BackendV2 example based on IBM Mumbai", - online_date=datetime.datetime.utcnow(), + online_date=datetime.datetime.now(datetime.timezone.utc), backend_version="0.0.1", ) dt = 0.2222222222222222e-9 diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 10d150e267a1..f8938aa075f8 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -40,8 +40,6 @@ .. automodule:: qiskit.pulse.schedule .. automodule:: qiskit.pulse.transforms .. automodule:: qiskit.pulse.builder -.. automodule:: qiskit.pulse.model - .. currentmodule:: qiskit.pulse @@ -67,8 +65,6 @@ from qiskit.pulse.builder import ( # Construction methods. active_backend, - active_transpiler_settings, - active_circuit_scheduler_settings, build, num_qubits, qubit_channels, @@ -97,21 +93,13 @@ align_left, align_right, align_sequential, - circuit_scheduler_settings, frequency_offset, phase_offset, - transpiler_settings, # Macros. macro, measure, measure_all, delay_qubits, - # Circuit instructions. - cx, - u1, - u2, - u3, - x, ) from qiskit.pulse.channels import ( AcquireChannel, @@ -163,21 +151,9 @@ GaussianDeriv, Sech, SechDeriv, - ParametricPulse, SymbolicPulse, ScalableSymbolicPulse, Waveform, ) from qiskit.pulse.library.samplers.decorators import functional_pulse from qiskit.pulse.schedule import Schedule, ScheduleBlock - -from qiskit.pulse.model import ( - LogicalElement, - Qubit, - Coupler, - Frame, - GenericFrame, - QubitFrame, - MeasurementFrame, - MixedFrame, -) diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index 913384a56f3c..5c753872962c 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -92,30 +92,38 @@ .. plot:: :include-source: - import math + from math import pi + from qiskit.compiler import schedule + from qiskit.circuit import QuantumCircuit from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse3Q + from qiskit.providers.fake_provider import FakePerth - # TODO: This example should use a real mock backend. - backend = FakeOpenPulse3Q() + backend = FakePerth() d2 = pulse.DriveChannel(2) - with pulse.build(backend) as bell_prep: - pulse.u2(0, math.pi, 0) - pulse.cx(0, 1) + qc = QuantumCircuit(2) + # Hadamard + qc.rz(pi/2, 0) + qc.sx(0) + qc.rz(pi/2, 0) + + qc.cx(0, 1) + + bell_sched = schedule(qc, backend) with pulse.build(backend) as decoupled_bell_prep_and_measure: # We call our bell state preparation schedule constructed above. with pulse.align_right(): - pulse.call(bell_prep) - pulse.play(pulse.Constant(bell_prep.duration, 0.02), d2) + pulse.call(bell_sched) + pulse.play(pulse.Constant(bell_sched.duration, 0.02), d2) pulse.barrier(0, 1, 2) registers = pulse.measure_all() decoupled_bell_prep_and_measure.draw() + With the pulse builder we are able to blend programming on qubits and channels. While the pulse schedule is based on instructions that operate on channels, the pulse builder automatically handles the mapping from qubits to @@ -126,6 +134,7 @@ .. code-block:: import math + from qiskit.compiler import schedule from qiskit import pulse, QuantumCircuit from qiskit.pulse import library @@ -133,6 +142,9 @@ backend = FakeOpenPulse2Q() + qc = QuantumCircuit(2, 2) + qc.cx(0, 1) + with pulse.build(backend) as pulse_prog: # Create a pulse. gaussian_pulse = library.gaussian(10, 1.0, 2) @@ -198,17 +210,12 @@ # delay for 100 cycles on qubits 0 and 1. pulse.delay_qubits(100, 0, 1) - # Call a quantum circuit. The pulse builder lazily constructs a quantum - # circuit which is then transpiled and scheduled before inserting into - # a pulse schedule. - # NOTE: Quantum register indices correspond to physical qubit indices. + # Call a schedule for a quantum circuit thereby inserting into + # the pulse schedule. qc = QuantumCircuit(2, 2) qc.cx(0, 1) - pulse.call(qc) - # Calling a small set of standard gates and decomposing to pulses is - # also supported with more natural syntax. - pulse.u3(0, math.pi, 0, 0) - pulse.cx(0, 1) + qc_sched = schedule(qc, backend) + pulse.call(qc_sched) # It is also be possible to call a preexisting schedule @@ -228,6 +235,7 @@ with pulse.phase_offset(math.pi, d0): pulse.play(gaussian_pulse, d0) + The above is just a small taste of what is possible with the builder. See the rest of the module documentation for more information on its capabilities. @@ -335,10 +343,8 @@ .. autofunction:: align_left .. autofunction:: align_right .. autofunction:: align_sequential -.. autofunction:: circuit_scheduler_settings .. autofunction:: frequency_offset .. autofunction:: phase_offset -.. autofunction:: transpiler_settings Macros @@ -366,36 +372,6 @@ .. autofunction:: delay_qubits -Circuit Gates -============= - -To use circuit level gates within your pulse program call a circuit -with :func:`call`. - -.. warning:: - These will be removed in future versions with the release of a circuit - builder interface in which it will be possible to calibrate a gate in - terms of pulses and use that gate in a circuit. - -.. code-block:: - - import math - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeArmonk - - backend = FakeArmonk() - - with pulse.build(backend) as u3_sched: - pulse.u3(math.pi, 0, math.pi, 0) - -.. autofunction:: cx -.. autofunction:: u1 -.. autofunction:: u2 -.. autofunction:: u3 -.. autofunction:: x - - Utilities ========= @@ -428,14 +404,11 @@ There are 1e-06 seconds in 4500 samples. .. autofunction:: active_backend -.. autofunction:: active_transpiler_settings -.. autofunction:: active_circuit_scheduler_settings .. autofunction:: num_qubits .. autofunction:: qubit_channels .. autofunction:: samples_to_seconds .. autofunction:: seconds_to_samples """ -import collections import contextvars import functools import itertools @@ -444,16 +417,13 @@ from contextlib import contextmanager from functools import singledispatchmethod from typing import ( - Any, Callable, ContextManager, Dict, Iterable, List, - Mapping, Optional, Set, - Tuple, TypeVar, Union, NewType, @@ -461,8 +431,6 @@ import numpy as np -from qiskit import circuit -from qiskit.circuit.library import standard_gates as gates from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType from qiskit.pulse import ( channels as chans, @@ -478,7 +446,6 @@ from qiskit.pulse.schedule import Schedule, ScheduleBlock from qiskit.pulse.transforms.alignments import AlignmentKind - #: contextvars.ContextVar[BuilderContext]: active builder BUILDER_CONTEXTVAR = contextvars.ContextVar("backend") @@ -487,18 +454,6 @@ StorageLocation = NewType("StorageLocation", Union[chans.MemorySlot, chans.RegisterSlot]) -def _compile_lazy_circuit_before(function: Callable[..., T]) -> Callable[..., T]: - """Decorator thats schedules and calls the lazily compiled circuit before - executing the decorated builder method.""" - - @functools.wraps(function) - def wrapper(self, *args, **kwargs): - self._compile_lazy_circuit() - return function(self, *args, **kwargs) - - return wrapper - - def _requires_backend(function: Callable[..., T]) -> Callable[..., T]: """Decorator a function to raise if it is called without a builder with a set backend. @@ -530,8 +485,6 @@ def __init__( block: Optional[ScheduleBlock] = None, name: Optional[str] = None, default_alignment: Union[str, AlignmentKind] = "left", - default_transpiler_settings: Mapping = None, - default_circuit_scheduler_settings: Mapping = None, ): """Initialize the builder context. @@ -550,9 +503,6 @@ def __init__( default_alignment: Default scheduling alignment for builder. One of ``left``, ``right``, ``sequential`` or an instance of :class:`~qiskit.pulse.transforms.alignments.AlignmentKind` subclass. - default_transpiler_settings: Default settings for the transpiler. - default_circuit_scheduler_settings: Default settings for the - circuit to pulse scheduler. Raises: PulseError: When invalid ``default_alignment`` or `block` is specified. @@ -563,15 +513,6 @@ def __init__( #: Union[None, ContextVar]: Token for this ``_PulseBuilder``'s ``ContextVar``. self._backend_ctx_token = None - #: QuantumCircuit: Lazily constructed quantum circuit - self._lazy_circuit = None - - #: Dict[str, Any]: Transpiler setting dictionary. - self._transpiler_settings = default_transpiler_settings or {} - - #: Dict[str, Any]: Scheduler setting dictionary. - self._circuit_scheduler_settings = default_circuit_scheduler_settings or {} - #: List[ScheduleBlock]: Stack of context. self._context_stack = [] @@ -615,7 +556,6 @@ def __enter__(self) -> ScheduleBlock: return output - @_compile_lazy_circuit_before def __exit__(self, exc_type, exc_val, exc_tb): """Exit the builder context and compile the built pulse program.""" self.compile() @@ -630,12 +570,10 @@ def backend(self): """ return self._backend - @_compile_lazy_circuit_before def push_context(self, alignment: AlignmentKind): """Push new context to the stack.""" self._context_stack.append(ScheduleBlock(alignment_context=alignment)) - @_compile_lazy_circuit_before def pop_context(self) -> ScheduleBlock: """Pop the last context from the stack.""" if len(self._context_stack) == 1: @@ -661,29 +599,6 @@ def num_qubits(self): return self.backend.num_qubits return self.backend.configuration().n_qubits - @property - def transpiler_settings(self) -> Mapping: - """The builder's transpiler settings.""" - return self._transpiler_settings - - @transpiler_settings.setter - @_compile_lazy_circuit_before - def transpiler_settings(self, settings: Mapping): - self._compile_lazy_circuit() - self._transpiler_settings = settings - - @property - def circuit_scheduler_settings(self) -> Mapping: - """The builder's circuit to pulse scheduler settings.""" - return self._circuit_scheduler_settings - - @circuit_scheduler_settings.setter - @_compile_lazy_circuit_before - def circuit_scheduler_settings(self, settings: Mapping): - self._compile_lazy_circuit() - self._circuit_scheduler_settings = settings - - @_compile_lazy_circuit_before def compile(self) -> ScheduleBlock: """Compile and output the built pulse program.""" # Not much happens because we currently compile as we build. @@ -696,33 +611,6 @@ def compile(self) -> ScheduleBlock: return self._context_stack[0] - def _compile_lazy_circuit(self): - """Call a context QuantumCircuit (lazy circuit) and append the output pulse schedule - to the builder's context schedule. - - Note that the lazy circuit is not stored as a call instruction. - """ - if self._lazy_circuit: - lazy_circuit = self._lazy_circuit - # reset lazy circuit - self._lazy_circuit = self._new_circuit() - self.call_subroutine(self._compile_circuit(lazy_circuit)) - - def _compile_circuit(self, circ) -> Schedule: - """Take a QuantumCircuit and output the pulse schedule associated with the circuit.""" - from qiskit import compiler # pylint: disable=cyclic-import - - transpiled_circuit = compiler.transpile(circ, self.backend, **self.transpiler_settings) - sched = compiler.schedule( - transpiled_circuit, self.backend, **self.circuit_scheduler_settings - ) - return sched - - def _new_circuit(self): - """Create a new circuit for lazy circuit scheduling.""" - return circuit.QuantumCircuit(self.num_qubits) - - @_compile_lazy_circuit_before def append_instruction(self, instruction: instructions.Instruction): """Add an instruction to the builder's context schedule. @@ -741,7 +629,6 @@ def append_reference(self, name: str, *extra_keys: str): inst = instructions.Reference(name, *extra_keys) self.append_instruction(inst) - @_compile_lazy_circuit_before def append_subroutine(self, subroutine: Union[Schedule, ScheduleBlock]): """Append a :class:`ScheduleBlock` to the builder's context schedule. @@ -769,7 +656,7 @@ def append_subroutine(self, subroutine: Union[Schedule, ScheduleBlock]): @singledispatchmethod def call_subroutine( self, - subroutine: Union[circuit.QuantumCircuit, Schedule, ScheduleBlock], + subroutine: Union[Schedule, ScheduleBlock], name: Optional[str] = None, value_dict: Optional[Dict[ParameterExpression, ParameterValueType]] = None, **kw_params: ParameterValueType, @@ -797,7 +684,7 @@ def call_subroutine( """ raise exceptions.PulseError( f"Subroutine type {subroutine.__class__.__name__} is " - "not valid data format. Call QuantumCircuit, " + "not valid data format. Call " "Schedule, or ScheduleBlock." ) @@ -864,62 +751,6 @@ def _( **kw_params, ) - @call_subroutine.register - def _( - self, - target_circuit: circuit.QuantumCircuit, - name: Optional[str] = None, - value_dict: Optional[Dict[ParameterExpression, ParameterValueType]] = None, - **kw_params: ParameterValueType, - ): - if len(target_circuit) == 0: - return - - self._compile_lazy_circuit() - self.call_subroutine( - self._compile_circuit(target_circuit), - name=name, - value_dict=value_dict, - **kw_params, - ) - - @_requires_backend - def call_gate(self, gate: circuit.Gate, qubits: Tuple[int, ...], lazy: bool = True): - """Call the circuit ``gate`` in the pulse program. - - The qubits are assumed to be defined on physical qubits. - - If ``lazy == True`` this circuit will extend a lazily constructed - quantum circuit. When an operation occurs that breaks the underlying - circuit scheduling assumptions such as adding a pulse instruction or - changing the alignment context the circuit will be - transpiled and scheduled into pulses with the current active settings. - - Args: - gate: Gate to call. - qubits: Qubits to call gate on. - lazy: If false the circuit will be transpiled and pulse scheduled - immediately. Otherwise, it will extend the active lazy circuit - as defined above. - """ - try: - iter(qubits) - except TypeError: - qubits = (qubits,) - - if lazy: - self._call_gate(gate, qubits) - else: - self._compile_lazy_circuit() - self._call_gate(gate, qubits) - self._compile_lazy_circuit() - - def _call_gate(self, gate, qargs): - if self._lazy_circuit is None: - self._lazy_circuit = self._new_circuit() - - self._lazy_circuit.append(gate, qargs=qargs) - @staticmethod def _naive_typecast_schedule(schedule: Schedule): # Naively convert into ScheduleBlock @@ -947,8 +778,6 @@ def build( schedule: Optional[ScheduleBlock] = None, name: Optional[str] = None, default_alignment: Optional[Union[str, AlignmentKind]] = "left", - default_transpiler_settings: Optional[Dict[str, Any]] = None, - default_circuit_scheduler_settings: Optional[Dict[str, Any]] = None, ) -> ContextManager[ScheduleBlock]: """Create a context manager for launching the imperative pulse builder DSL. @@ -982,9 +811,6 @@ def build( name: Name of pulse program to be built. default_alignment: Default scheduling alignment for builder. One of ``left``, ``right``, ``sequential`` or an alignment context. - default_transpiler_settings: Default settings for the transpiler. - default_circuit_scheduler_settings: Default settings for the - circuit to pulse scheduler. Returns: A new builder context which has the active builder initialized. @@ -994,8 +820,6 @@ def build( block=schedule, name=name, default_alignment=default_alignment, - default_transpiler_settings=default_transpiler_settings, - default_circuit_scheduler_settings=default_circuit_scheduler_settings, ) @@ -1197,59 +1021,6 @@ def _qubits_to_channels(*channels_or_qubits: Union[int, chans.Channel]) -> Set[c return channels -def active_transpiler_settings() -> Dict[str, Any]: - """Return the current active builder context's transpiler settings. - - Examples: - - .. code-block:: - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - transpiler_settings = {'optimization_level': 3} - - with pulse.build(backend, - default_transpiler_settings=transpiler_settings): - print(pulse.active_transpiler_settings()) - - .. parsed-literal:: - - {'optimization_level': 3} - - """ - return dict(_active_builder().transpiler_settings) - - -def active_circuit_scheduler_settings() -> Dict[str, Any]: - """Return the current active builder context's circuit scheduler settings. - - Examples: - - .. code-block:: - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - circuit_scheduler_settings = {'method': 'alap'} - - with pulse.build( - backend, - default_circuit_scheduler_settings=circuit_scheduler_settings): - print(pulse.active_circuit_scheduler_settings()) - - .. parsed-literal:: - - {'method': 'alap'} - - """ - return dict(_active_builder().circuit_scheduler_settings) - - # Contexts @@ -1507,73 +1278,6 @@ def general_transforms(alignment_context: AlignmentKind) -> ContextManager[None] builder.append_subroutine(current) -@contextmanager -def transpiler_settings(**settings) -> ContextManager[None]: - """Set the currently active transpiler settings for this context. - - Examples: - - .. code-block:: - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - with pulse.build(backend): - print(pulse.active_transpiler_settings()) - with pulse.transpiler_settings(optimization_level=3): - print(pulse.active_transpiler_settings()) - - .. parsed-literal:: - - {} - {'optimization_level': 3} - """ - builder = _active_builder() - curr_transpiler_settings = builder.transpiler_settings - builder.transpiler_settings = collections.ChainMap(settings, curr_transpiler_settings) - try: - yield - finally: - builder.transpiler_settings = curr_transpiler_settings - - -@contextmanager -def circuit_scheduler_settings(**settings) -> ContextManager[None]: - """Set the currently active circuit scheduler settings for this context. - - Examples: - - .. code-block:: - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - with pulse.build(backend): - print(pulse.active_circuit_scheduler_settings()) - with pulse.circuit_scheduler_settings(method='alap'): - print(pulse.active_circuit_scheduler_settings()) - - .. parsed-literal:: - - {} - {'method': 'alap'} - - """ - builder = _active_builder() - curr_circuit_scheduler_settings = builder.circuit_scheduler_settings - builder.circuit_scheduler_settings = collections.ChainMap( - settings, curr_circuit_scheduler_settings - ) - try: - yield - finally: - builder.circuit_scheduler_settings = curr_circuit_scheduler_settings - - @contextmanager def phase_offset(phase: float, *channels: chans.PulseChannel) -> ContextManager[None]: """Shift the phase of input channels on entry into context and undo on exit. @@ -1993,7 +1697,7 @@ def snapshot(label: str, snapshot_type: str = "statevector"): def call( - target: Optional[Union[circuit.QuantumCircuit, Schedule, ScheduleBlock]], + target: Optional[Union[Schedule, ScheduleBlock]], name: Optional[str] = None, value_dict: Optional[Dict[ParameterValueType, ParameterValueType]] = None, **kw_params: ParameterValueType, @@ -2167,52 +1871,6 @@ def call( The parameter assignment mechanism is available also for schedules. However, the called schedule is not treated as a reference. - 3. Calling a quantum circuit - - .. code-block:: - - backend = FakeBogotaV2() - - qc = circuit.QuantumCircuit(1) - qc.x(0) - - with pulse.build(backend) as pulse_prog: - pulse.call(qc) - - print(pulse_prog) - - .. parsed-literal:: - - ScheduleBlock( - Call( - Schedule( - ( - 0, - Play( - Drag( - duration=160, - amp=(0.18989731546729305+0j), - sigma=40, - beta=-1.201258305015517, - name='drag_86a8' - ), - DriveChannel(0), - name='drag_86a8' - ) - ), - name="circuit-87" - ), - name='circuit-87' - ), - name="block7", - transform=AlignLeft() - ) - - .. warning:: - - Calling a circuit from a schedule is not encouraged. Currently, the Qiskit execution model - is migrating toward the pulse gate model, where schedules are attached to - circuits through the :meth:`.QuantumCircuit.add_calibration` method. Args: target: Target circuit or pulse schedule to call. @@ -2309,14 +1967,14 @@ def barrier(*channels_or_qubits: Union[chans.Channel, int], name: Optional[str] with pulse.build(backend) as pulse_prog: with pulse.align_right(): - pulse.x(1) + pulse.call(backend.defaults.instruction_schedule_map.get('x', (1,))) # Barrier qubit 1 and d0. pulse.barrier(1, d0) # Due to barrier this will play before the gate on qubit 1. pulse.play(pulse.Constant(10, 1.0), d0) # This will end at the same time as the pulse above due to # the barrier. - pulse.x(1) + pulse.call(backend.defaults.instruction_schedule_map.get('x', (1,))) .. note:: Requires the active builder context to have a backend set if qubits are barriered on. @@ -2546,188 +2204,3 @@ def delay_qubits(duration: int, *qubits: Union[int, Iterable[int]]): with align_left(): for chan in qubit_chans: delay(duration, chan) - - -# Gate instructions -def call_gate(gate: circuit.Gate, qubits: Tuple[int, ...], lazy: bool = True): - """Call a gate and lazily schedule it to its corresponding - pulse instruction. - - .. note:: - Calling gates directly within the pulse builder namespace will be - deprecated in the future in favor of tight integration with a circuit - builder interface which is under development. - - Examples: - - .. code-block:: - - from qiskit import pulse - from qiskit.pulse import builder - from qiskit.circuit.library import standard_gates as gates - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - with pulse.build(backend) as pulse_prog: - builder.call_gate(gates.CXGate(), (0, 1)) - - We can see the role of the transpiler in scheduling gates by optimizing - away two consecutive CNOT gates: - - .. code-block:: - - with pulse.build(backend) as pulse_prog: - with pulse.transpiler_settings(optimization_level=3): - builder.call_gate(gates.CXGate(), (0, 1)) - builder.call_gate(gates.CXGate(), (0, 1)) - - assert pulse_prog == pulse.Schedule() - - .. note:: If multiple gates are called in a row they may be optimized by - the transpiler, depending on the - :func:`pulse.active_transpiler_settings``. - - .. note:: Requires the active builder context to have a backend set. - - Args: - gate: Circuit gate instance to call. - qubits: Qubits to call gate on. - lazy: If ``false`` the gate will be compiled immediately, otherwise - it will be added onto a lazily evaluated quantum circuit to be - compiled when the builder is forced to by a circuit assumption - being broken, such as the inclusion of a pulse instruction or - new alignment context. - """ - _active_builder().call_gate(gate, qubits, lazy=lazy) - - -def cx(control: int, target: int): # pylint: disable=invalid-name - """Call a :class:`~qiskit.circuit.library.standard_gates.CXGate` on the - input physical qubits. - - .. note:: - Calling gates directly within the pulse builder namespace will be - deprecated in the future in favor of tight integration with a circuit - builder interface which is under development. - - Examples: - - .. code-block:: - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - with pulse.build(backend) as pulse_prog: - pulse.cx(0, 1) - - """ - call_gate(gates.CXGate(), (control, target)) - - -def u1(theta: float, qubit: int): # pylint: disable=invalid-name - """Call a :class:`~qiskit.circuit.library.standard_gates.U1Gate` on the - input physical qubit. - - .. note:: - Calling gates directly within the pulse builder namespace will be - deprecated in the future in favor of tight integration with a circuit - builder interface which is under development. - - Examples: - - .. code-block:: - - import math - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - with pulse.build(backend) as pulse_prog: - pulse.u1(math.pi, 1) - - """ - call_gate(gates.U1Gate(theta), qubit) - - -def u2(phi: float, lam: float, qubit: int): # pylint: disable=invalid-name - """Call a :class:`~qiskit.circuit.library.standard_gates.U2Gate` on the - input physical qubit. - - .. note:: - Calling gates directly within the pulse builder namespace will be - deprecated in the future in favor of tight integration with a circuit - builder interface which is under development. - - Examples: - - .. code-block:: - - import math - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - with pulse.build(backend) as pulse_prog: - pulse.u2(0, math.pi, 1) - - """ - call_gate(gates.U2Gate(phi, lam), qubit) - - -def u3(theta: float, phi: float, lam: float, qubit: int): # pylint: disable=invalid-name - """Call a :class:`~qiskit.circuit.library.standard_gates.U3Gate` on the - input physical qubit. - - .. note:: - Calling gates directly within the pulse builder namespace will be - deprecated in the future in favor of tight integration with a circuit - builder interface which is under development. - - Examples: - - .. code-block:: - - import math - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - with pulse.build(backend) as pulse_prog: - pulse.u3(math.pi, 0, math.pi, 1) - - """ - call_gate(gates.U3Gate(theta, phi, lam), qubit) - - -def x(qubit: int): - """Call a :class:`~qiskit.circuit.library.standard_gates.XGate` on the - input physical qubit. - - .. note:: - Calling gates directly within the pulse builder namespace will be - deprecated in the future in favor of tight integration with a circuit - builder interface which is under development. - - Examples: - - .. code-block:: - - from qiskit import pulse - from qiskit.providers.fake_provider import FakeOpenPulse2Q - - backend = FakeOpenPulse2Q() - - with pulse.build(backend) as pulse_prog: - pulse.x(0) - - """ - call_gate(gates.XGate(), qubit) diff --git a/qiskit/pulse/channels.py b/qiskit/pulse/channels.py index 687b837700d3..e3da9b923ca4 100644 --- a/qiskit/pulse/channels.py +++ b/qiskit/pulse/channels.py @@ -96,7 +96,6 @@ def __init__(self, index: int): """ self._validate_index(index) self._index = index - self._hash = hash((self.__class__.__name__, self._index)) @property def index(self) -> Union[int, ParameterExpression]: @@ -156,7 +155,7 @@ def __eq__(self, other: "Channel") -> bool: return type(self) is type(other) and self._index == other._index def __hash__(self): - return self._hash + return hash((type(self), self._index)) class PulseChannel(Channel, metaclass=ABCMeta): diff --git a/qiskit/pulse/instructions/call.py b/qiskit/pulse/instructions/call.py index 49c9a6fe42c0..d38e1355881e 100644 --- a/qiskit/pulse/instructions/call.py +++ b/qiskit/pulse/instructions/call.py @@ -33,6 +33,7 @@ class Call(instruction.Instruction): @deprecate_func( since="0.25.0", + package_name="qiskit-terra", additional_msg="Instead, use the pulse builder function " "qiskit.pulse.builder.call(subroutine) within an active building context.", ) diff --git a/qiskit/pulse/instructions/instruction.py b/qiskit/pulse/instructions/instruction.py index 3a1764f817f6..18e724241f97 100644 --- a/qiskit/pulse/instructions/instruction.py +++ b/qiskit/pulse/instructions/instruction.py @@ -22,14 +22,11 @@ sched += Delay(duration, channel) # Delay is a specific subclass of Instruction """ from abc import ABC, abstractmethod -from typing import Callable, Iterable, List, Optional, Set, Tuple +from typing import Iterable, List, Optional, Set, Tuple from qiskit.circuit import Parameter from qiskit.pulse.channels import Channel from qiskit.pulse.exceptions import PulseError -from qiskit.utils import optionals as _optionals - -from qiskit.utils.deprecation import deprecate_func # pylint: disable=bad-docstring-quotes @@ -53,7 +50,6 @@ def __init__( """ self._operands = operands self._name = name - self._hash = None self._validate() def _validate(self): @@ -223,75 +219,6 @@ def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" return any(self.parameters) - @deprecate_func( - additional_msg=( - "No direct alternative is being provided to drawing individual pulses. But, " - "instructions can be visualized as part of a complete schedule using " - "``qiskit.visualization.pulse_drawer``." - ), - since="0.23.0", - ) - @_optionals.HAS_MATPLOTLIB.require_in_call - def draw( - self, - dt: float = 1, - style=None, - filename: Optional[str] = None, - interp_method: Optional[Callable] = None, - scale: float = 1, - plot_all: bool = False, - plot_range: Optional[Tuple[float]] = None, - interactive: bool = False, - table: bool = True, - label: bool = False, - framechange: bool = True, - channels: Optional[List[Channel]] = None, - ): - """Plot the instruction. - - Args: - dt: Time interval of samples - style (Optional[SchedStyle]): A style sheet to configure plot appearance - filename: Name required to save pulse image - interp_method: A function for interpolation - scale: Relative visual scaling of waveform amplitudes - plot_all: Plot empty channels - plot_range: A tuple of time range to plot - interactive: When set true show the circuit in a new window - (this depends on the matplotlib backend being used supporting this) - table: Draw event table for supported instructions - label: Label individual instructions - framechange: Add framechange indicators - channels: A list of channel names to plot - - Returns: - matplotlib.figure: A matplotlib figure object of the pulse schedule - """ - # pylint: disable=cyclic-import - from qiskit.visualization.pulse.matplotlib import ScheduleDrawer - from qiskit.visualization.utils import matplotlib_close_if_inline - - drawer = ScheduleDrawer(style=style) - image = drawer.draw( - self, - dt=dt, - interp_method=interp_method, - scale=scale, - plot_range=plot_range, - plot_all=plot_all, - table=table, - label=label, - framechange=framechange, - channels=channels, - ) - if filename: - image.savefig(filename, dpi=drawer.style.dpi, bbox_inches="tight") - - matplotlib_close_if_inline(image) - if image and interactive: - image.show() - return image - def __eq__(self, other: "Instruction") -> bool: """Check if this Instruction is equal to the `other` instruction. @@ -300,9 +227,7 @@ def __eq__(self, other: "Instruction") -> bool: return isinstance(other, type(self)) and self.operands == other.operands def __hash__(self) -> int: - if self._hash is None: - self._hash = hash((type(self), self.operands, self.name)) - return self._hash + return hash((type(self), self.operands, self.name)) def __add__(self, other): """Return a new schedule with `other` inserted within `self` at `start_time`. diff --git a/qiskit/pulse/library/__init__.py b/qiskit/pulse/library/__init__.py index d8809607e3d9..99d382e63f91 100644 --- a/qiskit/pulse/library/__init__.py +++ b/qiskit/pulse/library/__init__.py @@ -23,8 +23,7 @@ This model provides the most flexibility to express arbitrary waveforms and allows a rapid prototyping of new control techniques. However, this model is typically memory inefficient and might be hard to scale to large-size quantum processors. -Several waveform subclasses are defined by :ref:`waveforms`, -but a user can also directly instantiate the :class:`~Waveform` class with ``samples`` argument +A user can directly instantiate the :class:`~Waveform` class with ``samples`` argument which is usually a complex numpy array or any kind of array-like data. In contrast, the :class:`~SymbolicPulse` model only stores the function and its parameters @@ -49,27 +48,6 @@ Waveform SymbolicPulse - ParametricPulse - - -.. _waveforms: - -Waveform Pulse Representation -============================= - -.. autofunction:: constant -.. autofunction:: zero -.. autofunction:: square -.. autofunction:: sawtooth -.. autofunction:: triangle -.. autofunction:: cos -.. autofunction:: sin -.. autofunction:: gaussian -.. autofunction:: gaussian_deriv -.. autofunction:: sech -.. autofunction:: sech_deriv -.. autofunction:: gaussian_square -.. autofunction:: drag .. _symbolic_pulses: @@ -97,22 +75,6 @@ """ -from .discrete import ( - constant, - zero, - square, - sawtooth, - triangle, - cos, - sin, - gaussian, - gaussian_deriv, - sech, - sech_deriv, - gaussian_square, - drag, -) -from .parametric_pulses import ParametricPulse from .symbolic_pulses import ( SymbolicPulse, ScalableSymbolicPulse, diff --git a/qiskit/pulse/library/discrete.py b/qiskit/pulse/library/discrete.py deleted file mode 100644 index 046944471270..000000000000 --- a/qiskit/pulse/library/discrete.py +++ /dev/null @@ -1,633 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - - -"""Module for builtin discrete pulses. - -Note the sampling strategy use for all discrete pulses is ``midpoint``. -""" -from typing import Optional - -from qiskit.utils.deprecation import deprecate_func -from ..exceptions import PulseError -from .waveform import Waveform -from . import continuous -from . import samplers - - -_sampled_constant_pulse = samplers.midpoint(continuous.constant) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including constant() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Constant(...).get_waveform(). " - " Note that complex value support for the `amp` parameter is pending deprecation" - " in the SymbolicPulse library. It is therefore recommended to use two float values" - " for (`amp`, `angle`) instead of complex `amp`", - pending=True, -) -def constant(duration: int, amp: complex, name: Optional[str] = None) -> Waveform: - r"""Generates constant-sampled :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp``, samples from the function: - - .. math:: - - f(x) = A - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Complex pulse amplitude. - name: Name of pulse. - """ - return _sampled_constant_pulse(duration, amp, name=name) - - -_sampled_zero_pulse = samplers.midpoint(continuous.zero) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including zero() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Constant(amp=0,...).get_waveform().", - pending=True, -) -def zero(duration: int, name: Optional[str] = None) -> Waveform: - """Generates zero-sampled :class:`~qiskit.pulse.library.Waveform`. - - Samples from the function: - - .. math:: - - f(x) = 0 - - Args: - duration: Duration of pulse. Must be greater than zero. - name: Name of pulse. - """ - return _sampled_zero_pulse(duration, name=name) - - -_sampled_square_pulse = samplers.midpoint(continuous.square) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including square() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Square(...).get_waveform()." - " Note that pulse.Square() does not support complex values for `amp`," - " and that the phase is defined differently. See documentation.", - pending=True, -) -def square( - duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None -) -> Waveform: - r"""Generates square wave :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp``, :math:`T=` ``period``, and :math:`\phi=` ``phase``, - applies the `midpoint` sampling strategy to generate a discrete pulse sampled from - the continuous function: - - .. math:: - - f(x) = A \text{sign}\left[ \sin\left(\frac{2 \pi x}{T} + 2\phi\right) \right] - - with the convention :math:`\text{sign}(0) = 1`. - - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude. Wave range is :math:`[-` ``amp`` :math:`,` ``amp`` :math:`]`. - freq: Pulse frequency, units of 1./dt. If ``None`` defaults to 1./duration. - phase: Pulse phase. - name: Name of pulse. - """ - if freq is None: - freq = 1.0 / duration - - return _sampled_square_pulse(duration, amp, freq, phase=phase, name=name) - - -_sampled_sawtooth_pulse = samplers.midpoint(continuous.sawtooth) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including sawtooth() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Sawtooth(...).get_waveform()." - " Note that pulse.Sawtooth() does not support complex values for `amp`." - " Instead, use two float values for (`amp`, `angle`)." - " Also note that the phase is defined differently, such that 2*pi phase" - " shifts by a full cycle.", - pending=True, -) -def sawtooth( - duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None -) -> Waveform: - r"""Generates sawtooth wave :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp``, :math:`T=` ``period``, and :math:`\phi=` ``phase``, - applies the `midpoint` sampling strategy to generate a discrete pulse sampled from - the continuous function: - - .. math:: - - f(x) = 2 A \left( g(x) - \left\lfloor \frac{1}{2} + g(x) \right\rfloor\right) - - where :math:`g(x) = x/T + \phi/\pi`. - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude. Wave range is :math:`[-` ``amp`` :math:`,` ``amp`` :math:`]`. - freq: Pulse frequency, units of 1./dt. If ``None`` defaults to 1./duration. - phase: Pulse phase. - name: Name of pulse. - - Example: - .. plot:: - :include-source: - - import matplotlib.pyplot as plt - from qiskit.pulse.library import sawtooth - import numpy as np - - duration = 100 - amp = 1 - freq = 1 / duration - sawtooth_wave = np.real(sawtooth(duration, amp, freq).samples) - plt.plot(range(duration), sawtooth_wave) - plt.show() - """ - if freq is None: - freq = 1.0 / duration - - return _sampled_sawtooth_pulse(duration, amp, freq, phase=phase, name=name) - - -_sampled_triangle_pulse = samplers.midpoint(continuous.triangle) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including triangle() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Triangle(...).get_waveform()." - " Note that pulse.Triangle() does not support complex values for `amp`." - " Instead, use two float values for (`amp`, `angle`).", - pending=True, -) -def triangle( - duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None -) -> Waveform: - r"""Generates triangle wave :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp``, :math:`T=` ``period``, and :math:`\phi=` ``phase``, - applies the `midpoint` sampling strategy to generate a discrete pulse sampled from - the continuous function: - - .. math:: - - f(x) = A \left(-2\left|\text{sawtooth}(x, A, T, \phi)\right| + 1\right) - - This a non-sinusoidal wave with linear ramping. - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude. Wave range is :math:`[-` ``amp`` :math:`,` ``amp`` :math:`]`. - freq: Pulse frequency, units of 1./dt. If ``None`` defaults to 1./duration. - phase: Pulse phase. - name: Name of pulse. - - Example: - .. plot:: - :include-source: - - import matplotlib.pyplot as plt - from qiskit.pulse.library import triangle - import numpy as np - - duration = 100 - amp = 1 - freq = 1 / duration - triangle_wave = np.real(triangle(duration, amp, freq).samples) - plt.plot(range(duration), triangle_wave) - plt.show() - """ - if freq is None: - freq = 1.0 / duration - - return _sampled_triangle_pulse(duration, amp, freq, phase=phase, name=name) - - -_sampled_cos_pulse = samplers.midpoint(continuous.cos) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including cos() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Cos(...).get_waveform()." - " Note that pulse.Cos() does not support complex values for `amp`." - " Instead, use two float values for (`amp`, `angle`).", - pending=True, -) -def cos( - duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None -) -> Waveform: - r"""Generates cosine wave :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp``, :math:`\omega=` ``freq``, and :math:`\phi=` ``phase``, - applies the `midpoint` sampling strategy to generate a discrete pulse sampled from - the continuous function: - - .. math:: - - f(x) = A \cos(2 \pi \omega x + \phi) - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude. - freq: Pulse frequency, units of 1/dt. If ``None`` defaults to single cycle. - phase: Pulse phase. - name: Name of pulse. - """ - if freq is None: - freq = 1 / duration - - return _sampled_cos_pulse(duration, amp, freq, phase=phase, name=name) - - -_sampled_sin_pulse = samplers.midpoint(continuous.sin) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including sin() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Sin(...).get_waveform()." - " Note that pulse.Sin() does not support complex values for `amp`." - " Instead, use two float values for (`amp`, `angle`).", - pending=True, -) -def sin( - duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None -) -> Waveform: - r"""Generates sine wave :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp``, :math:`\omega=` ``freq``, and :math:`\phi=` ``phase``, - applies the `midpoint` sampling strategy to generate a discrete pulse sampled from - the continuous function: - - .. math:: - - f(x) = A \sin(2 \pi \omega x + \phi) - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude. - freq: Pulse frequency, units of 1/dt. If ``None`` defaults to single cycle. - phase: Pulse phase. - name: Name of pulse. - """ - if freq is None: - freq = 1 / duration - - return _sampled_sin_pulse(duration, amp, freq, phase=phase, name=name) - - -_sampled_gaussian_pulse = samplers.midpoint(continuous.gaussian) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including gaussian() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Gaussian(...).get_waveform()." - " Note that complex value support for the `amp` parameter is pending deprecation" - " in the SymbolicPulse library. It is therefore recommended to use two float values" - " for (`amp`, `angle`) instead of complex `amp`", - pending=True, -) -def gaussian( - duration: int, amp: complex, sigma: float, name: Optional[str] = None, zero_ends: bool = True -) -> Waveform: - r"""Generates unnormalized gaussian :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp`` and :math:`\sigma=` ``sigma``, applies the ``midpoint`` sampling strategy - to generate a discrete pulse sampled from the continuous function: - - .. math:: - - f(x) = A\exp\left(\left(\frac{x - \mu}{2\sigma}\right)^2 \right), - - with the center :math:`\mu=` ``duration/2``. - - If ``zero_ends==True``, each output sample :math:`y` is modified according to: - - .. math:: - - y \mapsto A\frac{y-y^*}{A-y^*}, - - where :math:`y^*` is the value of the endpoint samples. This sets the endpoints - to :math:`0` while preserving the amplitude at the center. If :math:`A=y^*`, - :math:`y` is set to :math:`1`. By default, the endpoints are at ``x = -1, x = duration + 1``. - - Integrated area under the full curve is ``amp * np.sqrt(2*np.pi*sigma**2)`` - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude at ``duration/2``. - sigma: Width (standard deviation) of pulse. - name: Name of pulse. - zero_ends: If True, zero ends at ``x = -1, x = duration + 1``, but rescale to preserve amp. - """ - center = duration / 2 - zeroed_width = duration + 2 if zero_ends else None - rescale_amp = bool(zero_ends) - return _sampled_gaussian_pulse( - duration, amp, center, sigma, zeroed_width=zeroed_width, rescale_amp=rescale_amp, name=name - ) - - -_sampled_gaussian_deriv_pulse = samplers.midpoint(continuous.gaussian_deriv) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including gaussian_deriv() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.GaussianDeriv(...).get_waveform()." - " Note that pulse.GaussianDeriv() does not support complex values for `amp`." - " Instead, use two float values for (`amp`, `angle`).", - pending=True, -) -def gaussian_deriv( - duration: int, amp: complex, sigma: float, name: Optional[str] = None -) -> Waveform: - r"""Generates unnormalized gaussian derivative :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp`` and :math:`\sigma=` ``sigma`` applies the `midpoint` sampling strategy - to generate a discrete pulse sampled from the continuous function: - - .. math:: - - f(x) = -A\frac{(x - \mu)}{\sigma^2}\exp - \left(-\frac{1}{2}\left(\frac{x - \mu}{\sigma}\right)^2 \right) - - i.e. the derivative of the Gaussian function, with center :math:`\mu=` ``duration/2``. - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude of corresponding Gaussian at the pulse center (``duration/2``). - sigma: Width (standard deviation) of pulse. - name: Name of pulse. - """ - center = duration / 2 - return _sampled_gaussian_deriv_pulse(duration, amp, center, sigma, name=name) - - -_sampled_sech_pulse = samplers.midpoint(continuous.sech) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including sech() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Sech(...).get_waveform()." - " Note that pulse.Sech() does not support complex values for `amp`." - " Instead, use two float values for (`amp`, `angle`).", - pending=True, -) -def sech( - duration: int, amp: complex, sigma: float, name: str = None, zero_ends: bool = True -) -> Waveform: - r"""Generates unnormalized sech :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp`` and :math:`\sigma=` ``sigma``, applies the ``midpoint`` sampling strategy - to generate a discrete pulse sampled from the continuous function: - - .. math:: - - f(x) = A\text{sech}\left(\frac{x-\mu}{\sigma} \right) - - with the center :math:`\mu=` ``duration/2``. - - If ``zero_ends==True``, each output sample :math:`y` is modified according to: - - .. math:: - - y \mapsto A\frac{y-y^*}{A-y^*}, - - where :math:`y^*` is the value of the endpoint samples. This sets the endpoints - to :math:`0` while preserving the amplitude at the center. If :math:`A=y^*`, - :math:`y` is set to :math:`1`. By default, the endpoints are at ``x = -1, x = duration + 1``. - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude at `duration/2`. - sigma: Width (standard deviation) of pulse. - name: Name of pulse. - zero_ends: If True, zero ends at ``x = -1, x = duration + 1``, but rescale to preserve amp. - """ - center = duration / 2 - zeroed_width = duration + 2 if zero_ends else None - rescale_amp = bool(zero_ends) - return _sampled_sech_pulse( - duration, amp, center, sigma, zeroed_width=zeroed_width, rescale_amp=rescale_amp, name=name - ) - - -_sampled_sech_deriv_pulse = samplers.midpoint(continuous.sech_deriv) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including sech_deriv() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.SechDeriv(...).get_waveform()." - " Note that pulse.SechDeriv() does not support complex values for `amp`." - " Instead, use two float values for (`amp`, `angle`).", - pending=True, -) -def sech_deriv(duration: int, amp: complex, sigma: float, name: str = None) -> Waveform: - r"""Generates unnormalized sech derivative :class:`~qiskit.pulse.library.Waveform`. - - For :math:`A=` ``amp``, :math:`\sigma=` ``sigma``, and center :math:`\mu=` ``duration/2``, - applies the `midpoint` sampling strategy to generate a discrete pulse sampled from - the continuous function: - - .. math:: - f(x) = \frac{d}{dx}\left[A\text{sech}\left(\frac{x-\mu}{\sigma} \right)\right], - - i.e. the derivative of :math:`\text{sech}`. - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude at `center`. - sigma: Width (standard deviation) of pulse. - name: Name of pulse. - """ - center = duration / 2 - return _sampled_sech_deriv_pulse(duration, amp, center, sigma, name=name) - - -_sampled_gaussian_square_pulse = samplers.midpoint(continuous.gaussian_square) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including gaussian_square() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.GaussianSquare(...).get_waveform()." - " Note that complex value support for the `amp` parameter is pending deprecation" - " in the SymbolicPulse library. It is therefore recommended to use two float values" - " for (`amp`, `angle`) instead of complex `amp`", - pending=True, -) -def gaussian_square( - duration: int, - amp: complex, - sigma: float, - risefall: Optional[float] = None, - width: Optional[float] = None, - name: Optional[str] = None, - zero_ends: bool = True, -) -> Waveform: - r"""Generates gaussian square :class:`~qiskit.pulse.library.Waveform`. - - For :math:`d=` ``duration``, :math:`A=` ``amp``, :math:`\sigma=` ``sigma``, - and :math:`r=` ``risefall``, applies the ``midpoint`` sampling strategy to - generate a discrete pulse sampled from the continuous function: - - .. math:: - - f(x) = \begin{cases} - g(x - r) ) & x\leq r \\ - A & r\leq x\leq d-r \\ - g(x - (d - r)) & d-r\leq x - \end{cases} - - where :math:`g(x)` is the Gaussian function sampled from in :meth:`gaussian` - with :math:`A=` ``amp``, :math:`\mu=1`, and :math:`\sigma=` ``sigma``. I.e. - :math:`f(x)` represents a square pulse with smooth Gaussian edges. - - If ``zero_ends == True``, the samples for the Gaussian ramps are remapped as in - :meth:`gaussian`. - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude. - sigma: Width (standard deviation) of Gaussian rise/fall portion of the pulse. - risefall: Number of samples over which pulse rise and fall happen. Width of - square portion of pulse will be ``duration-2*risefall``. - width: The duration of the embedded square pulse. Only one of ``width`` or ``risefall`` - should be specified as the functional form requires - ``width = duration - 2 * risefall``. - name: Name of pulse. - zero_ends: If True, zero ends at ``x = -1, x = duration + 1``, but rescale to preserve amp. - - Raises: - PulseError: If ``risefall`` and ``width`` arguments are inconsistent or not enough info. - """ - if risefall is None and width is None: - raise PulseError("gaussian_square missing required argument: 'width' or 'risefall'.") - if risefall is not None: - if width is None: - width = duration - 2 * risefall - elif 2 * risefall + width != duration: - raise PulseError( - "Both width and risefall were specified, and they are " - "inconsistent: 2 * risefall + width == {} != " - "duration == {}.".format(2 * risefall + width, duration) - ) - center = duration / 2 - zeroed_width = duration + 2 if zero_ends else None - return _sampled_gaussian_square_pulse( - duration, amp, center, width, sigma, zeroed_width=zeroed_width, name=name - ) - - -_sampled_drag_pulse = samplers.midpoint(continuous.drag) - - -@deprecate_func( - since="0.25.0", - additional_msg="The discrete pulses library, including drag() is pending deprecation." - " Instead, use the SymbolicPulse library to create the waveform with" - " pulse.Drag(...).get_waveform()." - " Note that complex value support for the `amp` parameter is pending deprecation" - " in the SymbolicPulse library. It is therefore recommended to use two float values" - " for (`amp`, `angle`) instead of complex `amp`", - pending=True, -) -def drag( - duration: int, - amp: complex, - sigma: float, - beta: float, - name: Optional[str] = None, - zero_ends: bool = True, -) -> Waveform: - r"""Generates Y-only correction DRAG :class:`~qiskit.pulse.library.Waveform` for standard nonlinear - oscillator (SNO) [1]. - - For :math:`A=` ``amp``, :math:`\sigma=` ``sigma``, and :math:`\beta=` ``beta``, applies the - ``midpoint`` sampling strategy to generate a discrete pulse sampled from the - continuous function: - - .. math:: - - f(x) = g(x) + i \beta h(x), - - where :math:`g(x)` is the function sampled in :meth:`gaussian`, and :math:`h(x)` - is the function sampled in :meth:`gaussian_deriv`. - - If ``zero_ends == True``, the samples from :math:`g(x)` are remapped as in :meth:`gaussian`. - - References: - 1. |citation1|_ - - .. _citation1: http://dx.doi.org/10.1103/PhysRevA.83.012308 - - .. |citation1| replace:: *Gambetta, J. M., Motzoi, F., Merkel, S. T. & Wilhelm, F. K. - "Analytic control methods for high-fidelity unitary operations - in a weakly nonlinear oscillator." Phys. Rev. A 83, 012308 (2011).* - - Args: - duration: Duration of pulse. Must be greater than zero. - amp: Pulse amplitude at center ``duration/2``. - sigma: Width (standard deviation) of pulse. - beta: Y correction amplitude. For the SNO this is - :math:`\beta=-\frac{\lambda_1^2}{4\Delta_2}`. Where :math:`\lambda_1` is the - relative coupling strength between the first excited and second excited states - and :math:`\Delta_2` is the detuning between the respective excited states. - name: Name of pulse. - zero_ends: If True, zero ends at ``x = -1, x = duration + 1``, but rescale to preserve amp. - """ - center = duration / 2 - zeroed_width = duration + 2 if zero_ends else None - rescale_amp = bool(zero_ends) - return _sampled_drag_pulse( - duration, - amp, - center, - sigma, - beta, - zeroed_width=zeroed_width, - rescale_amp=rescale_amp, - name=name, - ) diff --git a/qiskit/pulse/library/parametric_pulses.py b/qiskit/pulse/library/parametric_pulses.py deleted file mode 100644 index 9251c1ded9a9..000000000000 --- a/qiskit/pulse/library/parametric_pulses.py +++ /dev/null @@ -1,603 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Parametric waveforms module. These are pulses which are described by a specified -parameterization. - -If a backend supports parametric pulses, it will have the attribute -`backend.configuration().parametric_pulses`, which is a list of supported pulse shapes, such as -`['gaussian', 'gaussian_square', 'drag']`. A Pulse Schedule, using parametric pulses, which is -assembled for a backend which supports those pulses, will result in a Qobj which is dramatically -smaller than one which uses Waveforms. - -This module can easily be extended to describe more pulse shapes. The new class should: - - have a descriptive name - - be a well known and/or well described formula (include the formula in the class docstring) - - take some parameters (at least `duration`) and validate them, if necessary - - implement a ``get_waveform`` method which returns a corresponding Waveform in the case that - it is assembled for a backend which does not support it. Ends are zeroed to avoid steep jumps at - pulse edges. By default, the ends are defined such that ``f(-1), f(duration+1) = 0``. - -The new pulse must then be registered by the assembler in -`qiskit/qobj/converters/pulse_instruction.py:ParametricPulseShapes` -by following the existing pattern: - - class ParametricPulseShapes(Enum): - gaussian = library.Gaussian - ... - new_supported_pulse_name = library.YourPulseWaveformClass -""" -from abc import abstractmethod -from typing import Any, Dict, Optional, Union - -import math -import numpy as np - -from qiskit.circuit.parameterexpression import ParameterExpression -from qiskit.pulse.exceptions import PulseError -from qiskit.pulse.library import continuous -from qiskit.pulse.library.discrete import gaussian, gaussian_square, drag, constant -from qiskit.pulse.library.pulse import Pulse -from qiskit.pulse.library.waveform import Waveform -from qiskit.utils.deprecation import deprecate_func - - -class ParametricPulse(Pulse): - """The abstract superclass for parametric pulses. - - .. warning:: - - This class is superseded by :class:`.SymbolicPulse` and will be deprecated - and eventually removed in the future because of the poor flexibility - for defining a new waveform type and serializing it through the :mod:`qiskit.qpy` framework. - - """ - - @abstractmethod - @deprecate_func( - additional_msg=( - "Instead, use SymbolicPulse because of QPY serialization support. See " - "qiskit.pulse.library.symbolic_pulses for details." - ), - since="0.22", - pending=True, - ) - def __init__( - self, - duration: Union[int, ParameterExpression], - name: Optional[str] = None, - limit_amplitude: Optional[bool] = None, - ): - """Create a parametric pulse and validate the input parameters. - - Args: - duration: Pulse length in terms of the sampling period `dt`. - name: Display name for this pulse envelope. - limit_amplitude: If ``True``, then limit the amplitude of the - waveform to 1. The default is ``True`` and the - amplitude is constrained to 1. - """ - super().__init__(duration=duration, name=name, limit_amplitude=limit_amplitude) - self.validate_parameters() - - @abstractmethod - def get_waveform(self) -> Waveform: - """Return a Waveform with samples filled according to the formula that the pulse - represents and the parameter values it contains. - """ - raise NotImplementedError - - @abstractmethod - def validate_parameters(self) -> None: - """ - Validate parameters. - - Raises: - PulseError: If the parameters passed are not valid. - """ - raise NotImplementedError - - def is_parameterized(self) -> bool: - """Return True iff the instruction is parameterized.""" - return any(_is_parameterized(val) for val in self.parameters.values()) - - def __eq__(self, other: Pulse) -> bool: - return super().__eq__(other) and self.parameters == other.parameters - - def __hash__(self) -> int: - return hash(tuple(self.parameters[k] for k in sorted(self.parameters))) - - -class Gaussian(ParametricPulse): - r"""A lifted and truncated pulse envelope shaped according to the Gaussian function whose - mean is centered at the center of the pulse (duration / 2): - - .. math:: - - f'(x) &= \exp\Bigl( -\frac12 \frac{{(x - \text{duration}/2)}^2}{\text{sigma}^2} \Bigr)\\ - f(x) &= \text{amp} \times \frac{f'(x) - f'(-1)}{1-f'(-1)}, \quad 0 \le x < \text{duration} - - where :math:`f'(x)` is the gaussian waveform without lifting or amplitude scaling. - - This pulse would be more accurately named as ``LiftedGaussian``, however, for historical - and practical DSP reasons it has the name ``Gaussian``. - """ - - @deprecate_func( - additional_msg=( - "Instead, use Gaussian from qiskit.pulse.library.symbolic_pulses because of " - "QPY serialization support." - ), - since="0.22", - pending=True, - ) - def __init__( - self, - duration: Union[int, ParameterExpression], - amp: Union[complex, ParameterExpression], - sigma: Union[float, ParameterExpression], - name: Optional[str] = None, - limit_amplitude: Optional[bool] = None, - ): - """Initialize the gaussian pulse. - - Args: - duration: Pulse length in terms of the sampling period `dt`. - amp: The amplitude of the Gaussian envelope. - sigma: A measure of how wide or narrow the Gaussian peak is; described mathematically - in the class docstring. - name: Display name for this pulse envelope. - limit_amplitude: If ``True``, then limit the amplitude of the - waveform to 1. The default is ``True`` and the - amplitude is constrained to 1. - """ - if not _is_parameterized(amp): - amp = complex(amp) - self._amp = amp - self._sigma = sigma - super().__init__(duration=duration, name=name, limit_amplitude=limit_amplitude) - - @property - def amp(self) -> Union[complex, ParameterExpression]: - """The Gaussian amplitude.""" - return self._amp - - @property - def sigma(self) -> Union[float, ParameterExpression]: - """The Gaussian standard deviation of the pulse width.""" - return self._sigma - - def get_waveform(self) -> Waveform: - return gaussian(duration=self.duration, amp=self.amp, sigma=self.sigma, zero_ends=True) - - def validate_parameters(self) -> None: - if not _is_parameterized(self.amp) and abs(self.amp) > 1.0 and self._limit_amplitude: - raise PulseError( - f"The amplitude norm must be <= 1, found: {abs(self.amp)}" - + "This can be overruled by setting Pulse.limit_amplitude." - ) - if not _is_parameterized(self.sigma) and self.sigma <= 0: - raise PulseError("Sigma must be greater than 0.") - - @property - def parameters(self) -> Dict[str, Any]: - return {"duration": self.duration, "amp": self.amp, "sigma": self.sigma} - - def __repr__(self) -> str: - return "{}(duration={}, amp={}, sigma={}{})".format( - self.__class__.__name__, - self.duration, - self.amp, - self.sigma, - f", name='{self.name}'" if self.name is not None else "", - ) - - -class GaussianSquare(ParametricPulse): - # Not a raw string because we need to be able to split lines. - """A square pulse with a Gaussian shaped risefall on both sides lifted such that - its first sample is zero. - - Either the ``risefall_sigma_ratio`` or ``width`` parameter has to be specified. - - If ``risefall_sigma_ratio`` is not None and ``width`` is None: - - .. math:: - - \\text{risefall} &= \\text{risefall_sigma_ratio} \\times \\text{sigma}\\\\ - \\text{width} &= \\text{duration} - 2 \\times \\text{risefall} - - If ``width`` is not None and ``risefall_sigma_ratio`` is None: - - .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} - - In both cases, the lifted gaussian square pulse :math:`f'(x)` is defined as: - - .. math:: - - f'(x) &= \\begin{cases}\ - \\exp\\biggl(-\\frac12 \\frac{(x - \\text{risefall})^2}{\\text{sigma}^2}\\biggr)\ - & x < \\text{risefall}\\\\ - 1\ - & \\text{risefall} \\le x < \\text{risefall} + \\text{width}\\\\ - \\exp\\biggl(-\\frac12\ - \\frac{{\\bigl(x - (\\text{risefall} + \\text{width})\\bigr)}^2}\ - {\\text{sigma}^2}\ - \\biggr)\ - & \\text{risefall} + \\text{width} \\le x\ - \\end{cases}\\\\ - f(x) &= \\text{amp} \\times \\frac{f'(x) - f'(-1)}{1-f'(-1)},\ - \\quad 0 \\le x < \\text{duration} - - where :math:`f'(x)` is the gaussian square waveform without lifting or amplitude scaling. - - This pulse would be more accurately named as ``LiftedGaussianSquare``, however, for historical - and practical DSP reasons it has the name ``GaussianSquare``. - """ - - @deprecate_func( - additional_msg=( - "Instead, use GaussianSquare from qiskit.pulse.library.symbolic_pulses because of " - "QPY serialization support." - ), - since="0.22", - pending=True, - ) - def __init__( - self, - duration: Union[int, ParameterExpression], - amp: Union[complex, ParameterExpression], - sigma: Union[float, ParameterExpression], - width: Union[float, ParameterExpression] = None, - risefall_sigma_ratio: Union[float, ParameterExpression] = None, - name: Optional[str] = None, - limit_amplitude: Optional[bool] = None, - ): - """Initialize the gaussian square pulse. - - Args: - duration: Pulse length in terms of the sampling period `dt`. - amp: The amplitude of the Gaussian and of the square pulse. - sigma: A measure of how wide or narrow the Gaussian risefall is; see the class - docstring for more details. - width: The duration of the embedded square pulse. - risefall_sigma_ratio: The ratio of each risefall duration to sigma. - name: Display name for this pulse envelope. - limit_amplitude: If ``True``, then limit the amplitude of the - waveform to 1. The default is ``True`` and the - amplitude is constrained to 1. - - Raises: - PulseError: If the parameters passed are not valid. - """ - if not _is_parameterized(amp): - amp = complex(amp) - self._amp = amp - self._sigma = sigma - self._risefall_sigma_ratio = risefall_sigma_ratio - self._width = width - - if self.width is not None and self.risefall_sigma_ratio is not None: - raise PulseError( - "Either the pulse width or the risefall_sigma_ratio parameter can be specified" - " but not both." - ) - - super().__init__(duration=duration, name=name, limit_amplitude=limit_amplitude) - - @property - def amp(self) -> Union[complex, ParameterExpression]: - """The Gaussian amplitude.""" - return self._amp - - @property - def sigma(self) -> Union[float, ParameterExpression]: - """The Gaussian standard deviation of the pulse width.""" - return self._sigma - - @property - def risefall_sigma_ratio(self) -> Union[float, ParameterExpression]: - """The duration of each risefall in terms of sigma.""" - return self._risefall_sigma_ratio - - @property - def width(self) -> Union[float, ParameterExpression]: - """The width of the square portion of the pulse.""" - return self._width - - def get_waveform(self) -> Waveform: - return gaussian_square( - duration=self.duration, amp=self.amp, width=self.width, sigma=self.sigma, zero_ends=True - ) - - def validate_parameters(self) -> None: - if not _is_parameterized(self.amp) and abs(self.amp) > 1.0 and self._limit_amplitude: - raise PulseError( - f"The amplitude norm must be <= 1, found: {abs(self.amp)}" - + "This can be overruled by setting Pulse.limit_amplitude." - ) - if not _is_parameterized(self.sigma) and self.sigma <= 0: - raise PulseError("Sigma must be greater than 0.") - if self.width is None and self.risefall_sigma_ratio is None: - raise PulseError( - "Either the pulse width or the risefall_sigma_ratio parameter must be specified." - ) - if self.width is not None: - if not _is_parameterized(self.width) and self.width < 0: - raise PulseError("The pulse width must be at least 0.") - if ( - not (_is_parameterized(self.width) or _is_parameterized(self.duration)) - and self.width >= self.duration - ): - raise PulseError("The pulse width must be less than its duration.") - self._risefall_sigma_ratio = (self.duration - self.width) / (2.0 * self.sigma) - - else: - if not _is_parameterized(self.risefall_sigma_ratio) and self.risefall_sigma_ratio <= 0: - raise PulseError("The parameter risefall_sigma_ratio must be greater than 0.") - if not ( - _is_parameterized(self.risefall_sigma_ratio) - or _is_parameterized(self.duration) - or _is_parameterized(self.sigma) - ) and self.risefall_sigma_ratio >= self.duration / (2.0 * self.sigma): - raise PulseError( - "The parameter risefall_sigma_ratio must be less than duration/(" - "2*sigma)={}.".format(self.duration / (2.0 * self.sigma)) - ) - self._width = self.duration - 2.0 * self.risefall_sigma_ratio * self.sigma - - @property - def parameters(self) -> Dict[str, Any]: - return { - "duration": self.duration, - "amp": self.amp, - "sigma": self.sigma, - "width": self.width, - } - - def __repr__(self) -> str: - return "{}(duration={}, amp={}, sigma={}, width={}{})".format( - self.__class__.__name__, - self.duration, - self.amp, - self.sigma, - self.width, - f", name='{self.name}'" if self.name is not None else "", - ) - - -class Drag(ParametricPulse): - """The Derivative Removal by Adiabatic Gate (DRAG) pulse is a standard Gaussian pulse - with an additional Gaussian derivative component and lifting applied. - - It is designed to reduce the frequency spectrum of a standard Gaussian pulse near - the :math:`|1\\rangle\\leftrightarrow|2\\rangle` transition, - reducing the chance of leakage to the :math:`|2\\rangle` state. - - .. math:: - - g(x) &= \\exp\\Bigl(-\\frac12 \\frac{(x - \\text{duration}/2)^2}{\\text{sigma}^2}\\Bigr)\\\\ - g'(x) &= \\text{amp}\\times\\frac{g(x)-g(-1)}{1-g(-1)}\\\\ - f(x) &= g'(x) \\times \\Bigl(1 + 1j \\times \\text{beta} \\times\ - \\Bigl(-\\frac{x - \\text{duration}/2}{\\text{sigma}^2}\\Bigr) \\Bigr), - \\quad 0 \\le x < \\text{duration} - - where :math:`g(x)` is a standard unlifted Gaussian waveform and - :math:`g'(x)` is the lifted :class:`~qiskit.pulse.library.Gaussian` waveform. - - This pulse, defined by :math:`f(x)`, would be more accurately named as ``LiftedDrag``, however, - for historical and practical DSP reasons it has the name ``Drag``. - - References: - 1. |citation1|_ - - .. _citation1: https://link.aps.org/doi/10.1103/PhysRevA.83.012308 - - .. |citation1| replace:: *Gambetta, J. M., Motzoi, F., Merkel, S. T. & Wilhelm, F. K. - Analytic control methods for high-fidelity unitary operations - in a weakly nonlinear oscillator. Phys. Rev. A 83, 012308 (2011).* - - 2. |citation2|_ - - .. _citation2: https://link.aps.org/doi/10.1103/PhysRevLett.103.110501 - - .. |citation2| replace:: *F. Motzoi, J. M. Gambetta, P. Rebentrost, and F. K. Wilhelm - Phys. Rev. Lett. 103, 110501 – Published 8 September 2009.* - """ - - @deprecate_func( - additional_msg=( - "Instead, use Drag from qiskit.pulse.library.symbolic_pulses because of " - "QPY serialization support." - ), - since="0.22", - pending=True, - ) - def __init__( - self, - duration: Union[int, ParameterExpression], - amp: Union[complex, ParameterExpression], - sigma: Union[float, ParameterExpression], - beta: Union[float, ParameterExpression], - name: Optional[str] = None, - limit_amplitude: Optional[bool] = None, - ): - """Initialize the drag pulse. - - Args: - duration: Pulse length in terms of the sampling period `dt`. - amp: The amplitude of the Drag envelope. - sigma: A measure of how wide or narrow the Gaussian peak is; described mathematically - in the class docstring. - beta: The correction amplitude. - name: Display name for this pulse envelope. - limit_amplitude: If ``True``, then limit the amplitude of the - waveform to 1. The default is ``True`` and the - amplitude is constrained to 1. - """ - if not _is_parameterized(amp): - amp = complex(amp) - self._amp = amp - self._sigma = sigma - self._beta = beta - super().__init__(duration=duration, name=name, limit_amplitude=limit_amplitude) - - @property - def amp(self) -> Union[complex, ParameterExpression]: - """The Gaussian amplitude.""" - return self._amp - - @property - def sigma(self) -> Union[float, ParameterExpression]: - """The Gaussian standard deviation of the pulse width.""" - return self._sigma - - @property - def beta(self) -> Union[float, ParameterExpression]: - """The weighing factor for the Gaussian derivative component of the waveform.""" - return self._beta - - def get_waveform(self) -> Waveform: - return drag( - duration=self.duration, amp=self.amp, sigma=self.sigma, beta=self.beta, zero_ends=True - ) - - def validate_parameters(self) -> None: - if not _is_parameterized(self.amp) and abs(self.amp) > 1.0 and self._limit_amplitude: - raise PulseError( - f"The amplitude norm must be <= 1, found: {abs(self.amp)}" - + "This can be overruled by setting Pulse.limit_amplitude." - ) - if not _is_parameterized(self.sigma) and self.sigma <= 0: - raise PulseError("Sigma must be greater than 0.") - if not _is_parameterized(self.beta) and isinstance(self.beta, complex): - raise PulseError("Beta must be real.") - # Check if beta is too large: the amplitude norm must be <=1 for all points - if ( - not _is_parameterized(self.beta) - and not _is_parameterized(self.sigma) - and np.abs(self.beta) > self.sigma - and self._limit_amplitude - ): - # If beta <= sigma, then the maximum amplitude is at duration / 2, which is - # already constrained by self.amp <= 1 - - # 1. Find the first maxima associated with the beta * d/dx gaussian term - # This eq is derived from solving for the roots of the norm of the drag function. - # There is a second maxima mirrored around the center of the pulse with the same - # norm as the first, so checking the value at the first x maxima is sufficient. - argmax_x = self.duration / 2 - (self.sigma / self.beta) * math.sqrt( - self.beta**2 - self.sigma**2 - ) - # If the max point is out of range, either end of the pulse will do - argmax_x = max(argmax_x, 0) - - # 2. Find the value at that maximum - max_val = continuous.drag( - np.array(argmax_x), - sigma=self.sigma, - beta=self.beta, - amp=self.amp, - center=self.duration / 2, - ) - if abs(max_val) > 1.0: - raise PulseError("Beta is too large; pulse amplitude norm exceeds 1.") - - @property - def parameters(self) -> Dict[str, Any]: - return {"duration": self.duration, "amp": self.amp, "sigma": self.sigma, "beta": self.beta} - - def __repr__(self) -> str: - return "{}(duration={}, amp={}, sigma={}, beta={}{})".format( - self.__class__.__name__, - self.duration, - self.amp, - self.sigma, - self.beta, - f", name='{self.name}'" if self.name is not None else "", - ) - - -class Constant(ParametricPulse): - """ - A simple constant pulse, with an amplitude value and a duration: - - .. math:: - - f(x) = amp , 0 <= x < duration - f(x) = 0 , elsewhere - """ - - @deprecate_func( - additional_msg=( - "Instead, use Constant from qiskit.pulse.library.symbolic_pulses because of " - "QPY serialization support." - ), - since="0.22", - pending=True, - ) - def __init__( - self, - duration: Union[int, ParameterExpression], - amp: Union[complex, ParameterExpression], - name: Optional[str] = None, - limit_amplitude: Optional[bool] = None, - ): - """ - Initialize the constant-valued pulse. - - Args: - duration: Pulse length in terms of the sampling period `dt`. - amp: The amplitude of the constant square pulse. - name: Display name for this pulse envelope. - limit_amplitude: If ``True``, then limit the amplitude of the - waveform to 1. The default is ``True`` and the - amplitude is constrained to 1. - """ - if not _is_parameterized(amp): - amp = complex(amp) - self._amp = amp - super().__init__(duration=duration, name=name, limit_amplitude=limit_amplitude) - - @property - def amp(self) -> Union[complex, ParameterExpression]: - """The constant value amplitude.""" - return self._amp - - def get_waveform(self) -> Waveform: - return constant(duration=self.duration, amp=self.amp) - - def validate_parameters(self) -> None: - if not _is_parameterized(self.amp) and abs(self.amp) > 1.0 and self._limit_amplitude: - raise PulseError( - f"The amplitude norm must be <= 1, found: {abs(self.amp)}" - + "This can be overruled by setting Pulse.limit_amplitude." - ) - - @property - def parameters(self) -> Dict[str, Any]: - return {"duration": self.duration, "amp": self.amp} - - def __repr__(self) -> str: - return "{}(duration={}, amp={}{})".format( - self.__class__.__name__, - self.duration, - self.amp, - f", name='{self.name}'" if self.name is not None else "", - ) - - -def _is_parameterized(value: Any) -> bool: - """Shorthand for a frequently checked predicate. ParameterExpressions cannot be - validated until they are numerically assigned. - """ - return isinstance(value, ParameterExpression) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index ca3c6a516d2b..4621aa2a4f38 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -23,19 +23,14 @@ from copy import deepcopy import numpy as np +import symengine as sym from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType from qiskit.pulse.exceptions import PulseError from qiskit.pulse.library.pulse import Pulse from qiskit.pulse.library.waveform import Waveform -from qiskit.utils import optionals as _optional from qiskit.utils.deprecation import deprecate_arg -if _optional.HAS_SYMENGINE: - import symengine as sym -else: - import sympy as sym - def _lifted_gaussian( t: sym.Symbol, @@ -183,34 +178,31 @@ def __set__(self, instance, value): continue params.append(p) - if _optional.HAS_SYMENGINE: - try: - lamb = sym.lambdify(params, [value], real=False) - - def _wrapped_lamb(*args): - if isinstance(args[0], np.ndarray): - # When the args[0] is a vector ("t"), tile other arguments args[1:] - # to prevent evaluation from looping over each element in t. - t = args[0] - args = np.hstack( - ( - t.reshape(t.size, 1), - np.tile(args[1:], t.size).reshape(t.size, len(args) - 1), - ) + try: + lamb = sym.lambdify(params, [value], real=False) + + def _wrapped_lamb(*args): + if isinstance(args[0], np.ndarray): + # When the args[0] is a vector ("t"), tile other arguments args[1:] + # to prevent evaluation from looping over each element in t. + t = args[0] + args = np.hstack( + ( + t.reshape(t.size, 1), + np.tile(args[1:], t.size).reshape(t.size, len(args) - 1), ) - return lamb(args) + ) + return lamb(args) - func = _wrapped_lamb - except RuntimeError: - # Currently symengine doesn't support complex_double version for - # several functions such as comparison operator and piecewise. - # If expression contains these function, it fall back to sympy lambdify. - # See https://github.com/symengine/symengine.py/issues/406 for details. - import sympy + func = _wrapped_lamb + except RuntimeError: + # Currently symengine doesn't support complex_double version for + # several functions such as comparison operator and piecewise. + # If expression contains these function, it fall back to sympy lambdify. + # See https://github.com/symengine/symengine.py/issues/406 for details. + import sympy - func = sympy.lambdify(params, value) - else: - func = sym.lambdify(params, value) + func = sympy.lambdify(params, value) self.lambda_funcs[key] = func @@ -611,6 +603,7 @@ class ScalableSymbolicPulse(SymbolicPulse): "Instead, use a float for ``amp`` (for the magnitude) and a float for ``angle``" ), since="0.25.0", + package_name="qiskit-terra", pending=False, predicate=lambda amp: isinstance(amp, complex), ) @@ -1472,8 +1465,7 @@ def __new__( # Note this is implemented using Piecewise instead of just returning amp # directly because otherwise the expression has no t dependence and sympy's # lambdify will produce a function f that for an array t returns amp - # instead of amp * np.ones(t.shape). This does not work well with - # ParametricPulse.get_waveform(). + # instead of amp * np.ones(t.shape). # # See: https://github.com/sympy/sympy/issues/5642 envelope_expr = ( diff --git a/qiskit/pulse/model/__init__.py b/qiskit/pulse/model/__init__.py deleted file mode 100644 index 05cd4513359c..000000000000 --- a/qiskit/pulse/model/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -r""" -========================================================== -Logical Elements & Frames (:mod:`qiskit.pulse.model`) -========================================================== - -Pulse is meant to be agnostic to the underlying hardware implementation, while still allowing -low-level control. Qiskit Pulse's logical element and frames create a flexible framework -to define where pulse instructions are applied, and what would be their carrier frequency and phase -(because typically AC pulses are used). Each :class:`.LogicalElement` represents a separate component -in the quantum computing system on which instructions could be applied. On the other hand, -each :class:`.Frame` represents a frequency and phase duo for the carrier of the pulse. - -This logical and virtual representation allows the user to write template pulse -programs without worrying about the exact details of the hardware implementation -(are the pulses to be played via the same port? Which NCO is used?), while still -allowing for effective utilization of the quantum hardware. The burden of mapping -the different combinations of :class:`.LogicalElement` and :class:`.Frame` -to hardware aware objects is left to the Pulse Compiler. - -.. _logical_elements: - -LogicalElement -================ -:class:`.LogicalElement` s are identified by their type and index. Currently, the most prominent example -is the :class:`~.pulse.Qubit`. - -.. autosummary:: - :toctree: ../stubs/ - - Qubit - Coupler - - -.. _frames: - -Frame -============= -:class:`.Frame` s are identified by their type and unique identifier. A :class:`.GenericFrame` is used to -specify custom frequency -and phase duos, while :class:`.QubitFrame` and :class:`.MeasurementFrame` are used to indicate that -backend defaults are to be used (for the qubit's driving frequency and measurement frequency -respectively). - -.. autosummary:: - :toctree: ../stubs/ - - GenericFrame - QubitFrame - MeasurementFrame - - -.. _mixed_frames: - -MixedFrame -============= -The combination of a :class:`.LogicalElement` and :class:`.Frame` is dubbed a :class:`.MixedFrame`. - -.. autosummary:: - :toctree: ../stubs/ - - MixedFrame -""" - -from .logical_elements import ( - LogicalElement, - Qubit, - Coupler, -) - -from .frames import ( - Frame, - GenericFrame, - QubitFrame, - MeasurementFrame, -) - -from .mixed_frames import ( - MixedFrame, -) diff --git a/qiskit/pulse/model/frames.py b/qiskit/pulse/model/frames.py deleted file mode 100644 index ae63fe3c7948..000000000000 --- a/qiskit/pulse/model/frames.py +++ /dev/null @@ -1,162 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Frames -""" - -from abc import ABC - -import numpy as np - -from qiskit.pulse.exceptions import PulseError - - -class Frame(ABC): - """Base class for pulse module frame. - - Because pulses used in Quantum hardware are typically AC pulses, the carrier frequency and phase - must be defined. The :class:`Frame` is the object which identifies the frequency and phase for - the carrier. - and each pulse and most other instructions are associated with a frame. The different types of frames - dictate how the frequency and phase duo are defined. - - The default initial phase for every frame is 0. - """ - - def __init__(self, identifier): - """Create ``Frame``. - - Args: - identifier: A unique identifier used to hash the Frame. - """ - self._hash = hash((type(self), identifier)) - - def __eq__(self, other: "Frame") -> bool: - """Return True iff self and other are equal, specifically, iff they have the same type and hash. - - Args: - other: The frame to compare to this one. - - Returns: - True iff equal. - """ - return type(self) is type(other) and self._hash == other._hash - - def __hash__(self) -> int: - return self._hash - - -class GenericFrame(Frame): - """Pulse module GenericFrame. - - The :class:`GenericFrame` is used for custom user defined frames, which are not associated with any - backend defaults. It is especially useful when the frame doesn't correspond to any frame of - the typical qubit model, like qudit control for example. Because no backend defaults exist for - these frames, during compilation an initial frequency and phase will need to be provided. - - :class:`GenericFrame` objects are identified by their unique name. - """ - - def __init__(self, name: str): - """Create ``GenericFrame``. - - Args: - name: A unique identifier used to identify the frame. - """ - self._name = name - super().__init__(name) - - @property - def name(self) -> str: - """Return the name of the frame.""" - return self._name - - def __repr__(self) -> str: - return f"GenericFrame({self._name})" - - -class QubitFrame(Frame): - """A frame associated with the driving of a qubit. - - :class:`QubitFrame` is a frame associated with the driving of a specific qubit. - The initial frequency of - the frame will be taken as the default driving frequency provided by the backend - during compilation. - """ - - def __init__(self, index: int): - """Create ``QubitFrame``. - - Args: - index: The index of the qubit represented by the frame. - """ - self._validate_index(index) - self._index = index - super().__init__("QubitFrame" + str(index)) - - @property - def index(self) -> int: - """Return the qubit index of the qubit frame.""" - return self._index - - def _validate_index(self, index) -> None: - """Raise a ``PulseError`` if the qubit index is invalid. Namely, check if the index is a - non-negative integer. - - Raises: - PulseError: If ``identifier`` (index) is a negative integer. - """ - if not isinstance(index, (int, np.integer)) or index < 0: - raise PulseError("Qubit index must be a non-negative integer") - - def __repr__(self) -> str: - return f"QubitFrame({self._index})" - - -class MeasurementFrame(Frame): - """A frame associated with the measurement of a qubit. - - ``MeasurementFrame`` is a frame associated with the readout of a specific qubit, - which requires a stimulus tone driven at frequency off resonant to qubit drive. - - If not set otherwise, the initial frequency of the frame will be taken as the default - measurement frequency provided by the backend during compilation. - """ - - def __init__(self, index: int): - """Create ``MeasurementFrame``. - - Args: - index: The index of the qubit represented by the frame. - """ - self._validate_index(index) - self._index = index - super().__init__("MeasurementFrame" + str(index)) - - @property - def index(self) -> int: - """Return the qubit index of the measurement frame.""" - return self._index - - def _validate_index(self, index) -> None: - """Raise a ``PulseError`` if the qubit index is invalid. Namely, check if the index is a - non-negative integer. - - Raises: - PulseError: If ``index`` is a negative integer. - """ - if not isinstance(index, (int, np.integer)) or index < 0: - raise PulseError("Qubit index must be a non-negative integer") - - def __repr__(self) -> str: - return f"MeasurementFrame({self._index})" diff --git a/qiskit/pulse/model/logical_elements.py b/qiskit/pulse/model/logical_elements.py deleted file mode 100644 index a593030c95d7..000000000000 --- a/qiskit/pulse/model/logical_elements.py +++ /dev/null @@ -1,140 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Logical Elements -""" -from abc import ABC, abstractmethod -from typing import Tuple -import numpy as np - -from qiskit.pulse.exceptions import PulseError - - -class LogicalElement(ABC): - """Base class of logical elements. - - A :class:`LogicalElement` is an abstraction of a quantum hardware component which can be controlled - by the user (instructions can be applied to it). - Every played pulse and most other instructions are associated with a :class:`LogicalElement` on which - they are performed. - A logical element is identified by its type and index. - - """ - - def __init__(self, index: Tuple[int, ...]): - """Create ``LogicalElement``. - - Args: - index: Tuple of indices of the logical element. - """ - self._validate_index(index) - self._index = index - self._hash = hash((self._index, type(self))) - - @property - def index(self) -> Tuple[int, ...]: - """Return the ``index`` of this logical element.""" - return self._index - - @abstractmethod - def _validate_index(self, index) -> None: - """Raise a PulseError if the logical element ``index`` is invalid. - - Raises: - PulseError: If ``index`` is not valid. - """ - pass - - def __eq__(self, other: "LogicalElement") -> bool: - """Return True iff self and other are equal, specifically, iff they have the same type - and the same ``index``. - - Args: - other: The logical element to compare to this one. - - Returns: - True iff equal. - """ - return type(self) is type(other) and self._index == other._index - - def __repr__(self) -> str: - ind_str = str(self._index) if len(self._index) > 1 else f"({self._index[0]})" - return type(self).__name__ + ind_str - - def __hash__(self) -> int: - return self._hash - - -class Qubit(LogicalElement): - """Qubit logical element. - - ``Qubit`` represents the different qubits in the system, as identified by - their (positive integer) index values. - """ - - def __init__(self, index: int): - """Qubit logical element. - - Args: - index: Qubit index. - """ - super().__init__((index,)) - - @property - def qubit_index(self): - """Index of the Qubit""" - return self.index[0] - - def _validate_index(self, index) -> None: - """Raise a ``PulseError`` if the qubit index is invalid. Namely, check if the index is a - non-negative integer. - - Raises: - PulseError: If ``index`` is a negative integer. - """ - if not isinstance(index[0], (int, np.integer)) or index[0] < 0: - raise PulseError("Qubit index must be a non-negative integer") - - -class Coupler(LogicalElement): - """Coupler logical element. - - :class:`Coupler` represents an element which couples qubits, and can be controlled on its own. - It is identified by the tuple of indices of the coupled qubits. - """ - - def __init__(self, *qubits): - """Coupler logical element. - - The coupler ``index`` is defined as the ``tuple`` (\\*qubits). - - Args: - *qubits: any number of qubit indices coupled by the coupler. - """ - super().__init__(tuple(qubits)) - - def _validate_index(self, index) -> None: - """Raise a ``PulseError`` if the coupler ``index`` is invalid. Namely, - check if coupled qubit indices are non-negative integers, at least two indices were provided, - and that the indices don't repeat. - - Raises: - PulseError: If ``index`` is invalid. - """ - if len(index) < 2: - raise PulseError("At least two qubit indices are needed for a Coupler") - for qubit_index in index: - if not isinstance(qubit_index, (int, np.integer)) or qubit_index < 0: - raise PulseError("Both indices of coupled qubits must be non-negative integers") - if len(set(index)) != len(index): - raise PulseError("Indices of a coupler can not repeat") diff --git a/qiskit/pulse/model/mixed_frames.py b/qiskit/pulse/model/mixed_frames.py deleted file mode 100644 index c268d6f496a1..000000000000 --- a/qiskit/pulse/model/mixed_frames.py +++ /dev/null @@ -1,78 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Mixed Frames -""" - -from .frames import Frame -from .logical_elements import LogicalElement - - -class MixedFrame: - """Representation of a :class:`LogicalElement` and :class:`Frame` combination. - - Most instructions need to be associated with both a :class:`LogicalElement` and a :class:`Frame`. - The combination - of the two is called a mixed frame and is represented by a :class:`MixedFrame` object. - - In most cases the :class:`MixedFrame` is used more by the compiler, and a pulse program - can be written without :class:`MixedFrame` s, by setting :class:`LogicalElement` and - :class:`Frame` independently. However, in some cases using :class:`MixedFrame` s can - better convey the meaning of the code, and change the compilation process. One example - is the use of the shift/set frequency/phase instructions which are not broadcasted to other - :class:`MixedFrame` s if applied on a specific :class:`MixedFrame` (unlike the behavior - of :class:`Frame`). User can also use a subclass of :class:`MixedFrame` for a particular - combination of logical elements and frames as if a syntactic sugar. This might - increase the readability of a user pulse program. As an example consider the cross - resonance architecture, in which a pulse is played on a target qubit frame and applied - to a control qubit logical element. - """ - - def __init__(self, logical_element: LogicalElement, frame: Frame): - """Create ``MixedFrame``. - - Args: - logical_element: The logical element associated with the mixed frame. - frame: The frame associated with the mixed frame. - """ - self._logical_element = logical_element - self._frame = frame - self._hash = hash((self._logical_element, self._frame)) - - @property - def logical_element(self) -> LogicalElement: - """Return the ``LogicalElement`` of this mixed frame.""" - return self._logical_element - - @property - def frame(self) -> Frame: - """Return the ``Frame`` of this mixed frame.""" - return self._frame - - def __repr__(self) -> str: - return f"MixedFrame({self.logical_element},{self.frame})" - - def __eq__(self, other: "MixedFrame") -> bool: - """Return True iff self and other are equal, specifically, iff they have the same logical - element and frame. - - Args: - other: The mixed frame to compare to this one. - - Returns: - True iff equal. - """ - return self._logical_element == other._logical_element and self._frame == other._frame - - def __hash__(self) -> int: - return self._hash diff --git a/qiskit/pulse/parameter_manager.py b/qiskit/pulse/parameter_manager.py index 4fac995b22f7..721fd9501a60 100644 --- a/qiskit/pulse/parameter_manager.py +++ b/qiskit/pulse/parameter_manager.py @@ -57,7 +57,7 @@ from qiskit.circuit.parameterexpression import ParameterExpression, ParameterValueType from qiskit.pulse import instructions, channels from qiskit.pulse.exceptions import PulseError -from qiskit.pulse.library import ParametricPulse, SymbolicPulse, Waveform +from qiskit.pulse.library import SymbolicPulse, Waveform from qiskit.pulse.schedule import Schedule, ScheduleBlock from qiskit.pulse.transforms.alignments import AlignmentKind from qiskit.pulse.utils import format_parameter_value @@ -207,19 +207,6 @@ def visit_Channel(self, node: channels.Channel): return node - def visit_ParametricPulse(self, node: ParametricPulse): - """Assign parameters to ``ParametricPulse`` object.""" - if node.is_parameterized(): - new_parameters = {} - for op, op_value in node.parameters.items(): - if isinstance(op_value, ParameterExpression): - op_value = self._assign_parameter_expression(op_value) - new_parameters[op] = op_value - - return node.__class__(**new_parameters, name=node.name) - - return node - def visit_SymbolicPulse(self, node: SymbolicPulse): """Assign parameters to ``SymbolicPulse`` object.""" if node.is_parameterized(): @@ -334,12 +321,6 @@ def visit_Channel(self, node: channels.Channel): """Get parameters from ``Channel`` object.""" self.parameters |= node.parameters - def visit_ParametricPulse(self, node: ParametricPulse): - """Get parameters from ``ParametricPulse`` object.""" - for op_value in node.parameters.values(): - if isinstance(op_value, ParameterExpression): - self.parameters |= op_value.parameters - def visit_SymbolicPulse(self, node: SymbolicPulse): """Get parameters from ``SymbolicPulse`` object.""" for op_value in node.parameters.values(): diff --git a/qiskit/qasm/__init__.py b/qiskit/qasm/__init__.py deleted file mode 100644 index 322d230343c6..000000000000 --- a/qiskit/qasm/__init__.py +++ /dev/null @@ -1,53 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -========================= -Qasm (:mod:`qiskit.qasm`) -========================= - -.. currentmodule:: qiskit.qasm - -QASM Routines -============= - -.. autoclass:: Qasm - - -Pygments -======== - -.. autoclass:: OpenQASMLexer - :class-doc-from: class - -.. autoclass:: QasmHTMLStyle - :class-doc-from: class - -.. autoclass:: QasmTerminalStyle - :class-doc-from: class -""" - -from numpy import pi - -from qiskit.utils.optionals import HAS_PYGMENTS - -from .qasm import Qasm -from .exceptions import QasmError - - -def __getattr__(name): - if name in ("OpenQASMLexer", "QasmHTMLStyle", "QasmTerminalStyle"): - import qiskit.qasm.pygments - - return getattr(qiskit.qasm.pygments, name) - - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") diff --git a/qiskit/qasm/exceptions.py b/qiskit/qasm/exceptions.py deleted file mode 100644 index 7bc40db35b8b..000000000000 --- a/qiskit/qasm/exceptions.py +++ /dev/null @@ -1,16 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Exception for errors raised while handling OpenQASM 2.0.""" - -# Re-export from the new place to ensure that old code continues to work. -from qiskit.qasm2.exceptions import QASM2Error as QasmError # pylint: disable=unused-import diff --git a/qiskit/qasm/node/__init__.py b/qiskit/qasm/node/__init__.py deleted file mode 100644 index 09e24db01bf5..000000000000 --- a/qiskit/qasm/node/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""OpenQASM 2 nodes.""" - -from .barrier import Barrier -from .binaryop import BinaryOp -from .binaryoperator import BinaryOperator -from .cnot import Cnot -from .creg import Creg -from .customunitary import CustomUnitary -from .expressionlist import ExpressionList -from .external import External -from .gate import Gate -from .gatebody import GateBody -from .id import Id -from .idlist import IdList -from .if_ import If -from .indexedid import IndexedId -from .intnode import Int -from .format import Format -from .measure import Measure -from .opaque import Opaque -from .prefix import Prefix -from .primarylist import PrimaryList -from .program import Program -from .qreg import Qreg -from .real import Real -from .reset import Reset -from .unaryoperator import UnaryOperator -from .universalunitary import UniversalUnitary -from .nodeexception import NodeException diff --git a/qiskit/qasm/node/barrier.py b/qiskit/qasm/node/barrier.py deleted file mode 100644 index afa16f2f9222..000000000000 --- a/qiskit/qasm/node/barrier.py +++ /dev/null @@ -1,30 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM barrier statement.""" - -from .node import Node - - -class Barrier(Node): - """Node for an OPENQASM barrier statement. - - children[0] is a primarylist node. - """ - - def __init__(self, children): - """Create the barrier node.""" - super().__init__("barrier", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return "barrier " + self.children[0].qasm() + ";" diff --git a/qiskit/qasm/node/binaryop.py b/qiskit/qasm/node/binaryop.py deleted file mode 100644 index 45d4de4364d0..000000000000 --- a/qiskit/qasm/node/binaryop.py +++ /dev/null @@ -1,59 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM binary operation expression.""" - -from qiskit.exceptions import MissingOptionalLibraryError -from .node import Node - - -class BinaryOp(Node): - """Node for an OPENQASM binary operation expression. - - children[0] is the operation, as a binary operator node. - children[1] is the left expression. - children[2] is the right expression. - """ - - def __init__(self, children): - """Create the binaryop node.""" - super().__init__("binop", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return ( - "(" + self.children[1].qasm() + self.children[0].value + self.children[2].qasm() + ")" - ) - - def latex(self): - """Return the corresponding math mode latex string.""" - try: - from pylatexenc.latexencode import utf8tolatex - except ImportError as ex: - raise MissingOptionalLibraryError( - "pylatexenc", "latex-from-qasm exporter", "pip install pylatexenc" - ) from ex - return utf8tolatex(self.sym()) - - def real(self): - """Return the correspond floating point number.""" - operation = self.children[0].operation() - lhs = self.children[1].real() - rhs = self.children[2].real() - return operation(lhs, rhs) - - def sym(self, nested_scope=None): - """Return the correspond symbolic number.""" - operation = self.children[0].operation() - lhs = self.children[1].sym(nested_scope) - rhs = self.children[2].sym(nested_scope) - return operation(lhs, rhs) diff --git a/qiskit/qasm/node/binaryoperator.py b/qiskit/qasm/node/binaryoperator.py deleted file mode 100644 index 57e7d883c547..000000000000 --- a/qiskit/qasm/node/binaryoperator.py +++ /dev/null @@ -1,52 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM binary operator.""" - -import operator - -from .node import Node -from .nodeexception import NodeException - - -VALID_OPERATORS = { - "+": operator.add, - "-": operator.sub, - "*": operator.mul, - "/": operator.truediv, - "^": operator.pow, -} - - -class BinaryOperator(Node): - """Node for an OPENQASM binary operator. - - This node has no children. The data is in the value field. - """ - - def __init__(self, operation): - """Create the operator node.""" - super().__init__("operator", None, None) - self.value = operation - - def operation(self): - """ - Return the operator as a function f(left, right). - """ - try: - return VALID_OPERATORS[self.value] - except KeyError as ex: - raise NodeException(f"internal error: undefined operator '{self.value}'") from ex - - def qasm(self): - """Return the OpenQASM 2 representation.""" - return self.value diff --git a/qiskit/qasm/node/cnot.py b/qiskit/qasm/node/cnot.py deleted file mode 100644 index 3034d0ca8e32..000000000000 --- a/qiskit/qasm/node/cnot.py +++ /dev/null @@ -1,31 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM CNOT statement.""" - -from .node import Node - - -class Cnot(Node): - """Node for an OPENQASM CNOT statement. - - children[0], children[1] are id nodes if CX is inside a gate body, - otherwise they are primary nodes. - """ - - def __init__(self, children): - """Create the cnot node.""" - super().__init__("cnot", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return "CX " + self.children[0].qasm() + "," + self.children[1].qasm() + ";" diff --git a/qiskit/qasm/node/creg.py b/qiskit/qasm/node/creg.py deleted file mode 100644 index 6a6bac6cb400..000000000000 --- a/qiskit/qasm/node/creg.py +++ /dev/null @@ -1,45 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM creg statement.""" -from .node import Node - - -class Creg(Node): - """Node for an OPENQASM creg statement. - - children[0] is an indexedid node. - """ - - def __init__(self, children): - """Create the creg node.""" - super().__init__("creg", children, None) - # This is the indexed id, the full "id[n]" object - self.id = children[0] # pylint: disable=invalid-name - # Name of the creg - self.name = self.id.name - # Source line number - self.line = self.id.line - # Source file name - self.file = self.id.file - # Size of the register - self.index = self.id.index - - def to_string(self, indent): - """Print the node data, with indent.""" - ind = indent * " " - print(ind, "creg") - self.children[0].to_string(indent + 3) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return "creg " + self.id.qasm() + ";" diff --git a/qiskit/qasm/node/customunitary.py b/qiskit/qasm/node/customunitary.py deleted file mode 100644 index 393cf160f3af..000000000000 --- a/qiskit/qasm/node/customunitary.py +++ /dev/null @@ -1,49 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM custom gate statement.""" -from .node import Node - - -class CustomUnitary(Node): - """Node for an OPENQASM custom gate statement. - - children[0] is an id node. - children[1] is an exp_list (if len==3) or primary_list. - children[2], if present, is a primary_list. - - Has properties: - .id = id node - .name = gate name string - .arguments = None or exp_list node - .bitlist = primary_list node - """ - - def __init__(self, children): - """Create the custom gate node.""" - super().__init__("custom_unitary", children, None) - self.id = children[0] # pylint: disable=invalid-name - self.name = self.id.name - if len(children) == 3: - self.arguments = children[1] - self.bitlist = children[2] - else: - self.arguments = None - self.bitlist = children[1] - - def qasm(self): - """Return the corresponding OPENQASM string.""" - string = self.name - if self.arguments is not None: - string += "(" + self.arguments.qasm() + ")" - string += " " + self.bitlist.qasm() + ";" - return string diff --git a/qiskit/qasm/node/expressionlist.py b/qiskit/qasm/node/expressionlist.py deleted file mode 100644 index bef4f7b2e268..000000000000 --- a/qiskit/qasm/node/expressionlist.py +++ /dev/null @@ -1,33 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM expression list.""" -from .node import Node - - -class ExpressionList(Node): - """Node for an OPENQASM expression list. - - children are expression nodes. - """ - - def __init__(self, children): - """Create the expression list node.""" - super().__init__("expression_list", children, None) - - def size(self): - """Return the number of expressions.""" - return len(self.children) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return ",".join([self.children[j].qasm() for j in range(self.size())]) diff --git a/qiskit/qasm/node/external.py b/qiskit/qasm/node/external.py deleted file mode 100644 index 2aecbf26bb62..000000000000 --- a/qiskit/qasm/node/external.py +++ /dev/null @@ -1,87 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM external function.""" - -import numpy as np - -from qiskit.exceptions import MissingOptionalLibraryError -from .node import Node -from .nodeexception import NodeException - - -class External(Node): - """Node for an OPENQASM external function. - - children[0] is an id node with the name of the function. - children[1] is an expression node. - """ - - def __init__(self, children): - """Create the external node.""" - super().__init__("external", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return self.children[0].qasm() + "(" + self.children[1].qasm() + ")" - - def latex(self): - """Return the corresponding math mode latex string.""" - try: - from pylatexenc.latexencode import utf8tolatex - except ImportError as ex: - raise MissingOptionalLibraryError( - "pylatexenc", "latex-from-qasm exporter", "pip install pylatexenc" - ) from ex - return utf8tolatex(self.sym()) - - def real(self, nested_scope=None): - """Return the correspond floating point number.""" - op = self.children[0].name - expr = self.children[1] - dispatch = { - "sin": np.sin, - "cos": np.cos, - "tan": np.tan, - "asin": np.arcsin, - "acos": np.arccos, - "atan": np.arctan, - "exp": np.exp, - "ln": np.log, - "sqrt": np.sqrt, - } - if op in dispatch: - arg = expr.real(nested_scope) - return dispatch[op](arg) - else: - raise NodeException("internal error: undefined external") - - def sym(self, nested_scope=None): - """Return the corresponding symbolic expression.""" - op = self.children[0].name - expr = self.children[1] - dispatch = { - "sin": np.sin, - "cos": np.cos, - "tan": np.tan, - "asin": np.arcsin, - "acos": np.arccos, - "atan": np.arctan, - "exp": np.exp, - "ln": np.log, - "sqrt": np.sqrt, - } - if op in dispatch: - arg = expr.sym(nested_scope) - return dispatch[op](arg) - else: - raise NodeException("internal error: undefined external") diff --git a/qiskit/qasm/node/format.py b/qiskit/qasm/node/format.py deleted file mode 100644 index 3ced9ca08a6a..000000000000 --- a/qiskit/qasm/node/format.py +++ /dev/null @@ -1,37 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM file identifier/version statement.""" - -import re - -from .node import Node - - -class Format(Node): - """Node for an OPENQASM file identifier/version statement.""" - - def __init__(self, value): - """Create the version node.""" - super().__init__("format", None, None) - parts = re.match(r"(\w+)\s+(\d+)(\.(\d+))?", value) - self.language = parts.group(1) - self.majorversion = parts.group(2) - self.minorversion = parts.group(4) if parts.group(4) is not None else "0" - - def version(self): - """Return the version.""" - return f"{self.majorversion}.{self.minorversion}" - - def qasm(self): - """Return the corresponding format string.""" - return f"{self.language} {self.version()};" diff --git a/qiskit/qasm/node/gate.py b/qiskit/qasm/node/gate.py deleted file mode 100644 index 122bd4f935df..000000000000 --- a/qiskit/qasm/node/gate.py +++ /dev/null @@ -1,62 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM gate definition.""" -from .node import Node - - -class Gate(Node): - """Node for an OPENQASM gate definition. - - children[0] is an id node. - If len(children) is 3, children[1] is an idlist node, - and children[2] is a gatebody node. - Otherwise, children[1] is an expressionlist node, - children[2] is an idlist node, and children[3] is a gatebody node. - """ - - def __init__(self, children): - """Create the gate node.""" - super().__init__("gate", children, None) - self.id = children[0] # pylint: disable=invalid-name - # The next three fields are required by the symbtab - self.name = self.id.name - self.line = self.id.line - self.file = self.id.file - - if len(children) == 3: - self.arguments = None - self.bitlist = children[1] - self.body = children[2] - else: - self.arguments = children[1] - self.bitlist = children[2] - self.body = children[3] - - def n_args(self): - """Return the number of parameter expressions.""" - if self.arguments: - return self.arguments.size() - return 0 - - def n_bits(self): - """Return the number of qubit arguments.""" - return self.bitlist.size() - - def qasm(self): - """Return the corresponding OPENQASM string.""" - string = "gate " + self.name - if self.arguments is not None: - string += "(" + self.arguments.qasm() + ")" - string += " " + self.bitlist.qasm() + "\n" - string += "{\n" + self.body.qasm() + "}" - return string diff --git a/qiskit/qasm/node/gatebody.py b/qiskit/qasm/node/gatebody.py deleted file mode 100644 index a7c591b549b8..000000000000 --- a/qiskit/qasm/node/gatebody.py +++ /dev/null @@ -1,41 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM custom gate body.""" -from .node import Node - - -class GateBody(Node): - """Node for an OPENQASM custom gate body. - - children is a list of gate operation nodes. - These are one of barrier, custom_unitary, U, or CX. - """ - - def __init__(self, children): - """Create the gatebody node.""" - super().__init__("gate_body", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - string = "" - for children in self.children: - string += " " + children.qasm() + "\n" - return string - - def calls(self): - """Return a list of custom gate names in this gate body.""" - lst = [] - for children in self.children: - if children.type == "custom_unitary": - lst.append(children.name) - return lst diff --git a/qiskit/qasm/node/id.py b/qiskit/qasm/node/id.py deleted file mode 100644 index 041ea452a4bf..000000000000 --- a/qiskit/qasm/node/id.py +++ /dev/null @@ -1,78 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM id.""" - -from .node import Node -from .nodeexception import NodeException - - -class Id(Node): - """Node for an OPENQASM id. - - The node has no children but has fields name, line, and file. - There is a flag is_bit that is set when XXXXX to help with scoping. - """ - - def __init__(self, id, line, file): - """Create the id node.""" - # pylint: disable=redefined-builtin - super().__init__("id", None, None) - self.name = id - self.line = line - self.file = file - # To help with scoping rules, so we know the id is a bit, - # this flag is set to True when the id appears in a gate declaration - self.is_bit = False - - def to_string(self, indent): - """Print the node with indent.""" - ind = indent * " " - print(ind, "id", self.name) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return self.name - - def latex(self, nested_scope=None): - """Return the correspond math mode latex string.""" - if not nested_scope: - return "\textrm{" + self.name + "}" - else: - if self.name not in nested_scope[-1]: - raise NodeException( - "Expected local parameter name: ", - "name=%s, " % self.name, - "line=%s, " % self.line, - "file=%s" % self.file, - ) - - return nested_scope[-1][self.name].latex(nested_scope[0:-1]) - - def sym(self, nested_scope=None): - """Return the correspond symbolic number.""" - if not nested_scope or self.name not in nested_scope[-1]: - raise NodeException( - "Expected local parameter name: ", - f"name={self.name}, line={self.line}, file={self.file}", - ) - return nested_scope[-1][self.name].sym(nested_scope[0:-1]) - - def real(self, nested_scope=None): - """Return the correspond floating point number.""" - if not nested_scope or self.name not in nested_scope[-1]: - raise NodeException( - "Expected local parameter name: ", - f"name={self.name}, line={self.line}, file={self.file}", - ) - - return nested_scope[-1][self.name].real(nested_scope[0:-1]) diff --git a/qiskit/qasm/node/idlist.py b/qiskit/qasm/node/idlist.py deleted file mode 100644 index 889fd887f8df..000000000000 --- a/qiskit/qasm/node/idlist.py +++ /dev/null @@ -1,33 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM idlist.""" -from .node import Node - - -class IdList(Node): - """Node for an OPENQASM idlist. - - children is a list of id nodes. - """ - - def __init__(self, children): - """Create the idlist node.""" - super().__init__("id_list", children, None) - - def size(self): - """Return the length of the list.""" - return len(self.children) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return ",".join([self.children[j].qasm() for j in range(self.size())]) diff --git a/qiskit/qasm/node/if_.py b/qiskit/qasm/node/if_.py deleted file mode 100644 index c056b078fe01..000000000000 --- a/qiskit/qasm/node/if_.py +++ /dev/null @@ -1,39 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM if statement.""" -from .node import Node - - -class If(Node): - """Node for an OPENQASM if statement. - - children[0] is an id node. - children[1] is an integer node. - children[2] is quantum operation node, including U, CX, custom_unitary, - measure, reset, (and BUG: barrier, if). - """ - - def __init__(self, children): - """Create the if node.""" - super().__init__("if", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return ( - "if(" - + self.children[0].qasm() - + "==" - + str(self.children[1].value) - + ") " - + self.children[2].qasm() - ) diff --git a/qiskit/qasm/node/indexedid.py b/qiskit/qasm/node/indexedid.py deleted file mode 100644 index ea6aa93444d6..000000000000 --- a/qiskit/qasm/node/indexedid.py +++ /dev/null @@ -1,41 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM indexed id.""" - -from .node import Node - - -class IndexedId(Node): - """Node for an OPENQASM indexed id. - - children[0] is an id node. - children[1] is an Int node. - """ - - def __init__(self, children): - """Create the indexed id node.""" - super().__init__("indexed_id", children, None) - self.id = children[0] # pylint: disable=invalid-name - self.name = self.id.name - self.line = self.id.line - self.file = self.id.file - self.index = children[1].value - - def to_string(self, indent): - """Print with indent.""" - ind = indent * " " - print(ind, "indexed_id", self.name, self.index) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return self.name + "[%d]" % self.index diff --git a/qiskit/qasm/node/intnode.py b/qiskit/qasm/node/intnode.py deleted file mode 100644 index 2b61e670c06b..000000000000 --- a/qiskit/qasm/node/intnode.py +++ /dev/null @@ -1,51 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM integer.""" - -from .node import Node - - -class Int(Node): - """Node for an OPENQASM integer. - - This node has no children. The data is in the value field. - """ - - def __init__(self, id): - """Create the integer node.""" - # pylint: disable=redefined-builtin - super().__init__("int", None, None) - self.value = id - - def to_string(self, indent): - """Print with indent.""" - ind = indent * " " - print(ind, "int", self.value) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return "%d" % self.value - - def latex(self): - """Return the corresponding math mode latex string.""" - return "%d" % self.value - - def sym(self, nested_scope=None): - """Return the correspond symbolic number.""" - del nested_scope - return float(self.value) - - def real(self, nested_scope=None): - """Return the correspond floating point number.""" - del nested_scope # ignored - return float(self.value) diff --git a/qiskit/qasm/node/measure.py b/qiskit/qasm/node/measure.py deleted file mode 100644 index c2045fba54c8..000000000000 --- a/qiskit/qasm/node/measure.py +++ /dev/null @@ -1,30 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM measure statement.""" -from .node import Node - - -class Measure(Node): - """Node for an OPENQASM measure statement. - - children[0] is a primary node (id or indexedid) - children[1] is a primary node (id or indexedid) - """ - - def __init__(self, children): - """Create the measure node.""" - super().__init__("measure", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return "measure " + self.children[0].qasm() + " -> " + self.children[1].qasm() + ";" diff --git a/qiskit/qasm/node/node.py b/qiskit/qasm/node/node.py deleted file mode 100644 index 6f64dfb1343f..000000000000 --- a/qiskit/qasm/node/node.py +++ /dev/null @@ -1,59 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Base node object for the OPENQASM syntax tree.""" - - -class Node: - """Base node object for the OPENQASM syntax tree.""" - - def __init__(self, type, children=None, root=None): - """Construct a new node object.""" - # pylint: disable=redefined-builtin - self.type = type - if children: - self.children = children - else: - self.children = [] - self.root = root - # True if this node is an expression node, False otherwise - self.expression = False - - def is_expression(self): - """Return True if this is an expression node.""" - return self.expression - - def add_child(self, node): - """Add a child node.""" - self.children.append(node) - - def to_string(self, indent): - """Print with indent.""" - ind = indent * " " - if self.root: - print(ind, self.type, "---", self.root) - else: - print(ind, self.type) - indent = indent + 3 - ind = indent * " " - for children in self.children: - if children is None: - print("OOPS! type of parent is", type(self)) - print(self.children) - if isinstance(children, str): - print(ind, children) - elif isinstance(children, int): - print(ind, str(children)) - elif isinstance(children, float): - print(ind, str(children)) - else: - children.to_string(indent) diff --git a/qiskit/qasm/node/nodeexception.py b/qiskit/qasm/node/nodeexception.py deleted file mode 100644 index d351f499c929..000000000000 --- a/qiskit/qasm/node/nodeexception.py +++ /dev/null @@ -1,26 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Exception for errors raised while interpreting nodes.""" - - -class NodeException(Exception): - """Base class for errors raised while interpreting nodes.""" - - def __init__(self, *msg): - """Set the error message.""" - super().__init__(*msg) - self.msg = " ".join(msg) - - def __str__(self): - """Return the message.""" - return repr(self.msg) diff --git a/qiskit/qasm/node/opaque.py b/qiskit/qasm/node/opaque.py deleted file mode 100644 index 866ee711840d..000000000000 --- a/qiskit/qasm/node/opaque.py +++ /dev/null @@ -1,58 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM opaque gate declaration.""" - -from .node import Node - - -class Opaque(Node): - """Node for an OPENQASM opaque gate declaration. - - children[0] is an id node. - If len(children) is 3, children[1] is an expressionlist node, - and children[2] is an idlist node. - Otherwise, children[1] is an idlist node. - """ - - def __init__(self, children): - """Create the opaque gate node.""" - super().__init__("opaque", children, None) - self.id = children[0] # pylint: disable=invalid-name - # The next three fields are required by the symbtab - self.name = self.id.name - self.line = self.id.line - self.file = self.id.file - if len(children) == 3: - self.arguments = children[1] - self.bitlist = children[2] - else: - self.arguments = None - self.bitlist = children[1] - - def n_args(self): - """Return the number of parameter expressions.""" - if self.arguments: - return self.arguments.size() - return 0 - - def n_bits(self): - """Return the number of qubit arguments.""" - return self.bitlist.size() - - def qasm(self): - """Return the corresponding OPENQASM string.""" - string = "opaque %s" % self.name - if self.arguments is not None: - string += "(" + self.arguments.qasm() + ")" - string += " " + self.bitlist.qasm() + ";" - return string diff --git a/qiskit/qasm/node/prefix.py b/qiskit/qasm/node/prefix.py deleted file mode 100644 index c144e2b5e280..000000000000 --- a/qiskit/qasm/node/prefix.py +++ /dev/null @@ -1,54 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM prefix expression.""" - -from qiskit.exceptions import MissingOptionalLibraryError -from .node import Node - - -class Prefix(Node): - """Node for an OPENQASM prefix expression. - - children[0] is a unary operator node. - children[1] is an expression node. - """ - - def __init__(self, children): - """Create the prefix node.""" - super().__init__("prefix", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return self.children[0].value + "(" + self.children[1].qasm() + ")" - - def latex(self): - """Return the corresponding math mode latex string.""" - try: - from pylatexenc.latexencode import utf8tolatex - except ImportError as ex: - raise MissingOptionalLibraryError( - "pylatexenc", "latex-from-qasm exporter", "pip install pylatexenc" - ) from ex - return utf8tolatex(self.sym()) - - def real(self): - """Return the correspond floating point number.""" - operation = self.children[0].operation() - expr = self.children[1].real() - return operation(expr) - - def sym(self, nested_scope=None): - """Return the correspond symbolic number.""" - operation = self.children[0].operation() - expr = self.children[1].sym(nested_scope) - return operation(expr) diff --git a/qiskit/qasm/node/primarylist.py b/qiskit/qasm/node/primarylist.py deleted file mode 100644 index 20e20f7b30d3..000000000000 --- a/qiskit/qasm/node/primarylist.py +++ /dev/null @@ -1,33 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM primarylist.""" -from .node import Node - - -class PrimaryList(Node): - """Node for an OPENQASM primarylist. - - children is a list of primary nodes. Primary nodes are indexedid or id. - """ - - def __init__(self, children): - """Create the primarylist node.""" - super().__init__("primary_list", children, None) - - def size(self): - """Return the size of the list.""" - return len(self.children) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return ",".join([self.children[j].qasm() for j in range(self.size())]) diff --git a/qiskit/qasm/node/program.py b/qiskit/qasm/node/program.py deleted file mode 100644 index 4475cb12f3d2..000000000000 --- a/qiskit/qasm/node/program.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM program.""" -from .node import Node - - -class Program(Node): - """Node for an OPENQASM program. - - children is a list of nodes (statements). - """ - - def __init__(self, children): - """Create the program node.""" - super().__init__("program", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - string = "" - for children in self.children: - string += children.qasm() + "\n" - return string diff --git a/qiskit/qasm/node/qreg.py b/qiskit/qasm/node/qreg.py deleted file mode 100644 index fb384bd342e3..000000000000 --- a/qiskit/qasm/node/qreg.py +++ /dev/null @@ -1,45 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM qreg statement.""" -from .node import Node - - -class Qreg(Node): - """Node for an OPENQASM qreg statement. - - children[0] is an indexedid node. - """ - - def __init__(self, children): - """Create the qreg node.""" - super().__init__("qreg", children, None) - # This is the indexed id, the full "id[n]" object - self.id = children[0] # pylint: disable=invalid-name - # Name of the qreg - self.name = self.id.name - # Source line number - self.line = self.id.line - # Source file name - self.file = self.id.file - # Size of the register - self.index = self.id.index - - def to_string(self, indent): - """Print the node data, with indent.""" - ind = indent * " " - print(ind, "qreg") - self.children[0].to_string(indent + 3) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return "qreg " + self.id.qasm() + ";" diff --git a/qiskit/qasm/node/real.py b/qiskit/qasm/node/real.py deleted file mode 100644 index f967b7582502..000000000000 --- a/qiskit/qasm/node/real.py +++ /dev/null @@ -1,63 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM real number.""" - -import numpy as np - -from qiskit.exceptions import MissingOptionalLibraryError -from .node import Node - - -class Real(Node): - """Node for an OPENQASM real number. - - This node has no children. The data is in the value field. - """ - - def __init__(self, id): - """Create the real node.""" - # pylint: disable=redefined-builtin - super().__init__("real", None, None) - self.value = id - - def to_string(self, indent): - """Print with indent.""" - ind = indent * " " - print(ind, "real", self.value) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - if self.value == np.pi: - return "pi" - - return str(np.round(float(self.value))) - - def latex(self): - """Return the corresponding math mode latex string.""" - try: - from pylatexenc.latexencode import utf8tolatex - except ImportError as ex: - raise MissingOptionalLibraryError( - "pylatexenc", "latex-from-qasm exporter", "pip install pylatexenc" - ) from ex - return utf8tolatex(self.value) - - def sym(self, nested_scope=None): - """Return the correspond symbolic number.""" - del nested_scope # unused - return float(self.value) - - def real(self, nested_scope=None): - """Return the correspond floating point number.""" - del nested_scope # unused - return float(self.value.evalf()) diff --git a/qiskit/qasm/node/reset.py b/qiskit/qasm/node/reset.py deleted file mode 100644 index 29ccae931d96..000000000000 --- a/qiskit/qasm/node/reset.py +++ /dev/null @@ -1,29 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM reset statement.""" -from .node import Node - - -class Reset(Node): - """Node for an OPENQASM reset statement. - - children[0] is a primary node (id or indexedid) - """ - - def __init__(self, children): - """Create the reset node.""" - super().__init__("reset", children, None) - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return "reset " + self.children[0].qasm() + ";" diff --git a/qiskit/qasm/node/unaryoperator.py b/qiskit/qasm/node/unaryoperator.py deleted file mode 100644 index a81e9f0737b1..000000000000 --- a/qiskit/qasm/node/unaryoperator.py +++ /dev/null @@ -1,49 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OpenQASM 2 unary operator.""" - -import operator - -from .node import Node -from .nodeexception import NodeException - - -VALID_OPERATORS = { - "+": operator.pos, - "-": operator.neg, -} - - -class UnaryOperator(Node): - """Node for an OpenQASM 2 unary operator. - - This node has no children. The data is in the value field. - """ - - def __init__(self, operation): - """Create the operator node.""" - super().__init__("unary_operator", None, None) - self.value = operation - - def operation(self): - """ - Return the operator as a function f(left, right). - """ - try: - return VALID_OPERATORS[self.value] - except KeyError as ex: - raise NodeException(f"internal error: undefined prefix '{self.value}'") from ex - - def qasm(self): - """Return OpenQASM 2 representation.""" - return self.value diff --git a/qiskit/qasm/node/universalunitary.py b/qiskit/qasm/node/universalunitary.py deleted file mode 100644 index e00303821273..000000000000 --- a/qiskit/qasm/node/universalunitary.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Node for an OPENQASM U statement.""" -from .node import Node - - -class UniversalUnitary(Node): - """Node for an OPENQASM U statement. - - children[0] is an expressionlist node. - children[1] is a primary node (id or indexedid). - """ - - def __init__(self, children): - """Create the U node.""" - super().__init__("universal_unitary", children) - self.arguments = children[0] - self.bitlist = children[1] - - def qasm(self): - """Return the corresponding OPENQASM string.""" - return "U(" + self.children[0].qasm() + ") " + self.children[1].qasm() + ";" diff --git a/qiskit/qasm/pygments/__init__.py b/qiskit/qasm/pygments/__init__.py deleted file mode 100644 index 686bf5e7800d..000000000000 --- a/qiskit/qasm/pygments/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -================================================= -Qasm Pygments tools (:mod:`qiskit.qasm.pygments`) -================================================= - -.. currentmodule:: qiskit.qasm.pygments - -.. autosummary:: - :toctree: ../stubs/ - - OpenQASMLexer - QasmTerminalStyle - QasmHTMLStyle -""" - -# pylint: disable=wrong-import-position - -from qiskit.utils.optionals import HAS_PYGMENTS - -HAS_PYGMENTS.require_now("built-in OpenQASM 2 syntax highlighting") - -from .lexer import OpenQASMLexer, QasmTerminalStyle, QasmHTMLStyle diff --git a/qiskit/qasm/pygments/lexer.py b/qiskit/qasm/pygments/lexer.py deleted file mode 100644 index cba2163bb0d7..000000000000 --- a/qiskit/qasm/pygments/lexer.py +++ /dev/null @@ -1,133 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Pygments tools for Qasm. -""" - -from pygments.lexer import RegexLexer -from pygments.token import Comment, String, Keyword, Name, Number, Text -from pygments.style import Style - - -class QasmTerminalStyle(Style): - """A style for OpenQasm in a Terminal env (e.g. Jupyter print).""" - - styles = { - String: "ansibrightred", - Number: "ansibrightcyan", - Keyword.Reserved: "ansibrightgreen", - Keyword.Declaration: "ansibrightgreen", - Keyword.Type: "ansibrightmagenta", - Name.Builtin: "ansibrightblue", - Name.Function: "ansibrightyellow", - } - - -class QasmHTMLStyle(Style): - """A style for OpenQasm in a HTML env (e.g. Jupyter widget).""" - - styles = { - String: "ansired", - Number: "ansicyan", - Keyword.Reserved: "ansigreen", - Keyword.Declaration: "ansigreen", - Keyword.Type: "ansimagenta", - Name.Builtin: "ansiblue", - Name.Function: "ansiyellow", - } - - -class OpenQASMLexer(RegexLexer): - """A pygments lexer for OpenQasm.""" - - name = "OpenQASM" - aliases = ["qasm"] - filenames = ["*.qasm"] - - gates = [ - "id", - "cx", - "x", - "y", - "z", - "s", - "sdg", - "h", - "t", - "tdg", - "ccx", - "c3x", - "c4x", - "c3sqrtx", - "rx", - "ry", - "rz", - "cz", - "cy", - "ch", - "swap", - "cswap", - "crx", - "cry", - "crz", - "cu1", - "cu3", - "rxx", - "rzz", - "rccx", - "rc3x", - "u1", - "u2", - "u3", - ] - - tokens = { - "root": [ - (r"\n", Text), - (r"[^\S\n]+", Text), - (r"//\n", Comment), - (r"//.*?$", Comment.Single), - # Keywords - (r"(OPENQASM|include)\b", Keyword.Reserved, "keywords"), - (r"(qreg|creg)\b", Keyword.Declaration), - # Treat 'if' special - (r"(if)\b", Keyword.Reserved, "if_keywords"), - # Constants - (r"(pi)\b", Name.Constant), - # Special - (r"(barrier|measure|reset)\b", Name.Builtin, "params"), - # Gates (Types) - ("(" + "|".join(gates) + r")\b", Keyword.Type, "params"), - (r"[unitary\d+]", Keyword.Type), - # Functions - (r"(gate)\b", Name.Function, "gate"), - # Generic text - (r"[a-zA-Z_][a-zA-Z0-9_]*", Text, "index"), - ], - "keywords": [ - (r'\s*("([^"]|"")*")', String, "#push"), - (r"\d+", Number, "#push"), - (r".*\(", Text, "params"), - ], - "if_keywords": [ - (r"[a-zA-Z0-9_]*", String, "#pop"), - (r"\d+", Number, "#push"), - (r".*\(", Text, "params"), - ], - "params": [ - (r"[a-zA-Z_][a-zA-Z0-9_]*", Text, "#push"), - (r"\d+", Number, "#push"), - (r"(\d+\.\d*|\d*\.\d+)([eEf][+-]?[0-9]+)?", Number, "#push"), - (r"\)", Text), - ], - "gate": [(r"[unitary\d+]", Keyword.Type, "#push"), (r"p\d+", Text, "#push")], - "index": [(r"\d+", Number, "#pop")], - } diff --git a/qiskit/qasm/qasm.py b/qiskit/qasm/qasm.py deleted file mode 100644 index ded52b32d3fc..000000000000 --- a/qiskit/qasm/qasm.py +++ /dev/null @@ -1,53 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -OPENQASM circuit object. -""" -from .exceptions import QasmError -from .qasmparser import QasmParser - - -class Qasm: - """OPENQASM circuit object.""" - - def __init__(self, filename=None, data=None): - """Create an OPENQASM circuit object.""" - if filename is None and data is None: - raise QasmError("Missing input file and/or data") - if filename is not None and data is not None: - raise QasmError("File and data must not both be specified initializing OpenQASM 2") - self._filename = filename - self._data = data - - def return_filename(self): - """Return the filename.""" - return self._filename - - def generate_tokens(self): - """Returns a generator of the tokens.""" - if self._filename: - with open(self._filename) as ifile: - self._data = ifile.read() - - with QasmParser(self._filename) as qasm_p: - return qasm_p.read_tokens() - - def parse(self): - """Parse the data.""" - if self._filename: - with open(self._filename) as ifile: - self._data = ifile.read() - - with QasmParser(self._filename) as qasm_p: - qasm_p.parse_debug(False) - return qasm_p.parse(self._data) diff --git a/qiskit/qasm/qasmlexer.py b/qiskit/qasm/qasmlexer.py deleted file mode 100644 index 7766d81c2eec..000000000000 --- a/qiskit/qasm/qasmlexer.py +++ /dev/null @@ -1,203 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -OPENQASM Lexer. - -This is a wrapper around the PLY lexer to support the "include" statement -by creating a stack of lexers. -""" - -import os - -import numpy as np -from ply import lex - -from . import node -from .exceptions import QasmError - -CORE_LIBS_PATH = os.path.join(os.path.dirname(__file__), "libs") -CORE_LIBS = os.listdir(CORE_LIBS_PATH) - - -class QasmLexer: - """OPENQASM Lexer. - - This is a wrapper around the PLY lexer to support the "include" statement - by creating a stack of lexers. - """ - - # pylint: disable=invalid-name,missing-function-docstring - # pylint: disable=attribute-defined-outside-init,bad-docstring-quotes - - def __mklexer__(self, filename): - """Create a PLY lexer.""" - self.lexer = lex.lex(module=self, debug=False) - self.filename = filename - self.lineno = 1 - - if filename: - with open(filename) as ifile: - self.data = ifile.read() - self.lexer.input(self.data) - - def __init__(self, filename): - """Create the OPENQASM lexer.""" - self.__mklexer__(filename) - self.stack = [] - - def input(self, data): - """Set the input text data.""" - self.data = data - self.lexer.input(data) - - def token(self): - """Return the next token.""" - ret = self.lexer.token() - return ret - - def pop(self): - """Pop a PLY lexer off the stack.""" - self.lexer = self.stack.pop() - self.filename = self.lexer.qasm_file - self.lineno = self.lexer.qasm_line - - def push(self, filename): - """Push a PLY lexer on the stack to parse filename.""" - self.lexer.qasm_file = self.filename - self.lexer.qasm_line = self.lineno - self.stack.append(self.lexer) - self.__mklexer__(filename) - - # ---- Beginning of the PLY lexer ---- - literals = r'=()[]{};<>,.+-/*^"' - reserved = { - "barrier": "BARRIER", - "creg": "CREG", - "gate": "GATE", - "if": "IF", - "measure": "MEASURE", - "opaque": "OPAQUE", - "qreg": "QREG", - "pi": "PI", - "reset": "RESET", - } - tokens = [ - "NNINTEGER", - "REAL", - "CX", - "U", - "FORMAT", - "ASSIGN", - "MATCHES", - "ID", - "STRING", - ] + list(reserved.values()) - - def t_REAL(self, t): - r"(([0-9]+|([0-9]+)?\.[0-9]+|[0-9]+\.)[eE][+-]?[0-9]+)|(([0-9]+)?\.[0-9]+|[0-9]+\.)" - if np.iscomplex(t): - return t.real - else: - return t - - def t_NNINTEGER(self, t): - r"[1-9]+[0-9]*|0" - t.value = int(t.value) - return t - - def t_ASSIGN(self, t): - "->" - return t - - def t_MATCHES(self, t): - "==" - return t - - def t_STRING(self, t): - r"\"([^\\\"]|\\.)*\"" # fmt: skip - return t - - def t_INCLUDE(self, _): - "include" - # Now eat up the next two tokens which must be - # 1 - the name of the include file, and - # 2 - a terminating semicolon - # - # Then push the current lexer onto the stack, create a new one from - # the include file, and push it onto the stack. - # - # When we hit eof (the t_eof) rule, we pop. - next_token = self.lexer.token() - lineno = next_token.lineno - if isinstance(next_token.value, str): - incfile = next_token.value.strip('"') - else: - raise QasmError("Invalid include: must be a quoted string.") - - if incfile in CORE_LIBS: - incfile = os.path.join(CORE_LIBS_PATH, incfile) - - next_token = self.lexer.token() - if next_token is None or next_token.value != ";": - raise QasmError('Invalid syntax, missing ";" at line', str(lineno)) - - if not os.path.exists(incfile): - raise QasmError( - "Include file %s cannot be found, line %s, file %s" - % (incfile, str(next_token.lineno), self.filename) - ) - self.push(incfile) - return self.lexer.token() - - def t_FORMAT(self, t): - r"OPENQASM\s+[0-9]+(\.[0-9]+)?" - return t - - def t_COMMENT(self, _): - r"//.*" - pass - - def t_CX(self, t): - "CX" - return t - - def t_U(self, t): - "U" - return t - - def t_ID(self, t): - r"[a-z][a-zA-Z0-9_]*" - - t.type = self.reserved.get(t.value, "ID") - if t.type == "ID": - t.value = node.Id(t.value, self.lineno, self.filename) - return t - - def t_newline(self, t): - r"\n+" - self.lineno += len(t.value) - t.lexer.lineno = self.lineno - - def t_eof(self, _): - if self.stack: - self.pop() - return self.lexer.token() - return None - - t_ignore = " \t\r" - - def t_error(self, t): - raise QasmError( - "Unable to match any token rule, got -->%s<-- " - "Check your OPENQASM source and any include statements." % t.value[0] - ) diff --git a/qiskit/qasm/qasmparser.py b/qiskit/qasm/qasmparser.py deleted file mode 100644 index f5c2dc2c1fa0..000000000000 --- a/qiskit/qasm/qasmparser.py +++ /dev/null @@ -1,1156 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""OpenQASM parser.""" - -import os -import shutil -import tempfile - -import numpy as np -from ply import yacc - -from . import node -from .exceptions import QasmError -from .qasmlexer import QasmLexer - - -class QasmParser: - """OPENQASM Parser.""" - - # pylint: disable=missing-function-docstring,invalid-name - - def __init__(self, filename): - """Create the parser.""" - if filename is None: - filename = "" - self.lexer = QasmLexer(filename) - self.tokens = self.lexer.tokens - self.parse_dir = tempfile.mkdtemp(prefix="qiskit") - self.precedence = ( - ("left", "+", "-"), - ("left", "*", "/"), - ("left", "negative", "positive"), - ("right", "^"), - ) - # For yacc, also, write_tables = Bool and optimize = Bool - self.parser = yacc.yacc(module=self, debug=False, outputdir=self.parse_dir) - self.qasm = None - self.parse_deb = False - self.global_symtab = {} # global symtab - self.current_symtab = self.global_symtab # top of symbol stack - self.symbols = [] # symbol stack - self.external_functions = ["sin", "cos", "tan", "exp", "ln", "sqrt", "acos", "atan", "asin"] - - def __enter__(self): - return self - - def __exit__(self, *args): - if os.path.exists(self.parse_dir): - shutil.rmtree(self.parse_dir) - - def update_symtab(self, obj): - """Update a node in the symbol table. - - Everything in the symtab must be a node with these attributes: - name - the string name of the object - type - the string type of the object - line - the source line where the type was first found - file - the source file where the type was first found - """ - if obj.name in self.current_symtab: - prev = self.current_symtab[obj.name] - raise QasmError( - "Duplicate declaration for", - obj.type + " '" + obj.name + "' at line", - str(obj.line) + ", file", - obj.file + ".\nPrevious occurrence at line", - str(prev.line) + ", file", - prev.file, - ) - self.current_symtab[obj.name] = obj - - def verify_declared_bit(self, obj): - """Verify a qubit id against the gate prototype.""" - # We are verifying gate args against the formal parameters of a - # gate prototype. - if obj.name not in self.current_symtab: - raise QasmError( - "Cannot find symbol '" + obj.name + "' in argument list for gate, line", - str(obj.line), - "file", - obj.file, - ) - - # This insures the thing is from the bitlist and not from the - # argument list. - sym = self.current_symtab[obj.name] - if not (sym.type == "id" and sym.is_bit): - raise QasmError("Bit", obj.name, "is not declared as a bit in the gate.") - - def verify_bit_list(self, obj): - """Verify each qubit in a list of ids.""" - # We expect the object to be a bitlist or an idlist, we don't care. - # We will iterate it and ensure everything in it is declared as a bit, - # and throw if not. - for children in obj.children: - self.verify_declared_bit(children) - - def verify_exp_list(self, obj): - """Verify each expression in a list.""" - # A tad harder. This is a list of expressions each of which could be - # the head of a tree. We need to recursively walk each of these and - # ensure that any Id elements resolve to the current stack. - # - # I believe we only have to look at the current symtab. - if obj.children is not None: - for children in obj.children: - if isinstance(children, node.Id): - if children.name in self.external_functions: - continue - - if children.name not in self.current_symtab: - raise QasmError( - "Argument '" - + children.name - + "' in expression cannot be " - + "found, line", - str(children.line), - "file", - children.file, - ) - else: - if hasattr(children, "children"): - self.verify_exp_list(children) - - def verify_as_gate(self, obj, bitlist, arglist=None): - """Verify a user defined gate call.""" - if obj.name not in self.global_symtab: - raise QasmError( - "Cannot find gate definition for '" + obj.name + "', line", - str(obj.line), - "file", - obj.file, - ) - g_sym = self.global_symtab[obj.name] - if g_sym.type not in ("gate", "opaque"): - raise QasmError( - "'" - + obj.name - + "' is used as a gate " - + "or opaque call but the symbol is neither;" - + " it is a '" - + g_sym.type - + "' line", - str(obj.line), - "file", - obj.file, - ) - - if g_sym.n_bits() != bitlist.size(): - raise QasmError( - "Gate or opaque call to '" + obj.name + "' uses", - str(bitlist.size()), - "qubits but is declared for", - str(g_sym.n_bits()), - "qubits", - "line", - str(obj.line), - "file", - obj.file, - ) - - if arglist: - if g_sym.n_args() != arglist.size(): - raise QasmError( - "Gate or opaque call to '" + obj.name + "' uses", - str(arglist.size()), - "qubits but is declared for", - str(g_sym.n_args()), - "qubits", - "line", - str(obj.line), - "file", - obj.file, - ) - else: - if g_sym.n_args() > 0: - raise QasmError( - "Gate or opaque call to '" - + obj.name - + "' has no arguments but is declared for", - str(g_sym.n_args()), - "qubits", - "line", - str(obj.line), - "file", - obj.file, - ) - - def verify_reg(self, obj, object_type): - """Verify a register.""" - # How to verify: - # types must match - # indexes must be checked - if obj.name not in self.global_symtab: - raise QasmError( - "Cannot find definition for", - object_type, - "'" + obj.name + "'", - "at line", - str(obj.line), - "file", - obj.file, - ) - - g_sym = self.global_symtab[obj.name] - - if g_sym.type != object_type: - raise QasmError( - "Type for '" - + g_sym.name - + "' should be '" - + object_type - + "' but was found to be '" - + g_sym.type - + "'", - "line", - str(obj.line), - "file", - obj.file, - ) - - if obj.type == "indexed_id": - bound = g_sym.index - ndx = obj.index - if ndx < 0 or ndx >= bound: - raise QasmError( - "Register index for '" + g_sym.name + "' out of bounds. Index is", - str(ndx), - "bound is 0 <= index <", - str(bound), - "at line", - str(obj.line), - "file", - obj.file, - ) - - def verify_reg_list(self, obj, object_type): - """Verify a list of registers.""" - # We expect the object to be a bitlist or an idlist, we don't care. - # We will iterate it and ensure everything in it is declared as a bit, - # and throw if not. - for children in obj.children: - self.verify_reg(children, object_type) - - def id_tuple_list(self, id_node): - """Return a list of (name, index) tuples for this id node.""" - if id_node.type != "id": - raise QasmError("internal error, id_tuple_list") - bit_list = [] - try: - g_sym = self.current_symtab[id_node.name] - except KeyError: - g_sym = self.global_symtab[id_node.name] - if g_sym.type in ("qreg", "creg"): - # Return list of (name, idx) for reg ids - for idx in range(g_sym.index): - bit_list.append((id_node.name, idx)) - else: - # Return (name, -1) for other ids - bit_list.append((id_node.name, -1)) - return bit_list - - def verify_distinct(self, list_of_nodes): - """Check that objects in list_of_nodes represent distinct (qu)bits. - - list_of_nodes is a list containing nodes of type id, indexed_id, - primary_list, or id_list. We assume these are all the same type - 'qreg' or 'creg'. - This method raises an exception if list_of_nodes refers to the - same object more than once. - """ - bit_list = [] - line_number = -1 - filename = "" - for node_ in list_of_nodes: - # id node: add all bits in register or (name, -1) for id - if node_.type == "id": - bit_list.extend(self.id_tuple_list(node_)) - line_number = node_.line - filename = node_.file - # indexed_id: add the bit - elif node_.type == "indexed_id": - bit_list.append((node_.name, node_.index)) - line_number = node_.line - filename = node_.file - # primary_list: for each id or indexed_id child, add - elif node_.type == "primary_list": - for child in node_.children: - if child.type == "id": - bit_list.extend(self.id_tuple_list(child)) - else: - bit_list.append((child.name, child.index)) - line_number = child.line - filename = child.file - # id_list: for each id, add - elif node_.type == "id_list": - for child in node_.children: - bit_list.extend(self.id_tuple_list(child)) - line_number = child.line - filename = child.file - else: - raise QasmError("internal error, verify_distinct") - if len(bit_list) != len(set(bit_list)): - raise QasmError("duplicate identifiers at line %d file %s" % (line_number, filename)) - - def pop_scope(self): - """Return to the previous scope.""" - self.current_symtab = self.symbols.pop() - - def push_scope(self): - """Enter a new scope.""" - self.symbols.append(self.current_symtab) - self.current_symtab = {} - - # ---- Begin the PLY parser ---- - start = "main" - - def p_main(self, program): - """ - main : program - """ - self.qasm = program[1] - - # ---------------------------------------- - # program : statement - # | program statement - # ---------------------------------------- - def p_program_0(self, program): - """ - program : statement - """ - program[0] = node.Program([program[1]]) - - def p_program_1(self, program): - """ - program : program statement - """ - program[0] = program[1] - program[0].add_child(program[2]) - - # ---------------------------------------- - # statement : decl - # | quantum_op ';' - # | format ';' - # ---------------------------------------- - def p_statement(self, program): - """ - statement : decl - | quantum_op ';' - | format ';' - | ignore - | quantum_op error - | format error - """ - if len(program) > 2: - if program[2] != ";": - raise QasmError( - "Missing ';' at end of statement; " + "received", str(program[2].value) - ) - program[0] = program[1] - - def p_format(self, program): - """ - format : FORMAT - """ - version = node.Format(program[1]) - if (version.majorversion != "2") or (version.minorversion != "0"): - provided_version = f"{version.majorversion}.{version.minorversion}" - raise QasmError( - f"Invalid version: '{provided_version}'. This module supports OpenQASM 2.0 only." - ) - program[0] = version - - # ---------------------------------------- - # id : ID - # ---------------------------------------- - def p_id(self, program): - """ - id : ID - """ - program[0] = program[1] - - def p_id_e(self, program): - """ - id : error - """ - raise QasmError("Expected an ID, received '" + str(program[1].value) + "'") - - # ---------------------------------------- - # indexed_id : ID [ int ] - # ---------------------------------------- - def p_indexed_id(self, program): - """ - indexed_id : id '[' NNINTEGER ']' - | id '[' NNINTEGER error - | id '[' error - """ - if len(program) == 4: - raise QasmError("Expecting an integer index; received", str(program[3].value)) - if program[4] != "]": - raise QasmError("Missing ']' in indexed ID; received", str(program[4].value)) - program[0] = node.IndexedId([program[1], node.Int(program[3])]) - - # ---------------------------------------- - # primary : id - # | indexed_id - # ---------------------------------------- - def p_primary(self, program): - """ - primary : id - | indexed_id - """ - program[0] = program[1] - - # ---------------------------------------- - # id_list : id - # | id_list ',' id - # ---------------------------------------- - def p_id_list_0(self, program): - """ - id_list : id - """ - program[0] = node.IdList([program[1]]) - - def p_id_list_1(self, program): - """ - id_list : id_list ',' id - """ - program[0] = program[1] - program[0].add_child(program[3]) - - # ---------------------------------------- - # gate_id_list : id - # | gate_id_list ',' id - # ---------------------------------------- - def p_gate_id_list_0(self, program): - """ - gate_id_list : id - """ - program[0] = node.IdList([program[1]]) - self.update_symtab(program[1]) - - def p_gate_id_list_1(self, program): - """ - gate_id_list : gate_id_list ',' id - """ - program[0] = program[1] - program[0].add_child(program[3]) - self.update_symtab(program[3]) - - # ---------------------------------------- - # bit_list : bit - # | bit_list ',' bit - # ---------------------------------------- - def p_bit_list_0(self, program): - """ - bit_list : id - """ - program[0] = node.IdList([program[1]]) - program[1].is_bit = True - self.update_symtab(program[1]) - - def p_bit_list_1(self, program): - """ - bit_list : bit_list ',' id - """ - program[0] = program[1] - program[0].add_child(program[3]) - program[3].is_bit = True - self.update_symtab(program[3]) - - # ---------------------------------------- - # primary_list : primary - # | primary_list ',' primary - # ---------------------------------------- - def p_primary_list_0(self, program): - """ - primary_list : primary - """ - program[0] = node.PrimaryList([program[1]]) - - def p_primary_list_1(self, program): - """ - primary_list : primary_list ',' primary - """ - program[0] = program[1] - program[1].add_child(program[3]) - - # ---------------------------------------- - # decl : qreg_decl - # | creg_decl - # | gate_decl - # ---------------------------------------- - def p_decl(self, program): - """ - decl : qreg_decl ';' - | creg_decl ';' - | qreg_decl error - | creg_decl error - | gate_decl - """ - if len(program) > 2: - if program[2] != ";": - raise QasmError( - "Missing ';' in qreg or creg declaration." - " Instead received '" + program[2].value + "'" - ) - program[0] = program[1] - - # ---------------------------------------- - # qreg_decl : QREG indexed_id - # ---------------------------------------- - def p_qreg_decl(self, program): - """ - qreg_decl : QREG indexed_id - """ - program[0] = node.Qreg([program[2]]) - if program[2].name in self.external_functions: - raise QasmError( - "QREG names cannot be reserved words. " + "Received '" + program[2].name + "'" - ) - if program[2].index == 0: - raise QasmError("QREG size must be positive") - self.update_symtab(program[0]) - - def p_qreg_decl_e(self, program): - """ - qreg_decl : QREG error - """ - raise QasmError( - "Expecting indexed id (ID[int]) in QREG" + " declaration; received", program[2].value - ) - - # ---------------------------------------- - # creg_decl : QREG indexed_id - # ---------------------------------------- - def p_creg_decl(self, program): - """ - creg_decl : CREG indexed_id - """ - program[0] = node.Creg([program[2]]) - if program[2].name in self.external_functions: - raise QasmError( - "CREG names cannot be reserved words. " + "Received '" + program[2].name + "'" - ) - if program[2].index == 0: - raise QasmError("CREG size must be positive") - self.update_symtab(program[0]) - - def p_creg_decl_e(self, program): - """ - creg_decl : CREG error - """ - raise QasmError( - "Expecting indexed id (ID[int]) in CREG" + " declaration; received", program[2].value - ) - - # Gate_body will throw if there are errors, so we don't need to cover - # that here. Same with the id_lists - if they are not legal, we die - # before we get here - # - # ---------------------------------------- - # gate_decl : GATE id gate_scope bit_list gate_body - # | GATE id gate_scope '(' ')' bit_list gate_body - # | GATE id gate_scope '(' gate_id_list ')' bit_list gate_body - # - # ---------------------------------------- - def p_gate_decl_0(self, program): - """ - gate_decl : GATE id gate_scope bit_list gate_body - """ - program[0] = node.Gate([program[2], program[4], program[5]]) - if program[2].name in self.external_functions: - raise QasmError( - "GATE names cannot be reserved words. " + "Received '" + program[2].name + "'" - ) - self.pop_scope() - self.update_symtab(program[0]) - - def p_gate_decl_1(self, program): - """ - gate_decl : GATE id gate_scope '(' ')' bit_list gate_body - """ - program[0] = node.Gate([program[2], program[6], program[7]]) - if program[2].name in self.external_functions: - raise QasmError( - "GATE names cannot be reserved words. " + "Received '" + program[2].name + "'" - ) - self.pop_scope() - self.update_symtab(program[0]) - - def p_gate_decl_2(self, program): - """ - gate_decl : GATE id gate_scope '(' gate_id_list ')' bit_list gate_body - """ - program[0] = node.Gate([program[2], program[5], program[7], program[8]]) - if program[2].name in self.external_functions: - raise QasmError( - "GATE names cannot be reserved words. " + "Received '" + program[2].name + "'" - ) - self.pop_scope() - self.update_symtab(program[0]) - - def p_gate_scope(self, _): - """ - gate_scope : - """ - self.push_scope() - - # ---------------------------------------- - # gate_body : '{' gate_op_list '}' - # | '{' '}' - # - # | '{' gate_op_list error - # | '{' error - # - # Error handling: gete_op will throw if there's a problem so we won't - # get here with in the gate_op_list - # ---------------------------------------- - def p_gate_body_0(self, program): - """ - gate_body : '{' '}' - """ - if program[2] != "}": - raise QasmError( - "Missing '}' in gate definition; received'" + str(program[2].value) + "'" - ) - program[0] = node.GateBody(None) - - def p_gate_body_1(self, program): - """ - gate_body : '{' gate_op_list '}' - """ - program[0] = node.GateBody(program[2]) - - # ---------------------------------------- - # gate_op_list : gate_op - # | gate_op_ist gate_op - # - # Error handling: gete_op will throw if there's a problem so we won't - # get here with errors - # ---------------------------------------- - def p_gate_op_list_0(self, program): - """ - gate_op_list : gate_op - """ - program[0] = [program[1]] - - def p_gate_op_list_1(self, program): - """ - gate_op_list : gate_op_list gate_op - """ - program[0] = program[1] - program[0].append(program[2]) - - # ---------------------------------------- - # These are for use outside of gate_bodies and allow - # indexed ids everywhere. - # - # unitary_op : U '(' exp_list ')' primary - # | CX primary ',' primary - # | id primary_list - # | id '(' ')' primary_list - # | id '(' exp_list ')' primary_list - # - # Note that it might not be unitary - this is the mechanism that - # is also used to invoke calls to 'opaque' - # ---------------------------------------- - def p_unitary_op_0(self, program): - """ - unitary_op : U '(' exp_list ')' primary - """ - program[0] = node.UniversalUnitary([program[3], program[5]]) - self.verify_reg(program[5], "qreg") - self.verify_exp_list(program[3]) - - def p_unitary_op_1(self, program): - """ - unitary_op : CX primary ',' primary - """ - program[0] = node.Cnot([program[2], program[4]]) - self.verify_reg(program[2], "qreg") - self.verify_reg(program[4], "qreg") - self.verify_distinct([program[2], program[4]]) - # TODO: check that if both primary are id, same size - # TODO: this needs to be checked in other cases too - - def p_unitary_op_2(self, program): - """ - unitary_op : id primary_list - """ - program[0] = node.CustomUnitary([program[1], program[2]]) - self.verify_as_gate(program[1], program[2]) - self.verify_reg_list(program[2], "qreg") - self.verify_distinct([program[2]]) - - def p_unitary_op_3(self, program): - """ - unitary_op : id '(' ')' primary_list - """ - program[0] = node.CustomUnitary([program[1], program[4]]) - self.verify_as_gate(program[1], program[4]) - self.verify_reg_list(program[4], "qreg") - self.verify_distinct([program[4]]) - - def p_unitary_op_4(self, program): - """ - unitary_op : id '(' exp_list ')' primary_list - """ - program[0] = node.CustomUnitary([program[1], program[3], program[5]]) - self.verify_as_gate(program[1], program[5], arglist=program[3]) - self.verify_reg_list(program[5], "qreg") - self.verify_exp_list(program[3]) - self.verify_distinct([program[5]]) - - # ---------------------------------------- - # This is a restricted set of "quantum_op" which also - # prohibits indexed ids, for use in a gate_body - # - # gate_op : U '(' exp_list ')' id ';' - # | CX id ',' id ';' - # | id id_list ';' - # | id '(' ')' id_list ';' - # | id '(' exp_list ')' id_list ';' - # | BARRIER id_list ';' - # ---------------------------------------- - def p_gate_op_0(self, program): - """ - gate_op : U '(' exp_list ')' id ';' - """ - program[0] = node.UniversalUnitary([program[3], program[5]]) - self.verify_declared_bit(program[5]) - self.verify_exp_list(program[3]) - - def p_gate_op_0e1(self, p): - """ - gate_op : U '(' exp_list ')' error - """ - raise QasmError("Invalid U inside gate definition. " + "Missing bit id or ';'") - - def p_gate_op_0e2(self, _): - """ - gate_op : U '(' exp_list error - """ - raise QasmError("Missing ')' in U invocation in gate definition.") - - def p_gate_op_1(self, program): - """ - gate_op : CX id ',' id ';' - """ - program[0] = node.Cnot([program[2], program[4]]) - self.verify_declared_bit(program[2]) - self.verify_declared_bit(program[4]) - self.verify_distinct([program[2], program[4]]) - - def p_gate_op_1e1(self, program): - """ - gate_op : CX error - """ - raise QasmError( - "Invalid CX inside gate definition. " - + "Expected an ID or ',', received '" - + str(program[2].value) - + "'" - ) - - def p_gate_op_1e2(self, program): - """ - gate_op : CX id ',' error - """ - raise QasmError( - "Invalid CX inside gate definition. " - + "Expected an ID or ';', received '" - + str(program[4].value) - + "'" - ) - - def p_gate_op_2(self, program): - """ - gate_op : id id_list ';' - """ - program[0] = node.CustomUnitary([program[1], program[2]]) - # To verify: - # 1. id is declared as a gate in global scope - # 2. everything in the id_list is declared as a bit in local scope - self.verify_as_gate(program[1], program[2]) - self.verify_bit_list(program[2]) - self.verify_distinct([program[2]]) - - def p_gate_op_2e(self, _): - """ - gate_op : id id_list error - """ - raise QasmError("Invalid gate invocation inside gate definition.") - - def p_gate_op_3(self, program): - """ - gate_op : id '(' ')' id_list ';' - """ - program[0] = node.CustomUnitary([program[1], program[4]]) - self.verify_as_gate(program[1], program[4]) - self.verify_bit_list(program[4]) - self.verify_distinct([program[4]]) - - def p_gate_op_4(self, program): - """ - gate_op : id '(' exp_list ')' id_list ';' - """ - program[0] = node.CustomUnitary([program[1], program[3], program[5]]) - self.verify_as_gate(program[1], program[5], arglist=program[3]) - self.verify_bit_list(program[5]) - self.verify_exp_list(program[3]) - self.verify_distinct([program[5]]) - - def p_gate_op_4e0(self, _): - """ - gate_op : id '(' ')' error - """ - raise QasmError("Invalid bit list inside gate definition or" + " missing ';'") - - def p_gate_op_4e1(self, _): - """ - gate_op : id '(' error - """ - raise QasmError("Unmatched () for gate invocation inside gate" + " invocation.") - - def p_gate_op_5(self, program): - """ - gate_op : BARRIER id_list ';' - """ - program[0] = node.Barrier([program[2]]) - self.verify_bit_list(program[2]) - self.verify_distinct([program[2]]) - - def p_gate_op_5e(self, _): - """ - gate_op : BARRIER error - """ - raise QasmError("Invalid barrier inside gate definition.") - - # ---------------------------------------- - # opaque : OPAQUE id gate_scope bit_list - # | OPAQUE id gate_scope '(' ')' bit_list - # | OPAQUE id gate_scope '(' gate_id_list ')' bit_list - # - # These are like gate declarations only without a body. - # ---------------------------------------- - def p_opaque_0(self, program): - """ - opaque : OPAQUE id gate_scope bit_list - """ - # TODO: Review Opaque function - program[0] = node.Opaque([program[2], program[4]]) - if program[2].name in self.external_functions: - raise QasmError( - "OPAQUE names cannot be reserved words. " + "Received '" + program[2].name + "'" - ) - self.pop_scope() - self.update_symtab(program[0]) - - def p_opaque_1(self, program): - """ - opaque : OPAQUE id gate_scope '(' ')' bit_list - """ - program[0] = node.Opaque([program[2], program[6]]) - self.pop_scope() - self.update_symtab(program[0]) - - def p_opaque_2(self, program): - """ - opaque : OPAQUE id gate_scope '(' gate_id_list ')' bit_list - """ - program[0] = node.Opaque([program[2], program[5], program[7]]) - if program[2].name in self.external_functions: - raise QasmError( - "OPAQUE names cannot be reserved words. " + "Received '" + program[2].name + "'" - ) - self.pop_scope() - self.update_symtab(program[0]) - - def p_opaque_1e(self, _): - """ - opaque : OPAQUE id gate_scope '(' error - """ - raise QasmError("Poorly formed OPAQUE statement.") - - # ---------------------------------------- - # measure : MEASURE primary ASSIGN primary - # ---------------------------------------- - def p_measure(self, program): - """ - measure : MEASURE primary ASSIGN primary - """ - program[0] = node.Measure([program[2], program[4]]) - self.verify_reg(program[2], "qreg") - self.verify_reg(program[4], "creg") - - def p_measure_e(self, program): - """ - measure : MEASURE primary error - """ - raise QasmError("Illegal measure statement." + str(program[3].value)) - - # ---------------------------------------- - # barrier : BARRIER primary_list - # - # Errors are covered by handling errors in primary_list - # ---------------------------------------- - def p_barrier(self, program): - """ - barrier : BARRIER primary_list - """ - program[0] = node.Barrier([program[2]]) - self.verify_reg_list(program[2], "qreg") - self.verify_distinct([program[2]]) - - # ---------------------------------------- - # reset : RESET primary - # ---------------------------------------- - def p_reset(self, program): - """ - reset : RESET primary - """ - program[0] = node.Reset([program[2]]) - self.verify_reg(program[2], "qreg") - - # ---------------------------------------- - # IF '(' ID MATCHES NNINTEGER ')' quantum_op - # ---------------------------------------- - def p_if(self, program): - """ - if : IF '(' id MATCHES NNINTEGER ')' quantum_op - if : IF '(' id error - if : IF '(' id MATCHES error - if : IF '(' id MATCHES NNINTEGER error - if : IF error - """ - if len(program) == 3: - raise QasmError("Ill-formed IF statement. Perhaps a" + " missing '('?") - if len(program) == 5: - raise QasmError( - "Ill-formed IF statement. Expected '==', " + "received '" + str(program[4].value) - ) - if len(program) == 6: - raise QasmError( - "Ill-formed IF statement. Expected a number, " - + "received '" - + str(program[5].value) - ) - if len(program) == 7: - raise QasmError("Ill-formed IF statement, unmatched '('") - - if program[7].type == "if": - raise QasmError("Nested IF statements not allowed") - if program[7].type == "barrier": - raise QasmError("barrier not permitted in IF statement") - - program[0] = node.If([program[3], node.Int(program[5]), program[7]]) - - # ---------------------------------------- - # These are all the things you can have outside of a gate declaration - # quantum_op : unitary_op - # | opaque - # | measure - # | reset - # | barrier - # | if - # - # ---------------------------------------- - def p_quantum_op(self, program): - """ - quantum_op : unitary_op - | opaque - | measure - | barrier - | reset - | if - """ - program[0] = program[1] - - # ---------------------------------------- - # unary : NNINTEGER - # | REAL - # | PI - # | ID - # | '(' expression ')' - # | id '(' expression ')' - # - # We will trust 'expression' to throw before we have to handle it here - # ---------------------------------------- - def p_unary_0(self, program): - """ - unary : NNINTEGER - """ - program[0] = node.Int(program[1]) - - def p_unary_1(self, program): - """ - unary : REAL - """ - program[0] = node.Real(program[1]) - - def p_unary_2(self, program): - """ - unary : PI - """ - program[0] = node.Real(np.pi) - - def p_unary_3(self, program): - """ - unary : id - """ - program[0] = program[1] - - def p_unary_4(self, program): - """ - unary : '(' expression ')' - """ - program[0] = program[2] - - def p_unary_6(self, program): - """ - unary : id '(' expression ')' - """ - # note this is a semantic check, not syntactic - if program[1].name not in self.external_functions: - raise QasmError("Illegal external function call: ", str(program[1].name)) - program[0] = node.External([program[1], program[3]]) - - # ---------------------------------------- - # Prefix - # ---------------------------------------- - - def p_expression_1(self, program): - """ - expression : '-' expression %prec negative - | '+' expression %prec positive - """ - program[0] = node.Prefix([node.UnaryOperator(program[1]), program[2]]) - - def p_expression_0(self, program): - """ - expression : expression '*' expression - | expression '/' expression - | expression '+' expression - | expression '-' expression - | expression '^' expression - """ - program[0] = node.BinaryOp([node.BinaryOperator(program[2]), program[1], program[3]]) - - def p_expression_2(self, program): - """ - expression : unary - """ - program[0] = program[1] - - # ---------------------------------------- - # exp_list : exp - # | exp_list ',' exp - # ---------------------------------------- - def p_exp_list_0(self, program): - """ - exp_list : expression - """ - program[0] = node.ExpressionList([program[1]]) - - def p_exp_list_1(self, program): - """ - exp_list : exp_list ',' expression - """ - program[0] = program[1] - program[0].add_child(program[3]) - - def p_ignore(self, _): - """ - ignore : STRING - """ - # this should never hit but it keeps the insuppressible warnings at bay - pass - - def p_error(self, program): - # EOF is a special case because the stupid error token isn't placed - # on the stack - if not program: - raise QasmError("Error at end of file. " + "Perhaps there is a missing ';'") - - col = self.find_column(self.lexer.data, program) - print("Error near line", str(self.lexer.lineno), "Column", col) - - def find_column(self, input_, token): - """Compute the column. - - Input is the input text string. - token is a token instance. - """ - if token is None: - return 0 - last_cr = input_.rfind("\n", 0, token.lexpos) - last_cr = max(last_cr, 0) - column = (token.lexpos - last_cr) + 1 - return column - - def read_tokens(self): - """finds and reads the tokens.""" - try: - while True: - token = self.lexer.token() - - if not token: - break - - yield token - except QasmError as e: - print("Exception tokenizing qasm file:", e.msg) - - def parse_debug(self, val): - """Set the parse_deb field.""" - if val is True: - self.parse_deb = True - elif val is False: - self.parse_deb = False - else: - raise QasmError("Illegal debug value '" + str(val) + "' must be True or False.") - - def parse(self, data): - """Parse some data.""" - self.parser.parse(data, lexer=self.lexer, debug=self.parse_deb) - if self.qasm is None: - raise QasmError("Uncaught exception in parser; " + "see previous messages for details.") - return self.qasm - - def print_tree(self): - """Print parsed OPENQASM.""" - if self.qasm is not None: - self.qasm.to_string(0) - else: - print("No parsed qasm to print") - - def run(self, data): - """Parser runner. - - To use this module stand-alone. - """ - ast = self.parser.parse(data, debug=True) - self.parser.parse(data, debug=True) - ast.to_string(0) diff --git a/qiskit/qasm2/__init__.py b/qiskit/qasm2/__init__.py index e14ab420f380..485c210c0632 100644 --- a/qiskit/qasm2/__init__.py +++ b/qiskit/qasm2/__init__.py @@ -397,19 +397,6 @@ def add_one(x): serialisation format, and expanded its behaviour as Qiskit expanded. The new parser under all its defaults implements the specification more strictly. -The complete legacy code-paths are - -.. code-block:: python - - from qiskit.converters import ast_to_dag, dag_to_circuit - from qiskit.qasm import Qasm - - def from_qasm_file(path: str): - dag_to_circuit(ast_to_dag(Qasm(filename=path).parse())) - - def from_qasm_str(qasm_str: str): - dag_to_circuit(ast_to_dag(Qasm(data=qasm_str).parse())) - In particular, in the legacy importers: * the `include_path` is effectively: diff --git a/qiskit/qasm2/parse.py b/qiskit/qasm2/parse.py index 116c6b7c9aa0..2bb4514dc2dc 100644 --- a/qiskit/qasm2/parse.py +++ b/qiskit/qasm2/parse.py @@ -234,15 +234,13 @@ def from_bytecode(bytecode, custom_instructions: Iterable[CustomInstruction]): qc._append(CircuitInstruction(Measure(), (qubits[qubit],), (clbits[clbit],))) elif opcode == OpCode.ConditionedMeasure: qubit, clbit, creg, value = op.operands - measure = Measure() - measure.condition = (qc.cregs[creg], value) + measure = Measure().c_if(qc.cregs[creg], value) qc._append(CircuitInstruction(measure, (qubits[qubit],), (clbits[clbit],))) elif opcode == OpCode.Reset: qc._append(CircuitInstruction(Reset(), (qubits[op.operands[0]],))) elif opcode == OpCode.ConditionedReset: qubit, creg, value = op.operands - reset = Reset() - reset.condition = (qc.cregs[creg], value) + reset = Reset().c_if(qc.cregs[creg], value) qc._append(CircuitInstruction(reset, (qubits[qubit],))) elif opcode == OpCode.Barrier: op_qubits = op.operands[0] @@ -324,14 +322,15 @@ def _define(self): # to pickle ourselves, we just eagerly create the definition and pickle that. def __getstate__(self): - return (self.name, self.num_qubits, self.params, self.definition) + return (self.name, self.num_qubits, self.params, self.definition, self.condition) def __setstate__(self, state): - name, num_qubits, params, definition = state + name, num_qubits, params, definition, condition = state super().__init__(name, num_qubits, params) self._gates = () self._bytecode = () self._definition = definition + self._condition = condition def _gate_builder(name, num_qubits, known_gates, bytecode): diff --git a/qiskit/qasm3/__init__.py b/qiskit/qasm3/__init__.py index 36bb4dcc2843..8e3de893ea3c 100644 --- a/qiskit/qasm3/__init__.py +++ b/qiskit/qasm3/__init__.py @@ -83,7 +83,7 @@ All features enabled by the experimental flags are naturally transient. If it becomes necessary to remove flags, they will be subject to `the standard Qiskit deprecation policy - `__. We will leave these experimental + `__. We will leave these experimental flags in place for as long as is reasonable. However, we cannot guarantee any support windows for *consumers* of OpenQASM 3 code generated diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py index d88a79b2faba..e99d497dd314 100644 --- a/qiskit/qobj/converters/pulse_instruction.py +++ b/qiskit/qobj/converters/pulse_instruction.py @@ -50,12 +50,12 @@ class ParametricPulseShapes(Enum): @classmethod def from_instance( cls, - instance: Union[library.ParametricPulse, library.SymbolicPulse], + instance: library.SymbolicPulse, ) -> "ParametricPulseShapes": """Get Qobj name from the pulse class instance. Args: - instance: Symbolic or ParametricPulse class. + instance: SymbolicPulse class. Returns: Qobj name. @@ -354,7 +354,7 @@ def _convert_play( Returns: Qobj instruction data. """ - if isinstance(instruction.pulse, (library.ParametricPulse, library.SymbolicPulse)): + if isinstance(instruction.pulse, library.SymbolicPulse): params = dict(instruction.pulse.parameters) # IBM backends expect "amp" to be the complex amplitude if "amp" in params and "angle" in params: @@ -505,6 +505,7 @@ def _convert_bundled_acquire( @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_acquire(self, shift, instruction): return self._convert_instruction(instruction, shift) @@ -512,6 +513,7 @@ def convert_acquire(self, shift, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_bundled_acquires(self, shift, instructions_): return self._convert_bundled_acquire(instructions_, shift) @@ -519,6 +521,7 @@ def convert_bundled_acquires(self, shift, instructions_): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_set_frequency(self, shift, instruction): return self._convert_instruction(instruction, shift) @@ -526,6 +529,7 @@ def convert_set_frequency(self, shift, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_shift_frequency(self, shift, instruction): return self._convert_instruction(instruction, shift) @@ -533,6 +537,7 @@ def convert_shift_frequency(self, shift, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_set_phase(self, shift, instruction): return self._convert_instruction(instruction, shift) @@ -540,6 +545,7 @@ def convert_set_phase(self, shift, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_shift_phase(self, shift, instruction): return self._convert_instruction(instruction, shift) @@ -547,6 +553,7 @@ def convert_shift_phase(self, shift, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_delay(self, shift, instruction): return self._convert_instruction(instruction, shift) @@ -554,6 +561,7 @@ def convert_delay(self, shift, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_play(self, shift, instruction): return self._convert_instruction(instruction, shift) @@ -561,6 +569,7 @@ def convert_play(self, shift, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_snapshot(self, shift, instruction): return self._convert_instruction(instruction, shift) @@ -958,6 +967,7 @@ def _convert_generic( @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_acquire(self, instruction): t0 = instruction.t0 @@ -969,6 +979,7 @@ def convert_acquire(self, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_set_phase(self, instruction): t0 = instruction.t0 @@ -980,6 +991,7 @@ def convert_set_phase(self, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_shift_phase(self, instruction): t0 = instruction.t0 @@ -991,6 +1003,7 @@ def convert_shift_phase(self, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_set_frequency(self, instruction): t0 = instruction.t0 @@ -1002,6 +1015,7 @@ def convert_set_frequency(self, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_shift_frequency(self, instruction): t0 = instruction.t0 @@ -1013,6 +1027,7 @@ def convert_shift_frequency(self, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_delay(self, instruction): t0 = instruction.t0 @@ -1024,6 +1039,7 @@ def convert_delay(self, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def bind_pulse(self, pulse): if pulse.name not in self._pulse_library: @@ -1032,6 +1048,7 @@ def bind_pulse(self, pulse): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_parametric(self, instruction): t0 = instruction.t0 @@ -1043,6 +1060,7 @@ def convert_parametric(self, instruction): @deprecate_func( additional_msg="Instead, call converter instance directory.", since="0.23.0", + package_name="qiskit-terra", ) def convert_snapshot(self, instruction): t0 = instruction.t0 diff --git a/qiskit/qpy/__init__.py b/qiskit/qpy/__init__.py index d2fd4db57f40..e16894b3e35d 100644 --- a/qiskit/qpy/__init__.py +++ b/qiskit/qpy/__init__.py @@ -480,7 +480,7 @@ With the support of :class:`.~ScheduleBlock`, now :class:`~.QuantumCircuit` can be serialized together with :attr:`~.QuantumCircuit.calibrations`, or -`Pulse Gates `_. +`Pulse Gates `_. In QPY version 5 and above, :ref:`qpy_circuit_calibrations` payload is packed after the :ref:`qpy_instructions` block. @@ -921,7 +921,7 @@ SPARSE_PAULI_OP_LIST_ELEM ------------------------- -This represents an instance of :class:`.PauliSumOp`. +This represents an instance of :class:`.SparsePauliOp`. .. code-block:: c diff --git a/qiskit/qpy/binary_io/circuits.py b/qiskit/qpy/binary_io/circuits.py index 81785bf78151..6d910746a89a 100644 --- a/qiskit/qpy/binary_io/circuits.py +++ b/qiskit/qpy/binary_io/circuits.py @@ -319,9 +319,17 @@ def _read_instruction( if condition: gate = gate.c_if(*condition) else: - if gate_name in { - "Initialize", - "StatePreparation", + if gate_name in {"Initialize", "StatePreparation"}: + if isinstance(params[0], str): + # the params are the labels of the initial state + gate = gate_class("".join(label for label in params)) + elif instruction.num_parameters == 1: + # the params is the integer indicating which qubits to initialize + gate = gate_class(int(params[0].real), instruction.num_qargs) + else: + # the params represent a list of complex amplitudes + gate = gate_class(params) + elif gate_name in { "UCRXGate", "UCRYGate", "UCRZGate", diff --git a/qiskit/qpy/binary_io/schedules.py b/qiskit/qpy/binary_io/schedules.py index 83a023b9527b..ce22702c89da 100644 --- a/qiskit/qpy/binary_io/schedules.py +++ b/qiskit/qpy/binary_io/schedules.py @@ -19,6 +19,10 @@ from io import BytesIO import numpy as np +import symengine as sym +from symengine.lib.symengine_wrapper import ( # pylint: disable = no-name-in-module + load_basic, +) from qiskit.exceptions import QiskitError from qiskit.pulse import library, channels, instructions @@ -26,14 +30,8 @@ from qiskit.qpy import formats, common, type_keys from qiskit.qpy.binary_io import value from qiskit.qpy.exceptions import QpyError -from qiskit.utils import optionals as _optional from qiskit.pulse.configuration import Kernel, Discriminator -if _optional.HAS_SYMENGINE: - import symengine as sym -else: - import sympy as sym - def _read_channel(file_obj, version): type_key = common.read_type_key(file_obj) @@ -106,23 +104,15 @@ def _read_discriminator(file_obj, version): def _loads_symbolic_expr(expr_bytes, use_symengine=False): if expr_bytes == b"": return None + expr_bytes = zlib.decompress(expr_bytes) if use_symengine: - _optional.HAS_SYMENGINE.require_now("load a symengine expression") - from symengine.lib.symengine_wrapper import ( # pylint: disable = no-name-in-module - load_basic, - ) - - expr = load_basic(zlib.decompress(expr_bytes)) + return load_basic(expr_bytes) else: from sympy import parse_expr - expr_txt = zlib.decompress(expr_bytes).decode(common.ENCODE) + expr_txt = expr_bytes.decode(common.ENCODE) expr = parse_expr(expr_txt) - if _optional.HAS_SYMENGINE: - from symengine import sympify - - return sympify(expr) - return expr + return sym.sympify(expr) def _read_symbolic_pulse(file_obj, version): @@ -404,7 +394,6 @@ def _dumps_symbolic_expr(expr, use_symengine): if expr is None: return b"" if use_symengine: - _optional.HAS_SYMENGINE.require_now("dump a symengine expression") expr_bytes = expr.__reduce__()[1][0] else: from sympy import srepr, sympify @@ -484,7 +473,7 @@ def _dumps_operand(operand, use_symengine): def _write_element(file_obj, element, metadata_serializer, use_symengine): if isinstance(element, ScheduleBlock): common.write_type_key(file_obj, type_keys.Program.SCHEDULE_BLOCK) - write_schedule_block(file_obj, element, metadata_serializer) + write_schedule_block(file_obj, element, metadata_serializer, use_symengine) else: type_key = type_keys.ScheduleInstruction.assign(element) common.write_type_key(file_obj, type_key) diff --git a/qiskit/qpy/binary_io/value.py b/qiskit/qpy/binary_io/value.py index 7bae82c6911f..686e72c9a4e6 100644 --- a/qiskit/qpy/binary_io/value.py +++ b/qiskit/qpy/binary_io/value.py @@ -19,6 +19,11 @@ import uuid import numpy as np +import symengine +from symengine.lib.symengine_wrapper import ( # pylint: disable = no-name-in-module + load_basic, +) + from qiskit.circuit import CASE_DEFAULT, Clbit, ClassicalRegister from qiskit.circuit.classical import expr, types @@ -26,7 +31,6 @@ from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.circuit.parametervector import ParameterVector, ParameterVectorElement from qiskit.qpy import common, formats, exceptions, type_keys -from qiskit.utils import optionals as _optional def _write_parameter(file_obj, obj): @@ -51,7 +55,6 @@ def _write_parameter_vec(file_obj, obj): def _write_parameter_expression(file_obj, obj, use_symengine): if use_symengine: - _optional.HAS_SYMENGINE.require_now("write_parameter_expression") expr_bytes = obj._symbol_expr.__reduce__()[1][0] else: from sympy import srepr, sympify @@ -224,13 +227,7 @@ def _read_parameter_expression(file_obj): ) from sympy.parsing.sympy_parser import parse_expr - if _optional.HAS_SYMENGINE: - from symengine import sympify - - expr_ = sympify(parse_expr(file_obj.read(data.expr_size).decode(common.ENCODE))) - else: - expr_ = parse_expr(file_obj.read(data.expr_size).decode(common.ENCODE)) - + expr_ = symengine.sympify(parse_expr(file_obj.read(data.expr_size).decode(common.ENCODE))) symbol_map = {} for _ in range(data.map_elements): elem_data = formats.PARAM_EXPR_MAP_ELEM( @@ -264,23 +261,14 @@ def _read_parameter_expression_v3(file_obj, vectors, use_symengine): data = formats.PARAMETER_EXPR( *struct.unpack(formats.PARAMETER_EXPR_PACK, file_obj.read(formats.PARAMETER_EXPR_SIZE)) ) - from sympy.parsing.sympy_parser import parse_expr payload = file_obj.read(data.expr_size) if use_symengine: - _optional.HAS_SYMENGINE.require_now("read_parameter_expression_v3") - from symengine.lib.symengine_wrapper import ( # pylint: disable = no-name-in-module - load_basic, - ) - expr_ = load_basic(payload) else: - if _optional.HAS_SYMENGINE: - from symengine import sympify + from sympy.parsing.sympy_parser import parse_expr - expr_ = sympify(parse_expr(payload.decode(common.ENCODE))) - else: - expr_ = parse_expr(payload.decode(common.ENCODE)) + expr_ = symengine.sympify(parse_expr(payload.decode(common.ENCODE))) symbol_map = {} for _ in range(data.map_elements): diff --git a/qiskit/qpy/interface.py b/qiskit/qpy/interface.py index f4a77ec14598..6de673afbc0f 100644 --- a/qiskit/qpy/interface.py +++ b/qiskit/qpy/interface.py @@ -75,7 +75,7 @@ def dump( programs: Union[List[QPY_SUPPORTED_TYPES], QPY_SUPPORTED_TYPES], file_obj: BinaryIO, metadata_serializer: Optional[Type[JSONEncoder]] = None, - use_symengine: bool = False, + use_symengine: bool = True, ): """Write QPY binary data to a file diff --git a/qiskit/quantum_info/operators/symplectic/clifford.py b/qiskit/quantum_info/operators/symplectic/clifford.py index e84253b80b7b..5bfa7815b1f8 100644 --- a/qiskit/quantum_info/operators/symplectic/clifford.py +++ b/qiskit/quantum_info/operators/symplectic/clifford.py @@ -230,13 +230,18 @@ def copy(self): @deprecate_func( since="0.24.0", + package_name="qiskit-terra", additional_msg="Instead, index or iterate through the Clifford.tableau attribute.", ) def __getitem__(self, key): """Return a stabilizer Pauli row""" return self.table.__getitem__(key) - @deprecate_func(since="0.24.0", additional_msg="Use Clifford.tableau property instead.") + @deprecate_func( + since="0.24.0", + package_name="qiskit-terra", + additional_msg="Use Clifford.tableau property instead.", + ) def __setitem__(self, key, value): """Set a stabilizer Pauli row""" self.tableau.__setitem__(key, self._stack_table_phase(value.array, value.phase)) diff --git a/qiskit/quantum_info/operators/symplectic/pauli.py b/qiskit/quantum_info/operators/symplectic/pauli.py index 295d8a97d01c..c5b93ceaf4e7 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli.py +++ b/qiskit/quantum_info/operators/symplectic/pauli.py @@ -110,7 +110,7 @@ class initialization (``Pauli('-iXYZ')``). A ``Pauli`` object can be P = (-i)^{q + z\cdot x} Z^z \cdot X^x. - The :math:`k`th qubit corresponds to the :math:`k`th entry in the + The :math:`k`-th qubit corresponds to the :math:`k`-th entry in the :math:`z` and :math:`x` arrays .. math:: diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index 213eafc89ab6..84c1e09636e4 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -1101,17 +1101,20 @@ def assign_parameters( return None if inplace else bound def apply_layout( - self, layout: TranspileLayout | List[int], num_qubits: int | None = None + self, layout: TranspileLayout | List[int] | None, num_qubits: int | None = None ) -> SparsePauliOp: """Apply a transpiler layout to this :class:`~.SparsePauliOp` Args: - layout: Either a :class:`~.TranspileLayout` or a list of integers. + layout: Either a :class:`~.TranspileLayout`, a list of integers or None. + If both layout and num_qubits are none, a copy of the operator is + returned. num_qubits: The number of qubits to expand the operator to. If not provided then if ``layout`` is a :class:`~.TranspileLayout` the number of the transpiler output circuit qubits will be used by default. If ``layout`` is a list of integers the permutation - specified will be applied without any expansion. + specified will be applied without any expansion. If layout is + None, the operator will be expanded to the given number of qubits. Returns: @@ -1119,6 +1122,9 @@ def apply_layout( """ from qiskit.transpiler.layout import TranspileLayout + if layout is None and num_qubits is None: + return self.copy() + n_qubits = self.num_qubits if isinstance(layout, TranspileLayout): n_qubits = len(layout._output_qubit_list) @@ -1130,8 +1136,10 @@ def apply_layout( f"applied to a {n_qubits} qubit operator" ) n_qubits = num_qubits - if any(x >= n_qubits for x in layout): + if layout is not None and any(x >= n_qubits for x in layout): raise QiskitError("Provided layout contains indicies outside the number of qubits.") + if layout is None: + layout = list(range(self.num_qubits)) new_op = type(self)("I" * n_qubits) return new_op.compose(self, qargs=layout) diff --git a/qiskit/quantum_info/synthesis/clifford_decompose.py b/qiskit/quantum_info/synthesis/clifford_decompose.py index 7ef88a5097a1..6a92c6eb014e 100644 --- a/qiskit/quantum_info/synthesis/clifford_decompose.py +++ b/qiskit/quantum_info/synthesis/clifford_decompose.py @@ -23,7 +23,9 @@ @deprecate_func( - additional_msg="Instead, use the function qiskit.synthesis.synth_clifford_full.", since="0.23.0" + additional_msg="Instead, use the function qiskit.synthesis.synth_clifford_full.", + since="0.23.0", + package_name="qiskit-terra", ) def decompose_clifford(clifford, method=None): """DEPRECATED: Decompose a Clifford operator into a QuantumCircuit. diff --git a/qiskit/quantum_info/synthesis/cnotdihedral_decompose.py b/qiskit/quantum_info/synthesis/cnotdihedral_decompose.py index 8e4c1a373035..c0491ffbd1e1 100644 --- a/qiskit/quantum_info/synthesis/cnotdihedral_decompose.py +++ b/qiskit/quantum_info/synthesis/cnotdihedral_decompose.py @@ -24,6 +24,7 @@ @deprecate_func( additional_msg="Instead, use the function qiskit.synthesis.synth_cnotdihedral_full.", since="0.23.0", + package_name="qiskit-terra", ) def decompose_cnotdihedral(elem): """DEPRECATED: Decompose a CNOTDihedral element into a QuantumCircuit. diff --git a/qiskit/quantum_info/synthesis/two_qubit_decompose.py b/qiskit/quantum_info/synthesis/two_qubit_decompose.py index 82088ae051f1..ba5e99d3064f 100644 --- a/qiskit/quantum_info/synthesis/two_qubit_decompose.py +++ b/qiskit/quantum_info/synthesis/two_qubit_decompose.py @@ -1086,7 +1086,7 @@ def decomp3_supercontrolled(self, target): return U3r, U3l, U2r, U2l, U1r, U1l, U0r, U0l - @deprecate_arg("target", new_alias="unitary", since="0.23.0") + @deprecate_arg("target", new_alias="unitary", since="0.23.0", package_name="qiskit-terra") def __call__( self, unitary: Operator | np.ndarray, diff --git a/qiskit/result/sampled_expval.py b/qiskit/result/sampled_expval.py index 4968fc4b7436..b38840531018 100644 --- a/qiskit/result/sampled_expval.py +++ b/qiskit/result/sampled_expval.py @@ -40,7 +40,6 @@ def sampled_expectation_value(dist, oper): """ from .counts import Counts from qiskit.quantum_info import Pauli, SparsePauliOp - from qiskit.opflow import PauliOp, PauliSumOp # This should be removed when these return bit-string keys if isinstance(dist, (QuasiDistribution, ProbDistribution)): @@ -54,13 +53,6 @@ def sampled_expectation_value(dist, oper): elif isinstance(oper, Pauli): oper_strs = [oper.to_label()] coeffs = np.asarray([1.0]) - elif isinstance(oper, PauliOp): - oper_strs = [oper.primitive.to_label()] - coeffs = np.asarray([1.0]) - elif isinstance(oper, PauliSumOp): - spo = oper.primitive - oper_strs = spo.paulis.to_labels() - coeffs = np.asarray(spo.coeffs) * oper.coeff elif isinstance(oper, SparsePauliOp): oper_strs = oper.paulis.to_labels() coeffs = np.asarray(oper.coeffs) diff --git a/qiskit/synthesis/__init__.py b/qiskit/synthesis/__init__.py index c312ae0dfebd..f4d6a73f2a70 100644 --- a/qiskit/synthesis/__init__.py +++ b/qiskit/synthesis/__init__.py @@ -84,6 +84,11 @@ .. autofunction:: generate_basic_approximations +Basis Change Synthesis +====================== + +.. autofunction:: synth_qft_line + """ from .evolution import ( @@ -120,3 +125,4 @@ ) from .stabilizer import synth_stabilizer_layers, synth_stabilizer_depth_lnn from .discrete_basis import SolovayKitaevDecomposition, generate_basic_approximations +from .qft import synth_qft_line diff --git a/qiskit/algorithms/time_evolvers/variational/solvers/ode/__init__.py b/qiskit/synthesis/qft/__init__.py similarity index 81% rename from qiskit/algorithms/time_evolvers/variational/solvers/ode/__init__.py rename to qiskit/synthesis/qft/__init__.py index 06684cb2d012..99bd2f7da9b2 100644 --- a/qiskit/algorithms/time_evolvers/variational/solvers/ode/__init__.py +++ b/qiskit/synthesis/qft/__init__.py @@ -10,4 +10,6 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""ODE Solvers""" +"""Module containing stabilizer QFT circuit synthesis.""" + +from .qft_decompose_lnn import synth_qft_line diff --git a/qiskit/synthesis/qft/qft_decompose_lnn.py b/qiskit/synthesis/qft/qft_decompose_lnn.py new file mode 100644 index 000000000000..4923a45d9502 --- /dev/null +++ b/qiskit/synthesis/qft/qft_decompose_lnn.py @@ -0,0 +1,74 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Circuit synthesis for a QFT circuit. +""" + +import numpy as np +from qiskit.circuit import QuantumCircuit +from qiskit.synthesis.linear_phase.cz_depth_lnn import _append_cx_stage1, _append_cx_stage2 + + +def synth_qft_line( + num_qubits: int, do_swaps: bool = True, approximation_degree: int = 0 +) -> QuantumCircuit: + """Synthesis of a QFT circuit for a linear nearest neighbor connectivity. + Based on Fig 2.b in Fowler et al. [1]. + + Note that this method *reverts* the order of qubits in the circuit, + compared to the original :class:`.QFT` code. + Hence, the default value of the ``do_swaps`` parameter is ``True`` + since it produces a circuit with fewer CX gates. + + Args: + num_qubits: The number of qubits on which the QFT acts. + approximation_degree: The degree of approximation (0 for no approximation). + do_swaps: Whether to include the final swaps in the QFT. + + Return: + A circuit implementation of the QFT circuit. + + Reference: + 1. A. G. Fowler, S. J. Devitt, and L. C. L. Hollenberg, + *Implementation of Shor's algorithm on a linear nearest neighbour qubit array*, + Quantum Info. Comput. 4, 4 (July 2004), 237–251. + `arXiv:quant-ph/0402196 [quant-ph] `_ + """ + + qc = QuantumCircuit(num_qubits) + + for i in range(num_qubits): + qc.h(num_qubits - 1) + + for j in range(i, num_qubits - 1): + if j - i + 2 < num_qubits - approximation_degree + 1: + qc.p(np.pi / 2 ** (j - i + 2), num_qubits - j + i - 1) + qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2) + qc.p(-np.pi / 2 ** (j - i + 2), num_qubits - j + i - 2) + qc.cx(num_qubits - j + i - 2, num_qubits - j + i - 1) + qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2) + qc.p(np.pi / 2 ** (j - i + 2), num_qubits - j + i - 1) + else: + qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2) + qc.cx(num_qubits - j + i - 2, num_qubits - j + i - 1) + qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2) + + if not do_swaps: + # Add a reversal network for LNN connectivity in depth 2*n+2, + # based on Kutin at al., https://arxiv.org/abs/quant-ph/0701194, Section 5. + for _ in range((num_qubits + 1) // 2): + qc = _append_cx_stage1(qc, num_qubits) + qc = _append_cx_stage2(qc, num_qubits) + if (num_qubits % 2) == 0: + qc = _append_cx_stage1(qc, num_qubits) + + return qc diff --git a/qiskit/test/base.py b/qiskit/test/base.py index 588ca89434a0..1dde885bd67f 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -202,9 +202,6 @@ def setUpClass(cls): warnings.filterwarnings("error", category=DeprecationWarning) allow_DeprecationWarning_modules = [ - "test.python.pulse.test_parameters", - "test.python.pulse.test_transforms", - "test.python.circuit.test_gate_power", "test.python.pulse.test_builder", "test.python.pulse.test_block", "test.python.quantum_info.operators.symplectic.test_legacy_pauli", @@ -218,8 +215,6 @@ def setUpClass(cls): "qiskit.pulse.instructions.play", "qiskit.pulse.library.parametric_pulses", "qiskit.quantum_info.operators.symplectic.pauli", - "test.python.dagcircuit.test_dagcircuit", - "importlib_metadata", ] for mod in allow_DeprecationWarning_modules: warnings.filterwarnings("default", category=DeprecationWarning, module=mod) @@ -228,14 +223,8 @@ def setUpClass(cls): r"The jsonschema validation included in qiskit-terra.*", r"The DerivativeBase.parameter_expression_grad method.*", r"The property ``qiskit\.circuit\.bit\.Bit\.(register|index)`` is deprecated.*", - r"The CXDirection pass has been deprecated", # Caused by internal scikit-learn scipy usage r"The 'sym_pos' keyword is deprecated and should be replaced by using", - # jupyter_client 7.4.8 uses deprecated shims in pyzmq that raise warnings with pyzmq 25. - # These are due to be fixed by jupyter_client 8, see: - # - https://github.com/jupyter/jupyter_client/issues/913 - # - https://github.com/jupyter/jupyter_client/pull/842 - r"zmq\.eventloop\.ioloop is deprecated in pyzmq .*", ] for msg in allow_DeprecationWarning_message: warnings.filterwarnings("default", category=DeprecationWarning, message=msg) @@ -245,7 +234,7 @@ def setUpClass(cls): "Setting metadata to None.*", # and this one once Qiskit/qiskit-aer#1945 is merged and released. r"The method ``qiskit\.circuit\.quantumcircuit\.QuantumCircuit\.i\(\)`` is " - r"deprecated as of qiskit-terra 0\.45\.0\. It will be removed no earlier than 3 " + r"deprecated as of qiskit 0\.45\.0\. It will be removed no earlier than 3 " r"months after the release date\. Use QuantumCircuit\.id as direct replacement\.", ] diff --git a/qiskit/tools/jupyter/library.py b/qiskit/tools/jupyter/library.py index 09057db8c355..57a27ece8cdc 100644 --- a/qiskit/tools/jupyter/library.py +++ b/qiskit/tools/jupyter/library.py @@ -43,6 +43,7 @@ def _generate_circuit_library_visualization(circuit: QuantumCircuit): @deprecate_func( since="0.25.0", additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", + package_name="qiskit-terra", ) def circuit_data_table(circuit: QuantumCircuit) -> wid.HTML: """Create a HTML table widget for a given quantum circuit. @@ -112,6 +113,7 @@ def circuit_data_table(circuit: QuantumCircuit) -> wid.HTML: @deprecate_func( since="0.25.0", additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", + package_name="qiskit-terra", ) def properties_widget(circuit: QuantumCircuit) -> wid.VBox: """Create a HTML table widget with header for a given quantum circuit. @@ -129,69 +131,10 @@ def properties_widget(circuit: QuantumCircuit) -> wid.VBox: return properties -@_optionals.HAS_PYGMENTS.require_in_call -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", -) -def qasm_widget(circuit: QuantumCircuit) -> wid.VBox: - """Generate an OpenQASM widget with header for a quantum circuit. - - Args: - circuit: Input quantum circuit. - - Returns: - Output widget. - """ - import pygments - from pygments.formatters import HtmlFormatter - from qiskit.qasm.pygments import QasmHTMLStyle, OpenQASMLexer - - qasm_code = circuit.qasm() - code = pygments.highlight(qasm_code, OpenQASMLexer(), HtmlFormatter()) - - html_style = HtmlFormatter(style=QasmHTMLStyle).get_style_defs(".highlight") - - code_style = ( - """ - - """ - % html_style - ) - - out = wid.HTML( - code_style + code, - layout=wid.Layout(max_height="500px", height="auto", overflow="scroll scroll"), - ) - - out_label = wid.HTML( - f"

OpenQASM

", - layout=wid.Layout(margin="0px 0px 10px 0px"), - ) - - qasm = wid.VBox( - children=[out_label, out], - layout=wid.Layout( - height="auto", max_height="500px", width="60%", margin="0px 0px 0px 20px" - ), - ) - - qasm._code_length = len(qasm_code.split("\n")) - return qasm - - @deprecate_func( since="0.25.0", additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", + package_name="qiskit-terra", ) def circuit_diagram_widget() -> wid.Box: """Create a circuit diagram widget. @@ -218,6 +161,7 @@ def circuit_diagram_widget() -> wid.Box: @deprecate_func( since="0.25.0", additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", + package_name="qiskit-terra", ) def circuit_library_widget(circuit: QuantumCircuit) -> None: """Create a circuit library widget. @@ -225,8 +169,7 @@ def circuit_library_widget(circuit: QuantumCircuit) -> None: Args: circuit: Input quantum circuit. """ - qasm_wid = qasm_widget(circuit) - sep_length = str(min(20 * qasm_wid._code_length, 495)) + sep_length = str(min(20, 495)) # The separator widget sep = wid.HTML( @@ -234,7 +177,7 @@ def circuit_library_widget(circuit: QuantumCircuit) -> None: layout=wid.Layout(height="auto", max_height="495px", margin="40px 0px 0px 20px"), ) bottom = wid.HBox( - children=[properties_widget(circuit), sep, qasm_widget(circuit)], + children=[properties_widget(circuit), sep], layout=wid.Layout(max_height="550px", height="auto"), ) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 9abab3aa56a2..71f596c95dad 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -105,28 +105,35 @@ .. code-block:: python - from qiskit.circuit.library import XGate, HGate, RXGate, PhaseGate, TGate, TdgGate + import numpy as np + from qiskit.circuit.library import HGate, PhaseGate, RXGate, TdgGate, TGate, XGate from qiskit.transpiler import PassManager - from qiskit.transpiler.passes import ALAPScheduleAnalysis, PadDynamicalDecoupling - from qiskit.transpiler.passes import CXCancellation, InverseCancellation + from qiskit.transpiler.passes import ( + ALAPScheduleAnalysis, + CXCancellation, + InverseCancellation, + PadDynamicalDecoupling, + ) - backend_durations = backend.target.durations() dd_sequence = [XGate(), XGate()] - scheduling_pm = PassManager([ - ALAPScheduleAnalysis(backend_durations), - PadDynamicalDecoupling(backend_durations, dd_sequence), - ]) + scheduling_pm = PassManager( + [ + ALAPScheduleAnalysis(target=backend.target), + PadDynamicalDecoupling(target=backend.target, dd_sequence=dd_sequence), + ] + ) inverse_gate_list = [ HGate(), (RXGate(np.pi / 4), RXGate(-np.pi / 4)), (PhaseGate(np.pi / 4), PhaseGate(-np.pi / 4)), (TGate(), TdgGate()), - - ]) - logical_opt = PassManager([ - CXCancellation(), - InverseCancellation([HGate(), (RXGate(np.pi / 4), RXGate(-np.pi / 4)) - ]) + ] + logical_opt = PassManager( + [ + CXCancellation(), + InverseCancellation(inverse_gate_list), + ] + ) # Add pre-layout stage to run extra logical optimization @@ -134,26 +141,26 @@ # Set scheduling stage to custom pass manager pass_manager.scheduling = scheduling_pm - -Then when :meth:`~.StagedPassManager.run` is called on ``pass_manager`` the -``logical_opt`` :class:`~.PassManager` will be called prior to the ``layout`` stage -and for the ``scheduling`` stage our custom :class:`~.PassManager` -``scheduling_pm`` will be used. +Now, when the staged pass manager is run via the :meth:`~.StagedPassManager.run` method, +the ``logical_opt`` pass manager will be called before the ``layout`` stage, and the +``scheduling_pm`` pass manager will be used for the ``scheduling`` stage instead of the default. Custom Pass Managers ==================== In addition to modifying preset pass managers, it is also possible to construct a pass manager to build an entirely custom pipeline for transforming input -circuits. You can leverage the :class:`~.StagedPassManager` class directly to do +circuits. You can use the :class:`~.StagedPassManager` class directly to do this. You can define arbitrary stage names and populate them with a :class:`~.PassManager` -instance. For example:: +instance. For example, the following code creates a new :class:`~.StagedPassManager` +that has 2 stages, ``init`` and ``translation``.:: from qiskit.transpiler.passes import ( UnitarySynthesis, Collect2qBlocks, ConsolidateBlocks, UnitarySynthesis, + Unroll3qOrMore, ) from qiskit.transpiler import PassManager, StagedPassManager @@ -171,13 +178,11 @@ stages=["init", "translation"], init=init, translation=translate ) -will create a new :class:`~.StagedPassManager` that has 2 stages ``init`` and ``translation``. -There is no limit on the number of stages you can put in a custom :class:`~.StagedPassManager` -instance. +There is no limit on the number of stages you can put in a :class:`~.StagedPassManager`. -The :ref:`stage_generators` functions may be useful for the construction of custom pass managers. -They generate stages which provide common functionality used in many pass managers. -For example, :func:`~.generate_embed_passmanager` can be used to generate a stage +The :ref:`stage_generators` may be useful for the construction of custom :class:`~.StagedPassManager`s. +They generate pass managers which provide common functionality used in many stages. +For example, :func:`~.generate_embed_passmanager` generates a :class:`~.PassManager` to "embed" a selected initial :class:`~.Layout` from a layout pass to the specified target device. Representing Quantum Computers @@ -1054,7 +1059,7 @@ C ░░░░░░░░░░░░░░░░▒▒░ However, the :class:`.QuantumCircuit` representation is not accurate enough to represent -this model. In the circuit representation, the corresponding :class:`.pulse.Qubit` is occupied +this model. In the circuit representation, the corresponding :class:`.circuit.Qubit` is occupied by the stimulus microwave signal during the first half of the interval, and the :class:`.Clbit` is only occupied at the very end of the interval. @@ -1251,6 +1256,8 @@ .. autoexception:: TranspilerAccessError .. autoexception:: CouplingError .. autoexception:: LayoutError +.. autoexception:: CircuitTooWideForTarget + """ # For backward compatibility @@ -1263,7 +1270,13 @@ from .passmanager import PassManager, StagedPassManager from .passmanager_config import PassManagerConfig from .propertyset import PropertySet # pylint: disable=no-name-in-module -from .exceptions import TranspilerError, TranspilerAccessError, CouplingError, LayoutError +from .exceptions import ( + TranspilerError, + TranspilerAccessError, + CouplingError, + LayoutError, + CircuitTooWideForTarget, +) from .fencedobjs import FencedDAGCircuit, FencedPropertySet from .basepasses import AnalysisPass, TransformationPass from .coupling import CouplingMap diff --git a/qiskit/transpiler/basepasses.py b/qiskit/transpiler/basepasses.py index 02593c0bb9fd..c09ee190e38b 100644 --- a/qiskit/transpiler/basepasses.py +++ b/qiskit/transpiler/basepasses.py @@ -74,7 +74,6 @@ class BasePass(GenericPass, metaclass=MetaPass): def __init__(self): super().__init__() self.preserves: Iterable[GenericPass] = [] - self.property_set = PropertySet() self._hash = hash(None) def __hash__(self): @@ -118,21 +117,6 @@ def is_analysis_pass(self): """ return isinstance(self, AnalysisPass) - def execute( - self, - passmanager_ir: PassManagerIR, - state: PassManagerState, - callback: Callable = None, - ) -> tuple[PassManagerIR, PassManagerState]: - # For backward compatibility. - # Circuit passes access self.property_set. - self.property_set = state.property_set - return super().execute( - passmanager_ir=passmanager_ir, - state=state, - callback=callback, - ) - def __call__( self, circuit: QuantumCircuit, diff --git a/qiskit/transpiler/exceptions.py b/qiskit/transpiler/exceptions.py index ef79603bfed2..5c23cb2b3914 100644 --- a/qiskit/transpiler/exceptions.py +++ b/qiskit/transpiler/exceptions.py @@ -49,3 +49,7 @@ def __init__(self, *msg): def __str__(self): """Return the message.""" return repr(self.msg) + + +class CircuitTooWideForTarget(TranspilerError): + """Error raised if the circuit is too wide for the target.""" diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py index f461f335373e..c0cb58b3f63e 100644 --- a/qiskit/transpiler/passes/__init__.py +++ b/qiskit/transpiler/passes/__init__.py @@ -117,6 +117,10 @@ ValidatePulseGates InstructionDurationCheck SetIOLatency + ALAPSchedule + ASAPSchedule + DynamicalDecoupling + AlignMeasures Circuit Analysis ================ @@ -173,6 +177,7 @@ GatesInBasis ConvertConditionsToIfOps UnrollForLoops + FilterOpNodes """ # layout selection (placement) @@ -267,6 +272,10 @@ from .scheduling import ConstrainedReschedule from .scheduling import InstructionDurationCheck from .scheduling import SetIOLatency +from .scheduling import ALAPSchedule +from .scheduling import ASAPSchedule +from .scheduling import DynamicalDecoupling +from .scheduling import AlignMeasures # additional utility passes from .utils import CheckMap @@ -284,3 +293,4 @@ from .utils import GatesInBasis from .utils import ConvertConditionsToIfOps from .utils import UnrollForLoops +from .utils import FilterOpNodes diff --git a/qiskit/transpiler/passes/analysis/count_ops_longest_path.py b/qiskit/transpiler/passes/analysis/count_ops_longest_path.py index 4e06a2e6dd0a..fb3918bfed12 100644 --- a/qiskit/transpiler/passes/analysis/count_ops_longest_path.py +++ b/qiskit/transpiler/passes/analysis/count_ops_longest_path.py @@ -10,13 +10,13 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Count the operations on the longest path in a DAGcircuit.""" +"""Count the operations on the longest path in a DAGCircuit.""" from qiskit.transpiler.basepasses import AnalysisPass class CountOpsLongestPath(AnalysisPass): - """Count the operations on the longest path in a DAGcircuit. + """Count the operations on the longest path in a :class:`.DAGCircuit`. The result is saved in ``property_set['count_ops_longest_path']`` as an integer. """ diff --git a/qiskit/transpiler/passes/analysis/dag_longest_path.py b/qiskit/transpiler/passes/analysis/dag_longest_path.py index e35d86ca63ba..691910d2628f 100644 --- a/qiskit/transpiler/passes/analysis/dag_longest_path.py +++ b/qiskit/transpiler/passes/analysis/dag_longest_path.py @@ -10,13 +10,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Return the longest path in a DAGcircuit as a list of DAGNodes.""" +"""Return the longest path in a :class:`.DAGCircuit` as a list of DAGNodes.""" from qiskit.transpiler.basepasses import AnalysisPass class DAGLongestPath(AnalysisPass): - """Return the longest path in a DAGcircuit as a list of DAGOpNodes, DAGInNodes, and DAGOutNodes.""" + """Return the longest path in a :class:`.DAGCircuit` as a list of + :class:`.DAGOpNode`\\ s, :class:`.DAGInNode`\\ s, and :class:`.DAGOutNode`\\ s.""" def run(self, dag): """Run the DAGLongestPath pass on `dag`.""" diff --git a/qiskit/transpiler/passes/basis/basis_translator.py b/qiskit/transpiler/passes/basis/basis_translator.py index 3033ab398cfa..aed57ee34206 100644 --- a/qiskit/transpiler/passes/basis/basis_translator.py +++ b/qiskit/transpiler/passes/basis/basis_translator.py @@ -202,8 +202,8 @@ def run(self, dag): "target basis is not universal or there are additional equivalence rules " "needed in the EquivalenceLibrary being used. For more details on this " "error see: " - "https://qiskit.org/documentation/stubs/qiskit.transpiler.passes." - "BasisTranslator.html#translation_errors" + "https://docs.quantum-computing.ibm.com/api/qiskit/qiskit.transpiler.passes." + "BasisTranslator#translation-errors" ) qarg_local_basis_transforms[qarg] = local_basis_transforms @@ -220,8 +220,8 @@ def run(self, dag): f"basis: {list(target_basis)}. This likely means the target basis is not universal " "or there are additional equivalence rules needed in the EquivalenceLibrary being " "used. For more details on this error see: " - "https://qiskit.org/documentation/stubs/qiskit.transpiler.passes.BasisTranslator." - "html#translation_errors" + "https://docs.quantum-computing.ibm.com/api/qiskit/qiskit.transpiler.passes." + "BasisTranslator#translation-errors" ) # Compose found path into a set of instruction substitution rules. diff --git a/qiskit/transpiler/passes/basis/unroll_3q_or_more.py b/qiskit/transpiler/passes/basis/unroll_3q_or_more.py index ed6400dce94a..36433c71d1da 100644 --- a/qiskit/transpiler/passes/basis/unroll_3q_or_more.py +++ b/qiskit/transpiler/passes/basis/unroll_3q_or_more.py @@ -27,7 +27,7 @@ def __init__(self, target=None, basis_gates=None): Args: target (Target): The target object representing the compilation - target. If specified any multiqubit instructions in the + target. If specified any multi-qubit instructions in the circuit when the pass is run that are supported by the target device will be left in place. If both this and ``basis_gates`` are specified only the target will be checked. diff --git a/qiskit/transpiler/passes/basis/unroll_custom_definitions.py b/qiskit/transpiler/passes/basis/unroll_custom_definitions.py index 20e96f127d05..12e6811a2f03 100644 --- a/qiskit/transpiler/passes/basis/unroll_custom_definitions.py +++ b/qiskit/transpiler/passes/basis/unroll_custom_definitions.py @@ -29,7 +29,7 @@ def __init__(self, equivalence_library, basis_gates=None, target=None, min_qubit equivalence_library (EquivalenceLibrary): The equivalence library which will be used by the BasisTranslator pass. (Instructions in this library will not be unrolled by this pass.) - basis_gates (Optional[list[str]]): Target basis names to unroll to, e.g. `['u3', 'cx']`. + basis_gates (Optional[list[str]]): Target basis names to unroll to, e.g. ``['u3', 'cx']``. Ignored if ``target`` is also specified. target (Optional[Target]): The :class:`~.Target` object corresponding to the compilation target. When specified, any argument specified for ``basis_gates`` is ignored. diff --git a/qiskit/transpiler/passes/basis/unroller.py b/qiskit/transpiler/passes/basis/unroller.py index 25223756c3f4..918c9324f91e 100644 --- a/qiskit/transpiler/passes/basis/unroller.py +++ b/qiskit/transpiler/passes/basis/unroller.py @@ -29,8 +29,8 @@ class Unroller(TransformationPass): @deprecate_func( since="0.45.0", - additional_msg="This has been replaced by the `BasisTranslator` pass." - "This pass will be removed in Qiskit 1.0.", + additional_msg="This has been replaced by the `BasisTranslator` pass " + "and is going to be removed in Qiskit 1.0.", ) def __init__(self, basis=None, target=None): """Unroller initializer. diff --git a/qiskit/transpiler/passes/calibration/pulse_gate.py b/qiskit/transpiler/passes/calibration/pulse_gate.py index 9bfd3c544779..eacabbe89057 100644 --- a/qiskit/transpiler/passes/calibration/pulse_gate.py +++ b/qiskit/transpiler/passes/calibration/pulse_gate.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Instruction scheduel map reference pass.""" +"""Instruction schedule map reference pass.""" from typing import List, Union @@ -57,7 +57,7 @@ def __init__( Args: inst_map: Instruction schedule map that user may override. target: The :class:`~.Target` representing the target backend, if both - ``inst_map`` and this are specified then it updates instructions + ``inst_map`` and ``target`` are specified then it updates instructions in the ``target`` with ``inst_map``. """ super().__init__() diff --git a/qiskit/transpiler/passes/layout/apply_layout.py b/qiskit/transpiler/passes/layout/apply_layout.py index b6b7282e304e..8c6ed2cfec3a 100644 --- a/qiskit/transpiler/passes/layout/apply_layout.py +++ b/qiskit/transpiler/passes/layout/apply_layout.py @@ -36,7 +36,7 @@ class ApplyLayout(TransformationPass): """ def run(self, dag): - """Run the ApplyLayout pass on `dag`. + """Run the ApplyLayout pass on ``dag``. Args: dag (DAGCircuit): DAG to map. @@ -45,7 +45,7 @@ def run(self, dag): DAGCircuit: A mapped DAG (with physical qubits). Raises: - TranspilerError: if no layout is found in `property_set` or no full physical qubits. + TranspilerError: if no layout is found in ``property_set`` or no full physical qubits. """ layout = self.property_set["layout"] if not layout: @@ -77,7 +77,7 @@ def run(self, dag): new_dag.apply_operation_back(node.op, qargs, node.cargs, check=False) else: # First build a new layout object going from: - # old virtual -> old phsyical -> new virtual -> new physical + # old virtual -> old physical -> new virtual -> new physical # to: # old virtual -> new physical full_layout = Layout() diff --git a/qiskit/transpiler/passes/layout/dense_layout.py b/qiskit/transpiler/passes/layout/dense_layout.py index 973947420680..71c69739990d 100644 --- a/qiskit/transpiler/passes/layout/dense_layout.py +++ b/qiskit/transpiler/passes/layout/dense_layout.py @@ -31,9 +31,9 @@ class DenseLayout(AnalysisPass): of the circuit (Qubit). Note: - Even though a 'layout' is not strictly a property of the DAG, + Even though a ``'layout'`` is not strictly a property of the DAG, in the transpiler architecture it is best passed around between passes - by being set in `property_set`. + by being set in ``property_set``. """ def __init__(self, coupling_map=None, backend_prop=None, target=None): diff --git a/qiskit/transpiler/passes/layout/enlarge_with_ancilla.py b/qiskit/transpiler/passes/layout/enlarge_with_ancilla.py index 025f846b054b..fb0b24016f66 100644 --- a/qiskit/transpiler/passes/layout/enlarge_with_ancilla.py +++ b/qiskit/transpiler/passes/layout/enlarge_with_ancilla.py @@ -34,7 +34,7 @@ def run(self, dag): DAGCircuit: An extended DAG. Raises: - TranspilerError: If there is not layout in the property set or not set at init time. + TranspilerError: If there is no layout in the property set or not set at init time. """ layout = self.property_set["layout"] diff --git a/qiskit/transpiler/passes/layout/full_ancilla_allocation.py b/qiskit/transpiler/passes/layout/full_ancilla_allocation.py index 81ae7922806e..72d2c6f2c4d0 100644 --- a/qiskit/transpiler/passes/layout/full_ancilla_allocation.py +++ b/qiskit/transpiler/passes/layout/full_ancilla_allocation.py @@ -107,7 +107,7 @@ def run(self, dag): @staticmethod def validate_layout(layout_qubits, dag_qubits): """ - Checks if all the qregs in layout_qregs already exist in dag_qregs. Otherwise, raise. + Checks if all the qregs in ``layout_qregs`` already exist in ``dag_qregs``. Otherwise, raise. """ for qreg in layout_qubits: if qreg not in dag_qubits: diff --git a/qiskit/transpiler/passes/layout/layout_2q_distance.py b/qiskit/transpiler/passes/layout/layout_2q_distance.py index 6af709851702..fa1759a845f0 100644 --- a/qiskit/transpiler/passes/layout/layout_2q_distance.py +++ b/qiskit/transpiler/passes/layout/layout_2q_distance.py @@ -25,7 +25,7 @@ class Layout2qDistance(AnalysisPass): """Evaluate how good the layout selection was. - Saves in `property_set['layout_score']` (or the property name in property_name) + Saves in ``property_set['layout_score']`` (or the property name in property_name) the sum of distances for each circuit CX. The lower the number, the better the selection. Therefore, 0 is a perfect layout selection. No CX direction is considered. diff --git a/qiskit/transpiler/passes/layout/sabre_layout.py b/qiskit/transpiler/passes/layout/sabre_layout.py index 62af8fd57a35..ca71ebee2777 100644 --- a/qiskit/transpiler/passes/layout/sabre_layout.py +++ b/qiskit/transpiler/passes/layout/sabre_layout.py @@ -60,7 +60,7 @@ class SabreLayout(TransformationPass): This method exploits the reversibility of quantum circuits, and tries to include global circuit information in the choice of initial_layout. - By default this pass will run both layout and routing and will transform the + By default, this pass will run both layout and routing and will transform the circuit so that the layout is applied to the input dag (meaning that the output circuit will have ancilla qubits allocated for unused qubits on the coupling map and the qubits will be reordered to match the mapped physical qubits) and then @@ -152,7 +152,7 @@ def __init__( will be raised if both are used. skip_routing (bool): If this is set ``True`` and ``routing_pass`` is not used then routing will not be applied to the output circuit. Only the layout - will be returned in the property set. This is a tradeoff to run custom + will be set in the property set. This is a tradeoff to run custom routing with multiple layout trials, as using this option will cause SabreLayout to run the routing stage internally but not use that result. @@ -279,6 +279,10 @@ def run(self, dag): } ) + # Add the existing registers to the layout + for qreg in dag.qregs.values(): + self.property_set["layout"].add_register(qreg) + # If skip_routing is set then return the layout in the property set # and throwaway the extra work we did to compute the swap map. # We also skip routing here if there is more than one connected @@ -436,7 +440,7 @@ def _compose_layouts(self, initial_layout, pass_final_layout, qregs): The routing passes internally start with a trivial layout, as the layout gets applied to the circuit prior to running them. So the - "final_layout" they report must be amended to account for the actual + ``"final_layout"`` they report must be amended to account for the actual initial_layout that was selected. """ trivial_layout = Layout.generate_trivial_layout(*qregs) diff --git a/qiskit/transpiler/passes/layout/vf2_layout.py b/qiskit/transpiler/passes/layout/vf2_layout.py index d6de8f51d31f..4e3077eb1d4d 100644 --- a/qiskit/transpiler/passes/layout/vf2_layout.py +++ b/qiskit/transpiler/passes/layout/vf2_layout.py @@ -37,7 +37,7 @@ class VF2LayoutStopReason(Enum): class VF2Layout(AnalysisPass): - """A pass for choosing a Layout of a circuit onto a Coupling graph, as a + """A pass for choosing a Layout of a circuit onto a Coupling graph, as a subgraph isomorphism problem, solved by VF2++. If a solution is found that means there is a "perfect layout" and that no @@ -52,7 +52,7 @@ class VF2Layout(AnalysisPass): * ``"nonexistent solution"``: If no perfect layout was found. * ``">2q gates in basis"``: If VF2Layout can't work with basis - By default this pass will construct a heuristic scoring map based on the + By default, this pass will construct a heuristic scoring map based on the error rates in the provided ``target`` (or ``properties`` if ``target`` is not provided). However, analysis passes can be run prior to this pass and set ``vf2_avg_error_map`` in the property set with a :class:`~.ErrorMap` @@ -138,6 +138,8 @@ def run(self, dag): self.property_set["VF2Layout_stop_reason"] = VF2LayoutStopReason.MORE_THAN_2Q return im_graph, im_graph_node_map, reverse_im_graph_node_map, free_nodes = result + scoring_edge_list = vf2_utils.build_edge_list(im_graph) + scoring_bit_list = vf2_utils.build_bit_list(im_graph, im_graph_node_map) cm_graph, cm_nodes = vf2_utils.shuffle_coupling_graph( self.coupling_map, self.seed, self.strict_direction ) @@ -199,6 +201,8 @@ def mapping_to_layout(layout_mapping): reverse_im_graph_node_map, im_graph, self.strict_direction, + edge_list=scoring_edge_list, + bit_list=scoring_bit_list, ) # If the layout score is 0 we can't do any better and we'll just # waste time finding additional mappings that will at best match diff --git a/qiskit/transpiler/passes/layout/vf2_post_layout.py b/qiskit/transpiler/passes/layout/vf2_post_layout.py index cee0e1cf04a8..1f574fdeed10 100644 --- a/qiskit/transpiler/passes/layout/vf2_post_layout.py +++ b/qiskit/transpiler/passes/layout/vf2_post_layout.py @@ -34,13 +34,14 @@ class VF2PostLayoutStopReason(Enum): """Stop reasons for VF2PostLayout pass.""" SOLUTION_FOUND = "solution found" + NO_BETTER_SOLUTION_FOUND = "no better solution found" NO_SOLUTION_FOUND = "nonexistent solution" MORE_THAN_2Q = ">2q gates in basis" def _target_match(node_a, node_b): # Node A is the set of operations in the target. Node B is the count dict - # of oeprations on the node or edge in the circuit. + # of operations on the node or edge in the circuit. if isinstance(node_a, set): return node_a.issuperset(node_b.keys()) # Node A is the count dict of operations on the node or edge in the circuit @@ -50,7 +51,7 @@ def _target_match(node_a, node_b): class VF2PostLayout(AnalysisPass): - """A pass for choosing a Layout after transpilation of a circuit onto a + """A pass for improving an existing Layout after transpilation of a circuit onto a Coupling graph, as a subgraph isomorphism problem, solved by VF2++. Unlike the :class:`~.VF2Layout` transpiler pass which is designed to find an @@ -65,17 +66,18 @@ class VF2PostLayout(AnalysisPass): If a solution is found that means there is a lower error layout available for the circuit. If a solution is found the layout will be set in the property set as - ``property_set['post_layout']``. However, if no solution is found, no + ``property_set['post_layout']``. However, if no solution or no better solution is found, no ``property_set['post_layout']`` is set. The stopping reason is set in ``property_set['VF2PostLayout_stop_reason']`` in all the cases and will be one of the values enumerated in ``VF2PostLayoutStopReason`` which has the following values: * ``"solution found"``: If a solution was found. + * ``"no better solution found"``: If the initial layout of the circuit is the best solution. * ``"nonexistent solution"``: If no solution was found. - * ``">2q gates in basis"``: If VF2PostLayout can't work with basis + * ``">2q gates in basis"``: If VF2PostLayout can't work with the basis of the circuit. - By default this pass will construct a heuristic scoring map based on the + By default, this pass will construct a heuristic scoring map based on the error rates in the provided ``target`` (or ``properties`` if ``target`` is not provided). However, analysis passes can be run prior to this pass and set ``vf2_avg_error_map`` in the property set with a :class:`~.ErrorMap` @@ -167,6 +169,8 @@ def run(self, dag): self.property_set["VF2PostLayout_stop_reason"] = VF2PostLayoutStopReason.MORE_THAN_2Q return im_graph, im_graph_node_map, reverse_im_graph_node_map, free_nodes = result + scoring_bit_list = vf2_utils.build_bit_list(im_graph, im_graph_node_map) + scoring_edge_list = vf2_utils.build_edge_list(im_graph) if self.target is not None: # If qargs is None then target is global and ideal so no @@ -178,7 +182,7 @@ def run(self, dag): else: cm_graph = PyGraph(multigraph=False) # If None is present in qargs there are globally defined ideal operations - # we should add these to all entries based on the number of qubits so we + # we should add these to all entries based on the number of qubits, so we # treat that as a valid operation even if there is no scoring for the # strict direction case global_ops = None @@ -256,7 +260,10 @@ def run(self, dag): if self.strict_direction: initial_layout = Layout({bit: index for index, bit in enumerate(dag.qubits)}) chosen_layout_score = self._score_layout( - initial_layout, im_graph_node_map, reverse_im_graph_node_map, im_graph + initial_layout, + im_graph_node_map, + reverse_im_graph_node_map, + im_graph, ) else: initial_layout = { @@ -271,7 +278,11 @@ def run(self, dag): reverse_im_graph_node_map, im_graph, self.strict_direction, + edge_list=scoring_edge_list, + bit_list=scoring_bit_list, ) + chosen_layout = initial_layout + stop_reason = VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND # Circuit not in basis so we have nothing to compare against return here except KeyError: self.property_set[ @@ -286,7 +297,6 @@ def run(self, dag): for mapping in mappings: trials += 1 logger.debug("Running trial: %s", trials) - stop_reason = VF2PostLayoutStopReason.SOLUTION_FOUND layout_mapping = {im_i: cm_nodes[cm_i] for cm_i, im_i in mapping.items()} if self.strict_direction: layout = Layout( @@ -303,6 +313,8 @@ def run(self, dag): reverse_im_graph_node_map, im_graph, self.strict_direction, + edge_list=scoring_edge_list, + bit_list=scoring_bit_list, ) logger.debug("Trial %s has score %s", trials, layout_score) if layout_score < chosen_layout_score: @@ -318,6 +330,7 @@ def run(self, dag): ) chosen_layout = layout chosen_layout_score = layout_score + stop_reason = VF2PostLayoutStopReason.SOLUTION_FOUND if self.max_trials and trials >= self.max_trials: logger.debug("Trial %s is >= configured max trials %s", trials, self.max_trials) @@ -331,9 +344,7 @@ def run(self, dag): self.time_limit, ) break - if chosen_layout is None: - stop_reason = VF2PostLayoutStopReason.NO_SOLUTION_FOUND - else: + if stop_reason == VF2PostLayoutStopReason.SOLUTION_FOUND: chosen_layout = vf2_utils.map_free_qubits( free_nodes, chosen_layout, @@ -357,7 +368,10 @@ def run(self, dag): chosen_layout.add(bit, i) break self.property_set["post_layout"] = chosen_layout - + else: + if chosen_layout is None: + stop_reason = VF2PostLayoutStopReason.NO_SOLUTION_FOUND + # else the initial layout is optimal -> don't set post_layout, return 'no better solution' self.property_set["VF2PostLayout_stop_reason"] = stop_reason def _score_layout(self, layout, bit_map, reverse_bit_map, im_graph): diff --git a/qiskit/transpiler/passes/layout/vf2_utils.py b/qiskit/transpiler/passes/layout/vf2_utils.py index b6fc73f18802..99006017482c 100644 --- a/qiskit/transpiler/passes/layout/vf2_utils.py +++ b/qiskit/transpiler/passes/layout/vf2_utils.py @@ -95,6 +95,27 @@ def _visit(dag, weight, wire_map): return im_graph, im_graph_node_map, reverse_im_graph_node_map, free_nodes +def build_edge_list(im_graph): + """Generate an edge list for scoring.""" + return vf2_layout.EdgeList( + [((edge[0], edge[1]), sum(edge[2].values())) for edge in im_graph.edge_index_map().values()] + ) + + +def build_bit_list(im_graph, bit_map): + """Generate a bit list for scoring.""" + bit_list = np.zeros(len(im_graph), dtype=np.int32) + for node_index in bit_map.values(): + try: + bit_list[node_index] = sum(im_graph[node_index].values()) + # If node_index not in im_graph that means there was a standalone + # node we will score/sort separately outside the vf2 mapping, so we + # can skip the hole + except IndexError: + pass + return bit_list + + def score_layout( avg_error_map, layout_mapping, @@ -103,6 +124,8 @@ def score_layout( im_graph, strict_direction=False, run_in_parallel=False, + edge_list=None, + bit_list=None, ): """Score a layout given an average error map.""" if layout_mapping: @@ -110,18 +133,10 @@ def score_layout( else: size = 0 nlayout = NLayout(layout_mapping, size + 1, size + 1) - bit_list = np.zeros(len(im_graph), dtype=np.int32) - for node_index in bit_map.values(): - try: - bit_list[node_index] = sum(im_graph[node_index].values()) - # If node_index not in im_graph that means there was a standalone - # node we will score/sort separately outside the vf2 mapping, so we - # can skip the hole - except IndexError: - pass - edge_list = { - (edge[0], edge[1]): sum(edge[2].values()) for edge in im_graph.edge_index_map().values() - } + if bit_list is None: + bit_list = build_bit_list(im_graph, bit_map) + if edge_list is None: + edge_list = build_edge_list(im_graph) return vf2_layout.score_layout( bit_list, edge_list, avg_error_map, nlayout, strict_direction, run_in_parallel ) diff --git a/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py b/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py index b3e3f27b89ef..51b39d7e961b 100644 --- a/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py +++ b/qiskit/transpiler/passes/optimization/collect_multiqubit_blocks.py @@ -19,11 +19,11 @@ class CollectMultiQBlocks(AnalysisPass): """Collect sequences of uninterrupted gates acting on groups of qubits. - max_block_size specifies the maximum number of qubits that can be acted upon + ``max_block_size`` specifies the maximum number of qubits that can be acted upon by any single group of gates Traverse the DAG and find blocks of gates that act consecutively on - groups of qubits. Write the blocks to propert_set as a list of blocks + groups of qubits. Write the blocks to ``property_set`` as a list of blocks of the form:: [[g0, g1, g2], [g4, g5]] @@ -31,7 +31,7 @@ class CollectMultiQBlocks(AnalysisPass): Blocks are reported in a valid topological order. Further, the gates within each block are also reported in topological order Some gates may not be present in any block (e.g. if the number - of operands is greater than max_block_size) + of operands is greater than ``max_block_size``) A Disjoint Set Union data structure (DSU) is used to maintain blocks as gates are processed. This data structure points each qubit to a set at all diff --git a/qiskit/transpiler/passes/optimization/commutation_analysis.py b/qiskit/transpiler/passes/optimization/commutation_analysis.py index 0963a645c7b3..8c34c911a6ca 100644 --- a/qiskit/transpiler/passes/optimization/commutation_analysis.py +++ b/qiskit/transpiler/passes/optimization/commutation_analysis.py @@ -22,7 +22,7 @@ class CommutationAnalysis(AnalysisPass): """Analysis pass to find commutation relations between DAG nodes. - Property_set['commutation_set'] is a dictionary that describes + ``property_set['commutation_set']`` is a dictionary that describes the commutation relations on a given wire, all the gates on a wire are grouped into a set of gates that commute. """ @@ -35,7 +35,7 @@ def run(self, dag): """Run the CommutationAnalysis pass on `dag`. Run the pass on the DAG, and write the discovered commutation relations - into the property_set. + into the ``property_set``. """ # Initiate the commutation set self.property_set["commutation_set"] = defaultdict(list) diff --git a/qiskit/transpiler/passes/optimization/commutative_cancellation.py b/qiskit/transpiler/passes/optimization/commutative_cancellation.py index 777c4ff3dc48..b0eb6bd24137 100644 --- a/qiskit/transpiler/passes/optimization/commutative_cancellation.py +++ b/qiskit/transpiler/passes/optimization/commutative_cancellation.py @@ -50,7 +50,7 @@ def __init__(self, basis_gates=None, target=None): the set intersection between the ``basis_gates`` parameter and the gates in the dag. target (Target): The :class:`~.Target` representing the target backend, if both - ``basis_gates`` and this are specified then this argument will take + ``basis_gates`` and ``target`` are specified then this argument will take precedence and ``basis_gates`` will be ignored. """ super().__init__() diff --git a/qiskit/transpiler/passes/optimization/consolidate_blocks.py b/qiskit/transpiler/passes/optimization/consolidate_blocks.py index 6f973ee3fd6f..71065113bb5a 100644 --- a/qiskit/transpiler/passes/optimization/consolidate_blocks.py +++ b/qiskit/transpiler/passes/optimization/consolidate_blocks.py @@ -55,15 +55,15 @@ def __init__( ): """ConsolidateBlocks initializer. - If `kak_basis_gate` is not `None` it will be used as the basis gate for KAK decomposition. - Otherwise, if `basis_gates` is not `None` a basis gate will be chosen from this list. - Otherwise the basis gate will be `CXGate`. + If ``kak_basis_gate`` is not ``None`` it will be used as the basis gate for KAK decomposition. + Otherwise, if ``basis_gates`` is not ``None`` a basis gate will be chosen from this list. + Otherwise, the basis gate will be :class:`.CXGate`. Args: kak_basis_gate (Gate): Basis gate for KAK decomposition. force_consolidate (bool): Force block consolidation. basis_gates (List(str)): Basis gates from which to choose a KAK gate. - approximation_degree (float): a float between [0.0, 1.0]. Lower approximates more. + approximation_degree (float): a float between $[0.0, 1.0]$. Lower approximates more. target (Target): The target object for the compilation target backend. """ super().__init__() diff --git a/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py b/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py index 387bdc3ba4e0..cfde23061020 100644 --- a/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py +++ b/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py @@ -23,8 +23,8 @@ with simultaneous two-qubit and one-qubit gates. The method ignores crosstalk between pairs of single qubit gates. -The method assumes that all qubits get measured simultaneously whether or not -they need a measurement. This assumption is based on current device properties +The method assumes that all qubits get measured simultaneously, whether +they need a measurement or not. This assumption is based on current device properties and may need to be revised for future device generations. """ @@ -89,7 +89,7 @@ def __init__( inserts the measure gates. If CrosstalkAdaptiveSchedule is made aware of those measurements, it is included in the optimization. target (Target): A target representing the target backend, if both - ``backend_prop`` and this are specified then this argument will take + ``backend_prop`` and ``target`` are specified then this argument will take precedence and ``coupling_map`` will be ignored. Raises: ImportError: if unable to import z3 solver diff --git a/qiskit/transpiler/passes/optimization/cx_cancellation.py b/qiskit/transpiler/passes/optimization/cx_cancellation.py index 6dd4a5364552..df1ffc8ebe23 100644 --- a/qiskit/transpiler/passes/optimization/cx_cancellation.py +++ b/qiskit/transpiler/passes/optimization/cx_cancellation.py @@ -10,14 +10,14 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Cancel back-to-back `cx` gates in dag.""" +"""Cancel back-to-back ``cx`` gates in dag.""" from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.passes.utils import control_flow class CXCancellation(TransformationPass): - """Cancel back-to-back `cx` gates in dag.""" + """Cancel back-to-back ``cx`` gates in dag.""" @control_flow.trivial_recurse def run(self, dag): diff --git a/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py b/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py index 5f4be27d220e..9352ce08f365 100644 --- a/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py +++ b/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py @@ -41,7 +41,7 @@ def __init__(self, instruction_schedule_map=None, target=None): instruction_schedule_map (InstructionScheduleMap): the mapping from circuit :class:`~.circuit.Instruction` names and arguments to :class:`.Schedule`\\ s. target (Target): The :class:`~.Target` representing the target backend, if both - ``instruction_schedule_map`` and this are specified then this argument will take + ``instruction_schedule_map`` and ``target`` are specified then this argument will take precedence and ``instruction_schedule_map`` will be ignored. """ super().__init__() diff --git a/qiskit/transpiler/passes/optimization/inverse_cancellation.py b/qiskit/transpiler/passes/optimization/inverse_cancellation.py index 35dae7d85f07..bb9552b3aa19 100644 --- a/qiskit/transpiler/passes/optimization/inverse_cancellation.py +++ b/qiskit/transpiler/passes/optimization/inverse_cancellation.py @@ -29,12 +29,13 @@ def __init__(self, gates_to_cancel: List[Union[Gate, Tuple[Gate, Gate]]]): """Initialize InverseCancellation pass. Args: - gates_to_cancel: list of gates to cancel + gates_to_cancel: List describing the gates to cancel. Each element of the + list is either a single gate or a pair of gates. If a single gate, then + it should be self-inverse. If a pair of gates, then the gates in the + pair should be inverses of each other. Raises: - TranspilerError: - Initialization raises an error when the input is not a self-inverse gate - or a two-tuple of inverse gates. + TranspilerError: Input is not a self-inverse gate or a pair of inverse gates. """ for gates in gates_to_cancel: @@ -58,12 +59,16 @@ def __init__(self, gates_to_cancel: List[Union[Gate, Tuple[Gate, Gate]]]): self.self_inverse_gates = [] self.inverse_gate_pairs = [] + self.self_inverse_gate_names = set() + self.inverse_gate_pairs_names = set() for gates in gates_to_cancel: if isinstance(gates, Gate): self.self_inverse_gates.append(gates) + self.self_inverse_gate_names.add(gates.name) else: self.inverse_gate_pairs.append(gates) + self.inverse_gate_pairs_names.update(x.name for x in gates) super().__init__() @@ -76,11 +81,13 @@ def run(self, dag: DAGCircuit): Returns: DAGCircuit: Transformed DAG. """ + if self.self_inverse_gates: + dag = self._run_on_self_inverse(dag) + if self.inverse_gate_pairs: + dag = self._run_on_inverse_pairs(dag) + return dag - dag = self._run_on_self_inverse(dag, self.self_inverse_gates) - return self._run_on_inverse_pairs(dag, self.inverse_gate_pairs) - - def _run_on_self_inverse(self, dag: DAGCircuit, self_inverse_gates: List[Gate]): + def _run_on_self_inverse(self, dag: DAGCircuit): """ Run self-inverse gates on `dag`. @@ -91,14 +98,27 @@ def _run_on_self_inverse(self, dag: DAGCircuit, self_inverse_gates: List[Gate]): Returns: DAGCircuit: Transformed DAG. """ + op_counts = dag.count_ops() + if not self.self_inverse_gate_names.intersection(op_counts): + return dag # Sets of gate runs by name, for instance: [{(H 0, H 0), (H 1, H 1)}, {(X 0, X 0}] - gate_runs_sets = [dag.collect_runs([gate.name]) for gate in self_inverse_gates] - for gate_runs in gate_runs_sets: + for gate in self.self_inverse_gates: + gate_name = gate.name + gate_count = op_counts.get(gate_name, 0) + if gate_count <= 1: + continue + gate_runs = dag.collect_runs([gate_name]) for gate_cancel_run in gate_runs: partitions = [] chunk = [] for i in range(len(gate_cancel_run) - 1): - chunk.append(gate_cancel_run[i]) + if gate_cancel_run[i].op == gate: + chunk.append(gate_cancel_run[i]) + else: + if chunk: + partitions.append(chunk) + chunk = [] + continue if gate_cancel_run[i].qargs != gate_cancel_run[i + 1].qargs: partitions.append(chunk) chunk = [] @@ -112,7 +132,7 @@ def _run_on_self_inverse(self, dag: DAGCircuit, self_inverse_gates: List[Gate]): dag.remove_op_node(node) return dag - def _run_on_inverse_pairs(self, dag: DAGCircuit, inverse_gate_pairs: List[Tuple[Gate, Gate]]): + def _run_on_inverse_pairs(self, dag: DAGCircuit): """ Run inverse gate pairs on `dag`. @@ -123,8 +143,16 @@ def _run_on_inverse_pairs(self, dag: DAGCircuit, inverse_gate_pairs: List[Tuple[ Returns: DAGCircuit: Transformed DAG. """ - for pair in inverse_gate_pairs: - gate_cancel_runs = dag.collect_runs([pair[0].name, pair[1].name]) + op_counts = dag.count_ops() + if not self.inverse_gate_pairs_names.intersection(op_counts): + return dag + + for pair in self.inverse_gate_pairs: + gate_0_name = pair[0].name + gate_1_name = pair[1].name + if gate_0_name not in op_counts or gate_1_name not in op_counts: + continue + gate_cancel_runs = dag.collect_runs([gate_0_name, gate_1_name]) for dag_nodes in gate_cancel_runs: i = 0 while i < len(dag_nodes) - 1: diff --git a/qiskit/transpiler/passes/optimization/normalize_rx_angle.py b/qiskit/transpiler/passes/optimization/normalize_rx_angle.py index c8d9619c673f..b6f36e07de36 100644 --- a/qiskit/transpiler/passes/optimization/normalize_rx_angle.py +++ b/qiskit/transpiler/passes/optimization/normalize_rx_angle.py @@ -57,7 +57,7 @@ def __init__(self, target=None, resolution_in_radian=0): corresponding RX gates with SX and X gates. resolution_in_radian (float): Resolution for RX rotation angle quantization. If set to zero, this pass won't modify the rotation angles in the given DAG. - (=Provides aribitary-angle RX) + (=Provides arbitrary-angle RX) """ super().__init__() self.target = target diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py b/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py index fea459b41b9e..450490734e46 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py @@ -46,10 +46,10 @@ class Optimize1qGatesSimpleCommutation(TransformationPass): """ - Optimizes 1Q gate strings interrupted by 2Q gates by commuting the components and re- - synthesizing the results. The commutation rules are stored in `commutation_table`. + Optimizes 1Q gate strings interrupted by 2Q gates by commuting the components and + resynthesizing the results. The commutation rules are stored in ``commutation_table``. - NOTE: In addition to those mentioned in `commutation_table`, this pass has some limitations: + NOTE: In addition to those mentioned in ``commutation_table``, this pass has some limitations: + Does not handle multiple commutations in a row without intermediate progress. + Can only commute into positions where there are pre-existing runs. + Does not exhaustively test all the different ways commuting gates can be assigned to @@ -58,7 +58,7 @@ class Optimize1qGatesSimpleCommutation(TransformationPass): barriers.) """ - # NOTE: A run from `dag.collect_1q_runs` is always nonempty, so we sometimes use an empty list + # NOTE: A run from dag.collect_1q_runs is always nonempty, so we sometimes use an empty list # to signify the absence of a run. def __init__(self, basis=None, run_to_completion=False, target=None): @@ -83,7 +83,7 @@ def _find_adjoining_run(dag, runs, run, front=True): Finds the run which abuts `run` from the front (or the rear if `front == False`), separated by a blocking node. - Returns a pair of the abutting multi-qubit gate and the run which it separates from this + Returns a pair of the abutting multiqubit gate and the run which it separates from this one. The next run can be the empty list `[]` if it is absent. """ edge_node = run[0] if front else run[-1] diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py b/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py index 854fe8204551..9eb31b8f8134 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py @@ -59,9 +59,9 @@ class Optimize1qGatesDecomposition(TransformationPass): """Optimize chains of single-qubit gates by combining them into a single gate. - The decision to replace the original chain with a new resynthesis depends on: + The decision to replace the original chain with a new re-synthesis depends on: - whether the original chain was out of basis: replace - - whether the original chain was in basis but resynthesis is lower error: replace + - whether the original chain was in basis but re-synthesis is lower error: replace - whether the original chain contains a pulse gate: do not replace - whether the original chain amounts to identity: replace with null @@ -110,12 +110,12 @@ def _build_error_map(self): def _resynthesize_run(self, matrix, qubit=None): """ - Resynthesizes one 2x2 `matrix`, typically extracted via `dag.collect_1q_runs`. + Re-synthesizes one 2x2 `matrix`, typically extracted via `dag.collect_1q_runs`. Returns the newly synthesized circuit in the indicated basis, or None if no synthesis routine applied. - When multiple synthesis options are available, it prefers the one with lowest + When multiple synthesis options are available, it prefers the one with the lowest error when the circuit is applied to `qubit`. """ if self._target: @@ -173,10 +173,16 @@ def _substitution_checks(self, dag, old_run, new_circ, basis, qubit): # if we're outside of the basis set, we're obligated to logically decompose. # if we're outside of the set of gates for which we have physical definitions, # then we _try_ to decompose, using the results if we see improvement. + new_error = 0.0 + old_error = 0.0 + if not uncalibrated_and_not_basis_p: + new_error = self._error(new_circ, qubit) + old_error = self._error(old_run, qubit) + return ( uncalibrated_and_not_basis_p - or (uncalibrated_p and self._error(new_circ, qubit) < self._error(old_run, qubit)) - or math.isclose(self._error(new_circ, qubit)[0], 0) + or (uncalibrated_p and new_error < old_error) + or (math.isclose(new_error[0], 0) and not math.isclose(old_error[0], 0)) ) @control_flow.trivial_recurse diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_gates.py b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py index 1dd03a738e32..3d080219274b 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_gates.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py @@ -43,7 +43,7 @@ def __init__(self, basis=None, eps=1e-15, target=None): the set `{'u1','u2','u3', 'u', 'p'}`. eps (float): EPS to check against target (Target): The :class:`~.Target` representing the target backend, if both - ``basis`` and this are specified then this argument will take + ``basis`` and ``target`` are specified then this argument will take precedence and ``basis`` will be ignored. """ super().__init__() @@ -61,7 +61,7 @@ def run(self, dag): DAGCircuit: the optimized DAG. Raises: - TranspilerError: if YZY and ZYZ angles do not give same rotation matrix. + TranspilerError: if ``YZY`` and ``ZYZ`` angles do not give same rotation matrix. """ use_u = "u" in self.basis use_p = "p" in self.basis diff --git a/qiskit/transpiler/passes/optimization/reset_after_measure_simplification.py b/qiskit/transpiler/passes/optimization/reset_after_measure_simplification.py index d49485784026..faf0ed8de6b0 100644 --- a/qiskit/transpiler/passes/optimization/reset_after_measure_simplification.py +++ b/qiskit/transpiler/passes/optimization/reset_after_measure_simplification.py @@ -26,7 +26,7 @@ class ResetAfterMeasureSimplification(TransformationPass): This optimization is suitable for use on IBM Quantum systems where the reset operation is performed by a measurement followed by a conditional - x-gate. It might not be desireable on other backends if reset is implemented + x-gate. It might not be desirable on other backends if reset is implemented differently. """ diff --git a/qiskit/transpiler/passes/optimization/template_optimization.py b/qiskit/transpiler/passes/optimization/template_optimization.py index 0aeea99ab3c6..f4274d2331ec 100644 --- a/qiskit/transpiler/passes/optimization/template_optimization.py +++ b/qiskit/transpiler/passes/optimization/template_optimization.py @@ -57,16 +57,16 @@ def __init__( heuristics_backward_param (list[int]): [length, survivor] Those are the parameters for applying heuristics on the backward part of the algorithm. This part of the algorithm creates a tree of matching scenario. This tree grows exponentially. The - heuristics evaluates which scenarios have the longest match and keep only those. + heuristics evaluate which scenarios have the longest match and keep only those. The length is the interval in the tree for cutting it and survivor is the number - of scenarios that are kept. We advice to use l=3 and s=1 to have serious time + of scenarios that are kept. We advise to use l=3 and s=1 to have serious time advantage. We remind that the heuristics implies losing a part of the maximal matches. Check reference for more details. heuristics_qubits_param (list[int]): [length] The heuristics for the qubit choice make guesses from the dag dependency of the circuit in order to limit the number of qubit configurations to explore. The length is the number of successors or not predecessors that will be explored in the dag dependency of the circuit, each - qubits of the nodes are added to the set of authorized qubits. We advice to use + qubits of the nodes are added to the set of authorized qubits. We advise to use length=1. Check reference for more details. user_cost_dict (Dict[str, int]): quantum cost dictionary passed to TemplateSubstitution to configure its behavior. This will override any default values if None diff --git a/qiskit/transpiler/passes/routing/basic_swap.py b/qiskit/transpiler/passes/routing/basic_swap.py index af721ee51c7d..c73b2fb009c6 100644 --- a/qiskit/transpiler/passes/routing/basic_swap.py +++ b/qiskit/transpiler/passes/routing/basic_swap.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Map (with minimum effort) a DAGCircuit onto a `coupling_map` adding swap gates.""" +"""Map (with minimum effort) a DAGCircuit onto a ``coupling_map`` adding swap gates.""" from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.exceptions import TranspilerError @@ -22,7 +22,7 @@ class BasicSwap(TransformationPass): - """Map (with minimum effort) a DAGCircuit onto a `coupling_map` adding swap gates. + """Map (with minimum effort) a DAGCircuit onto a ``coupling_map`` adding swap gates. The basic mapper is a minimum effort to insert swap gates to map the DAG onto a coupling map. When a cx is not in the coupling map possibilities, it inserts @@ -34,7 +34,7 @@ def __init__(self, coupling_map, fake_run=False): Args: coupling_map (Union[CouplingMap, Target]): Directed graph represented a coupling map. - fake_run (bool): if true, it only pretend to do routing, i.e., no + fake_run (bool): if true, it will only pretend to do routing, i.e., no swap is effectively added. """ super().__init__() @@ -57,7 +57,7 @@ def run(self, dag): Raises: TranspilerError: if the coupling map or the layout are not - compatible with the DAG, or if the coupling_map=None. + compatible with the DAG, or if the ``coupling_map=None``. """ if self.fake_run: return self._fake_run(dag) diff --git a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py index 60b5cf9e3ad3..402aa9146f0a 100644 --- a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +++ b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py @@ -33,7 +33,7 @@ class Commuting2qGateRouter(TransformationPass): The mapping to the coupling map is done using swap strategies, see :class:`.SwapStrategy`. The swap strategy should suit the problem and the coupling map. This transpiler pass should ideally be executed before the quantum circuit is enlarged with any idle ancilla - qubits. Otherwise we may swap qubits outside of the portion of the chip we want to use. + qubits. Otherwise, we may swap qubits outside the portion of the chip we want to use. Therefore, the swap strategy and its associated coupling map do not represent physical qubits. Instead, they represent an intermediate mapping that corresponds to the physical qubits once the initial layout is applied. The example below shows how to map a four @@ -54,8 +54,8 @@ class Commuting2qGateRouter(TransformationPass): .. code-block:: python from qiskit import QuantumCircuit - from qiskit.opflow import PauliSumOp from qiskit.circuit.library import PauliEvolutionGate + from qiskit.quantum_info import SparsePauliOp from qiskit.transpiler import Layout, CouplingMap, PassManager from qiskit.transpiler.passes import FullAncillaAllocation from qiskit.transpiler.passes import EnlargeWithAncilla @@ -69,7 +69,7 @@ class Commuting2qGateRouter(TransformationPass): ) # Define the circuit on virtual qubits - op = PauliSumOp.from_list([("IZZI", 1), ("ZIIZ", 2), ("ZIZI", 3)]) + op = SparsePauliOp.from_list([("IZZI", 1), ("ZIIZ", 2), ("ZIZI", 3)]) circ = QuantumCircuit(4) circ.append(PauliEvolutionGate(op, 1), range(4)) @@ -111,7 +111,7 @@ def __init__( Args: swap_strategy: An instance of a :class:`.SwapStrategy` that holds the swap layers that are used, and the order in which to apply them, to map the instruction to - the hardware. If this field is not given if should be contained in the + the hardware. If this field is not given, it should be contained in the property set of the pass. This allows other passes to determine the most appropriate swap strategy at run-time. edge_coloring: An optional edge coloring of the coupling map (I.e. no two edges that diff --git a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py index 8b92c5cdaf53..641b40c9f3e1 100644 --- a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py +++ b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py @@ -113,7 +113,7 @@ def _pauli_to_edge(pauli: Pauli) -> Tuple[int, ...]: return edge def _decompose_to_2q(self, dag: DAGCircuit, op: PauliEvolutionGate) -> DAGCircuit: - """Decompose the PauliSumOp into two-qubit. + """Decompose the SparsePauliOp into two-qubit. Args: dag: The dag needed to get access to qubits. diff --git a/qiskit/transpiler/passes/routing/layout_transformation.py b/qiskit/transpiler/passes/routing/layout_transformation.py index 218e6ca958d3..42bf44e13f2a 100644 --- a/qiskit/transpiler/passes/routing/layout_transformation.py +++ b/qiskit/transpiler/passes/routing/layout_transformation.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Map (with minimum effort) a DAGCircuit onto a `coupling_map` adding swap gates.""" +"""Map (with minimum effort) a DAGCircuit onto a ``coupling_map`` adding swap gates.""" from __future__ import annotations import numpy as np @@ -49,7 +49,7 @@ def __init__( to_layout (Union[Layout, str]): The final layout of qubits on physical qubits. - If the type is str, look up `property_set` when this pass runs. + If the type is str, look up ``property_set`` when this pass runs. seed (Union[int, np.random.default_rng]): Seed to use for random trials. diff --git a/qiskit/transpiler/passes/routing/lookahead_swap.py b/qiskit/transpiler/passes/routing/lookahead_swap.py index 0893736a2aca..a961162ab802 100644 --- a/qiskit/transpiler/passes/routing/lookahead_swap.py +++ b/qiskit/transpiler/passes/routing/lookahead_swap.py @@ -90,7 +90,7 @@ def __init__(self, coupling_map, search_depth=4, search_width=4, fake_run=False) coupling_map (Union[CouplingMap, Target]): CouplingMap of the target backend. search_depth (int): lookahead tree depth when ranking best SWAP options. search_width (int): lookahead tree width when ranking best SWAP options. - fake_run (bool): if true, it only pretend to do routing, i.e., no + fake_run (bool): if true, it will only pretend to do routing, i.e., no swap is effectively added. """ diff --git a/qiskit/transpiler/passes/routing/sabre_swap.py b/qiskit/transpiler/passes/routing/sabre_swap.py index 3f83a02237cd..250b310d3a80 100644 --- a/qiskit/transpiler/passes/routing/sabre_swap.py +++ b/qiskit/transpiler/passes/routing/sabre_swap.py @@ -309,6 +309,7 @@ def process_dag(block_dag, wire_map): node._node_id, [wire_map[x] for x in node.qargs], cargs, + getattr(node.op, "_directive", False), ) ) return SabreDAG(num_physical_qubits, block_dag.num_clbits(), dag_list, node_blocks) diff --git a/qiskit/transpiler/passes/routing/stochastic_swap.py b/qiskit/transpiler/passes/routing/stochastic_swap.py index 90001bb06cc8..52f0d569931d 100644 --- a/qiskit/transpiler/passes/routing/stochastic_swap.py +++ b/qiskit/transpiler/passes/routing/stochastic_swap.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Map a DAGCircuit onto a `coupling_map` adding swap gates.""" +"""Map a DAGCircuit onto a ``coupling_map`` adding swap gates.""" import itertools import logging @@ -72,7 +72,7 @@ def __init__(self, coupling_map, trials=20, seed=None, fake_run=False, initial_l map. trials (int): maximum number of iterations to attempt seed (int): seed for random number generator - fake_run (bool): if true, it only pretend to do routing, i.e., no + fake_run (bool): if true, it will only pretend to do routing, i.e., no swap is effectively added. initial_layout (Layout): starting layout at beginning of pass. """ diff --git a/qiskit/transpiler/passes/scheduling/__init__.py b/qiskit/transpiler/passes/scheduling/__init__.py index 69485e88e4cf..6283faff0001 100644 --- a/qiskit/transpiler/passes/scheduling/__init__.py +++ b/qiskit/transpiler/passes/scheduling/__init__.py @@ -12,6 +12,9 @@ """Module containing circuit scheduling passes.""" +from .alap import ALAPSchedule +from .asap import ASAPSchedule +from .dynamical_decoupling import DynamicalDecoupling from .scheduling import ALAPScheduleAnalysis, ASAPScheduleAnalysis, SetIOLatency from .time_unit_conversion import TimeUnitConversion from .padding import PadDelay, PadDynamicalDecoupling @@ -21,3 +24,4 @@ from . import alignments as instruction_alignments # TODO Deprecated pass. Will be removed after deprecation period. +from .alignments import AlignMeasures diff --git a/qiskit/transpiler/passes/scheduling/alap.py b/qiskit/transpiler/passes/scheduling/alap.py new file mode 100644 index 000000000000..9ee0f4988b4a --- /dev/null +++ b/qiskit/transpiler/passes/scheduling/alap.py @@ -0,0 +1,155 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""ALAP Scheduling.""" + +from qiskit.circuit import Delay, Qubit, Measure +from qiskit.dagcircuit import DAGCircuit +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.utils.deprecation import deprecate_func + +from .base_scheduler import BaseSchedulerTransform + + +class ALAPSchedule(BaseSchedulerTransform): + """ALAP Scheduling pass, which schedules the **stop** time of instructions as late as possible. + + See :class:`~qiskit.transpiler.passes.scheduling.base_scheduler.BaseSchedulerTransform` for the + detailed behavior of the control flow operation, i.e. ``c_if``. + """ + + @deprecate_func( + additional_msg=( + "Instead, use :class:`~.ALAPScheduleAnalysis`, which is an " + "analysis pass that requires a padding pass to later modify the circuit." + ), + since="0.21.0", + pending=True, + ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def run(self, dag): + """Run the ALAPSchedule pass on `dag`. + + Args: + dag (DAGCircuit): DAG to schedule. + + Returns: + DAGCircuit: A scheduled DAG. + + Raises: + TranspilerError: if the circuit is not mapped on physical qubits. + TranspilerError: if conditional bit is added to non-supported instruction. + """ + if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None: + raise TranspilerError("ALAP schedule runs on physical circuits only") + + time_unit = self.property_set["time_unit"] + new_dag = DAGCircuit() + for qreg in dag.qregs.values(): + new_dag.add_qreg(qreg) + for creg in dag.cregs.values(): + new_dag.add_creg(creg) + + idle_before = {q: 0 for q in dag.qubits + dag.clbits} + for node in reversed(list(dag.topological_op_nodes())): + op_duration = self._get_node_duration(node, dag) + + # compute t0, t1: instruction interval, note that + # t0: start time of instruction + # t1: end time of instruction + + # since this is alap scheduling, node is scheduled in reversed topological ordering + # and nodes are packed from the very end of the circuit. + # the physical meaning of t0 and t1 is flipped here. + if isinstance(node.op, self.CONDITIONAL_SUPPORTED): + t0q = max(idle_before[q] for q in node.qargs) + if node.op.condition_bits: + # conditional is bit tricky due to conditional_latency + t0c = max(idle_before[c] for c in node.op.condition_bits) + # Assume following case (t0c > t0q): + # + # |t0q + # Q ░░░░░░░░░░░░░▒▒▒ + # C ░░░░░░░░▒▒▒▒▒▒▒▒ + # |t0c + # + # In this case, there is no actual clbit read before gate. + # + # |t0q' = t0c - conditional_latency + # Q ░░░░░░░░▒▒▒░░▒▒▒ + # C ░░░░░░▒▒▒▒▒▒▒▒▒▒ + # |t1c' = t0c + conditional_latency + # + # rather than naively doing + # + # |t1q' = t0c + duration + # Q ░░░░░▒▒▒░░░░░▒▒▒ + # C ░░▒▒░░░░▒▒▒▒▒▒▒▒ + # |t1c' = t0c + duration + conditional_latency + # + t0 = max(t0q, t0c - op_duration) + t1 = t0 + op_duration + for clbit in node.op.condition_bits: + idle_before[clbit] = t1 + self.conditional_latency + else: + t0 = t0q + t1 = t0 + op_duration + else: + if node.op.condition_bits: + raise TranspilerError( + f"Conditional instruction {node.op.name} is not supported in ALAP scheduler." + ) + + if isinstance(node.op, Measure): + # clbit time is always right (alap) justified + t0 = max(idle_before[bit] for bit in node.qargs + node.cargs) + t1 = t0 + op_duration + # + # |t1 = t0 + duration + # Q ░░░░░▒▒▒▒▒▒▒▒▒▒▒ + # C ░░░░░░░░░▒▒▒▒▒▒▒ + # |t0 + (duration - clbit_write_latency) + # + for clbit in node.cargs: + idle_before[clbit] = t0 + (op_duration - self.clbit_write_latency) + else: + # It happens to be directives such as barrier + t0 = max(idle_before[bit] for bit in node.qargs + node.cargs) + t1 = t0 + op_duration + + for bit in node.qargs: + delta = t0 - idle_before[bit] + if delta > 0 and self._delay_supported(dag.find_bit(bit).index): + new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [], check=False) + idle_before[bit] = t1 + + new_dag.apply_operation_front(node.op, node.qargs, node.cargs, check=False) + + circuit_duration = max(idle_before.values()) + for bit, before in idle_before.items(): + delta = circuit_duration - before + if not (delta > 0 and isinstance(bit, Qubit)): + continue + if self._delay_supported(dag.find_bit(bit).index): + new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [], check=False) + + new_dag.name = dag.name + new_dag.metadata = dag.metadata + new_dag.calibrations = dag.calibrations + + # set circuit duration and unit to indicate it is scheduled + new_dag.duration = circuit_duration + new_dag.unit = time_unit + + return new_dag diff --git a/qiskit/transpiler/passes/scheduling/alignments/__init__.py b/qiskit/transpiler/passes/scheduling/alignments/__init__.py index a25ec01bc1cf..513144937ab5 100644 --- a/qiskit/transpiler/passes/scheduling/alignments/__init__.py +++ b/qiskit/transpiler/passes/scheduling/alignments/__init__.py @@ -78,3 +78,4 @@ from .check_durations import InstructionDurationCheck from .pulse_gate_validation import ValidatePulseGates from .reschedule import ConstrainedReschedule +from .align_measures import AlignMeasures diff --git a/qiskit/transpiler/passes/scheduling/alignments/align_measures.py b/qiskit/transpiler/passes/scheduling/alignments/align_measures.py new file mode 100644 index 000000000000..668d65f6abd5 --- /dev/null +++ b/qiskit/transpiler/passes/scheduling/alignments/align_measures.py @@ -0,0 +1,256 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Align measurement instructions.""" +from __future__ import annotations +import itertools +import warnings +from collections import defaultdict +from collections.abc import Iterable +from typing import Type + +from qiskit.circuit.quantumcircuit import ClbitSpecifier, QubitSpecifier + +from qiskit.circuit.delay import Delay +from qiskit.circuit.measure import Measure +from qiskit.circuit.parameterexpression import ParameterExpression +from qiskit.dagcircuit import DAGCircuit +from qiskit.transpiler.basepasses import TransformationPass +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.utils.deprecation import deprecate_func + + +class AlignMeasures(TransformationPass): + """Measurement alignment. + + This is a control electronics aware optimization pass. + + In many quantum computing architectures gates (instructions) are implemented with + shaped analog stimulus signals. These signals are digitally stored in the + waveform memory of the control electronics and converted into analog voltage signals + by electronic components called digital to analog converters (DAC). + + In a typical hardware implementation of superconducting quantum processors, + a single qubit instruction is implemented by a + microwave signal with the duration of around several tens of ns with a per-sample + time resolution of ~0.1-10ns, as reported by ``backend.configuration().dt``. + In such systems requiring higher DAC bandwidth, control electronics often + defines a `pulse granularity`, in other words a data chunk, to allow the DAC to + perform the signal conversion in parallel to gain the bandwidth. + + Measurement alignment is required if a backend only allows triggering ``measure`` + instructions at a certain multiple value of this pulse granularity. + This value is usually provided by ``backend.configuration().timing_constraints``. + + In Qiskit SDK, the duration of delay can take arbitrary value in units of ``dt``, + thus circuits involving delays may violate the above alignment constraint (i.e. misalignment). + This pass shifts measurement instructions to a new time position to fix the misalignment, + by inserting extra delay right before the measure instructions. + The input of this pass should be scheduled :class:`~qiskit.dagcircuit.DAGCircuit`, + thus one should select one of the scheduling passes + (:class:`~qiskit.transpiler.passes.ALAPSchedule` or + :class:`~qiskit.trasnpiler.passes.ASAPSchedule`) before calling this. + + Examples: + We assume executing the following circuit on a backend with ``alignment=16``. + + .. parsed-literal:: + + ┌───┐┌────────────────┐┌─┐ + q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├ + └───┘└────────────────┘└╥┘ + c: 1/════════════════════════╩═ + 0 + + Note that delay of 100 dt induces a misalignment of 4 dt at the measurement. + This pass appends an extra 12 dt time shift to the input circuit. + + .. parsed-literal:: + + ┌───┐┌────────────────┐┌─┐ + q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├ + └───┘└────────────────┘└╥┘ + c: 1/════════════════════════╩═ + 0 + + This pass always inserts a positive delay before measurements + rather than reducing other delays. + + Notes: + The Backend may allow users to execute circuits violating the alignment constraint. + However, it may return meaningless measurement data mainly due to the phase error. + """ + + @deprecate_func( + additional_msg=( + "Instead, use :class:`~.ConstrainedReschedule`, which performs the same function " + "but also supports aligning to additional timing constraints." + ), + since="0.21.0", + pending=True, + ) + def __init__(self, alignment: int = 1): + """Create new pass. + + Args: + alignment: Integer number representing the minimum time resolution to + trigger measure instruction in units of ``dt``. This value depends on + the control electronics of your quantum processor. + """ + super().__init__() + self.alignment = alignment + + def run(self, dag: DAGCircuit): + """Run the measurement alignment pass on `dag`. + + Args: + dag (DAGCircuit): DAG to be checked. + + Returns: + DAGCircuit: DAG with consistent timing and op nodes annotated with duration. + + Raises: + TranspilerError: If circuit is not scheduled. + """ + time_unit = self.property_set["time_unit"] + + if not _check_alignment_required(dag, self.alignment, Measure): + # return input as-is to avoid unnecessary scheduling. + # because following procedure regenerate new DAGCircuit, + # we should avoid continuing if not necessary from performance viewpoint. + return dag + + # if circuit is not yet scheduled, schedule with ALAP method + if dag.duration is None: + raise TranspilerError( + f"This circuit {dag.name} may involve a delay instruction violating the " + "pulse controller alignment. To adjust instructions to " + "right timing, you should call one of scheduling passes first. " + "This is usually done by calling transpiler with scheduling_method='alap'." + ) + + # the following lines are basically copied from ASAPSchedule pass + # + # * some validations for non-scheduled nodes are dropped, since we assume scheduled input + # * pad_with_delay is called only with non-delay node to avoid consecutive delay + new_dag = dag.copy_empty_like() + + qubit_time_available: dict[QubitSpecifier, int] = defaultdict(int) # to track op start time + qubit_stop_times: dict[QubitSpecifier, int] = defaultdict( + int + ) # to track delay start time for padding + clbit_readable: dict[ClbitSpecifier, int] = defaultdict(int) + clbit_writeable: dict[ClbitSpecifier, int] = defaultdict(int) + + def pad_with_delays(qubits: Iterable[QubitSpecifier], until, unit) -> None: + """Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``.""" + for q in qubits: + if qubit_stop_times[q] < until: + idle_duration = until - qubit_stop_times[q] + new_dag.apply_operation_back(Delay(idle_duration, unit), (q,), check=False) + + for node in dag.topological_op_nodes(): + # choose appropriate clbit available time depending on op + clbit_time_available = ( + clbit_writeable if isinstance(node.op, Measure) else clbit_readable + ) + # correction to change clbit start time to qubit start time + delta = node.op.duration if isinstance(node.op, Measure) else 0 + start_time = max( + itertools.chain( + (qubit_time_available[q] for q in node.qargs), + ( + clbit_time_available[c] - delta + for c in node.cargs + tuple(node.op.condition_bits) + ), + ) + ) + + if isinstance(node.op, Measure): + if start_time % self.alignment != 0: + start_time = ((start_time // self.alignment) + 1) * self.alignment + + if not isinstance(node.op, Delay): # exclude delays for combining consecutive delays + pad_with_delays(node.qargs, until=start_time, unit=time_unit) + new_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False) + + stop_time = start_time + node.op.duration + # update time table + for q in node.qargs: + qubit_time_available[q] = stop_time + if not isinstance(node.op, Delay): + qubit_stop_times[q] = stop_time + for c in node.cargs: # measure + clbit_writeable[c] = clbit_readable[c] = stop_time + for c in node.op.condition_bits: # conditional op + clbit_writeable[c] = max(start_time, clbit_writeable[c]) + + working_qubits = qubit_time_available.keys() + circuit_duration = max(qubit_time_available[q] for q in working_qubits) + pad_with_delays(new_dag.qubits, until=circuit_duration, unit=time_unit) + + new_dag.name = dag.name + new_dag.metadata = dag.metadata + + # set circuit duration and unit to indicate it is scheduled + new_dag.duration = circuit_duration + new_dag.unit = time_unit + + return new_dag + + +def _check_alignment_required( + dag: DAGCircuit, + alignment: int, + instructions: Type | list[Type], +) -> bool: + """Check DAG nodes and return a boolean representing if instruction scheduling is necessary. + + Args: + dag: DAG circuit to check. + alignment: Instruction alignment condition. + instructions: Target instructions. + + Returns: + If instruction scheduling is necessary. + """ + if not isinstance(instructions, list): + instructions = [instructions] + + if alignment == 1: + # disable alignment if arbitrary t0 value can be used + return False + + if all(len(dag.op_nodes(inst)) == 0 for inst in instructions): + # disable alignment if target instruction is not involved + return False + + # check delay durations + for delay_node in dag.op_nodes(Delay): + duration = delay_node.op.duration + if isinstance(duration, ParameterExpression): + # duration is parametrized: + # raise user warning if backend alignment is not 1. + warnings.warn( + f"Parametrized delay with {repr(duration)} is found in circuit {dag.name}. " + f"This backend requires alignment={alignment}. " + "Please make sure all assigned values are multiple values of the alignment.", + UserWarning, + ) + else: + # duration is bound: + # check duration and trigger alignment if it violates constraint + if duration % alignment != 0: + return True + + # disable alignment if all delays are multiple values of the alignment + return False diff --git a/qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py b/qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py index 697d36fa71b6..e4bcdecbb666 100644 --- a/qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py +++ b/qiskit/transpiler/passes/scheduling/alignments/pulse_gate_validation.py @@ -30,7 +30,7 @@ class ValidatePulseGates(AnalysisPass): In Qiskit SDK, we can define the pulse-level implementation of custom quantum gate instructions, as a `pulse gate - `__, + `__, thus user gates should satisfy all waveform memory constraints imposed by the backend. This pass validates all attached calibration entries and raises ``TranspilerError`` to diff --git a/qiskit/transpiler/passes/scheduling/asap.py b/qiskit/transpiler/passes/scheduling/asap.py new file mode 100644 index 000000000000..cebc32af71a8 --- /dev/null +++ b/qiskit/transpiler/passes/scheduling/asap.py @@ -0,0 +1,177 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""ASAP Scheduling.""" + +from qiskit.circuit import Delay, Qubit, Measure +from qiskit.dagcircuit import DAGCircuit +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.utils.deprecation import deprecate_func + +from .base_scheduler import BaseSchedulerTransform + + +class ASAPSchedule(BaseSchedulerTransform): + """ASAP Scheduling pass, which schedules the start time of instructions as early as possible.. + + See :class:`~qiskit.transpiler.passes.scheduling.base_scheduler.BaseSchedulerTransform` for the + detailed behavior of the control flow operation, i.e. ``c_if``. + + .. note:: + + This base class has been superseded by :class:`~.ASAPScheduleAnalysis` and + the new scheduling workflow. It will be deprecated and subsequently + removed in a future release. + """ + + @deprecate_func( + additional_msg=( + "Instead, use :class:`~.ASAPScheduleAnalysis`, which is an " + "analysis pass that requires a padding pass to later modify the circuit." + ), + since="0.21.0", + pending=True, + ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def run(self, dag): + """Run the ASAPSchedule pass on `dag`. + + Args: + dag (DAGCircuit): DAG to schedule. + + Returns: + DAGCircuit: A scheduled DAG. + + Raises: + TranspilerError: if the circuit is not mapped on physical qubits. + TranspilerError: if conditional bit is added to non-supported instruction. + """ + if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None: + raise TranspilerError("ASAP schedule runs on physical circuits only") + + time_unit = self.property_set["time_unit"] + + new_dag = DAGCircuit() + for qreg in dag.qregs.values(): + new_dag.add_qreg(qreg) + for creg in dag.cregs.values(): + new_dag.add_creg(creg) + + idle_after = {q: 0 for q in dag.qubits + dag.clbits} + for node in dag.topological_op_nodes(): + op_duration = self._get_node_duration(node, dag) + + # compute t0, t1: instruction interval, note that + # t0: start time of instruction + # t1: end time of instruction + if isinstance(node.op, self.CONDITIONAL_SUPPORTED): + t0q = max(idle_after[q] for q in node.qargs) + if node.op.condition_bits: + # conditional is bit tricky due to conditional_latency + t0c = max(idle_after[bit] for bit in node.op.condition_bits) + if t0q > t0c: + # This is situation something like below + # + # |t0q + # Q ▒▒▒▒▒▒▒▒▒░░ + # C ▒▒▒░░░░░░░░ + # |t0c + # + # In this case, you can insert readout access before tq0 + # + # |t0q + # Q ▒▒▒▒▒▒▒▒▒▒▒ + # C ▒▒▒░░░▒▒░░░ + # |t0q - conditional_latency + # + t0c = max(t0q - self.conditional_latency, t0c) + t1c = t0c + self.conditional_latency + for bit in node.op.condition_bits: + # Lock clbit until state is read + idle_after[bit] = t1c + # It starts after register read access + t0 = max(t0q, t1c) + else: + t0 = t0q + t1 = t0 + op_duration + else: + if node.op.condition_bits: + raise TranspilerError( + f"Conditional instruction {node.op.name} is not supported in ASAP scheduler." + ) + + if isinstance(node.op, Measure): + # measure instruction handling is bit tricky due to clbit_write_latency + t0q = max(idle_after[q] for q in node.qargs) + t0c = max(idle_after[c] for c in node.cargs) + # Assume following case (t0c > t0q) + # + # |t0q + # Q ▒▒▒▒░░░░░░░░░░░░ + # C ▒▒▒▒▒▒▒▒░░░░░░░░ + # |t0c + # + # In this case, there is no actual clbit access until clbit_write_latency. + # The node t0 can be push backward by this amount. + # + # |t0q' = t0c - clbit_write_latency + # Q ▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒ + # C ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ + # |t0c' = t0c + # + # rather than naively doing + # + # |t0q' = t0c + # Q ▒▒▒▒░░░░▒▒▒▒▒▒▒▒ + # C ▒▒▒▒▒▒▒▒░░░▒▒▒▒▒ + # |t0c' = t0c + clbit_write_latency + # + t0 = max(t0q, t0c - self.clbit_write_latency) + t1 = t0 + op_duration + for clbit in node.cargs: + idle_after[clbit] = t1 + else: + # It happens to be directives such as barrier + t0 = max(idle_after[bit] for bit in node.qargs + node.cargs) + t1 = t0 + op_duration + + # Add delay to qubit wire + for bit in node.qargs: + delta = t0 - idle_after[bit] + if ( + delta > 0 + and isinstance(bit, Qubit) + and self._delay_supported(dag.find_bit(bit).index) + ): + new_dag.apply_operation_back(Delay(delta, time_unit), [bit], []) + idle_after[bit] = t1 + + new_dag.apply_operation_back(node.op, node.qargs, node.cargs) + + circuit_duration = max(idle_after.values()) + for bit, after in idle_after.items(): + delta = circuit_duration - after + if not (delta > 0 and isinstance(bit, Qubit)): + continue + if self._delay_supported(dag.find_bit(bit).index): + new_dag.apply_operation_back(Delay(delta, time_unit), [bit], []) + + new_dag.name = dag.name + new_dag.metadata = dag.metadata + new_dag.calibrations = dag.calibrations + + # set circuit duration and unit to indicate it is scheduled + new_dag.duration = circuit_duration + new_dag.unit = time_unit + return new_dag diff --git a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py new file mode 100644 index 000000000000..cf8477278372 --- /dev/null +++ b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py @@ -0,0 +1,285 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Dynamical Decoupling insertion pass.""" + +import itertools + +import numpy as np +from qiskit.circuit import Gate, Delay, Reset +from qiskit.circuit.library.standard_gates import IGate, UGate, U3Gate +from qiskit.dagcircuit import DAGOpNode, DAGInNode +from qiskit.quantum_info.operators.predicates import matrix_equal +from qiskit.quantum_info.synthesis import OneQubitEulerDecomposer +from qiskit.transpiler.passes.optimization import Optimize1qGates +from qiskit.transpiler.basepasses import TransformationPass +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.utils.deprecation import deprecate_func + + +class DynamicalDecoupling(TransformationPass): + """Dynamical decoupling insertion pass. + + This pass works on a scheduled, physical circuit. It scans the circuit for + idle periods of time (i.e. those containing delay instructions) and inserts + a DD sequence of gates in those spots. These gates amount to the identity, + so do not alter the logical action of the circuit, but have the effect of + mitigating decoherence in those idle periods. + + As a special case, the pass allows a length-1 sequence (e.g. [XGate()]). + In this case the DD insertion happens only when the gate inverse can be + absorbed into a neighboring gate in the circuit (so we would still be + replacing Delay with something that is equivalent to the identity). + This can be used, for instance, as a Hahn echo. + + This pass ensures that the inserted sequence preserves the circuit exactly + (including global phase). + + .. plot:: + :include-source: + + import numpy as np + from qiskit.circuit import QuantumCircuit + from qiskit.circuit.library import XGate + from qiskit.transpiler import PassManager, InstructionDurations + from qiskit.transpiler.passes import ALAPSchedule, DynamicalDecoupling + from qiskit.visualization import timeline_drawer + + # Because the legacy passes do not propagate the scheduling information correctly, it is + # necessary to run a no-op "re-schedule" before the output circuits can be drawn. + def draw(circuit): + from qiskit import transpile + + scheduled = transpile( + circuit, + optimization_level=0, + instruction_durations=InstructionDurations(), + scheduling_method="alap", + ) + return timeline_drawer(scheduled) + + circ = QuantumCircuit(4) + circ.h(0) + circ.cx(0, 1) + circ.cx(1, 2) + circ.cx(2, 3) + circ.measure_all() + durations = InstructionDurations( + [("h", 0, 50), ("cx", [0, 1], 700), ("reset", None, 10), + ("cx", [1, 2], 200), ("cx", [2, 3], 300), + ("x", None, 50), ("measure", None, 1000)] + ) + # balanced X-X sequence on all qubits + dd_sequence = [XGate(), XGate()] + pm = PassManager([ALAPSchedule(durations), + DynamicalDecoupling(durations, dd_sequence)]) + circ_dd = pm.run(circ) + draw(circ_dd) + + # Uhrig sequence on qubit 0 + n = 8 + dd_sequence = [XGate()] * n + def uhrig_pulse_location(k): + return np.sin(np.pi * (k + 1) / (2 * n + 2)) ** 2 + spacing = [] + for k in range(n): + spacing.append(uhrig_pulse_location(k) - sum(spacing)) + spacing.append(1 - sum(spacing)) + pm = PassManager( + [ + ALAPSchedule(durations), + DynamicalDecoupling(durations, dd_sequence, qubits=[0], spacing=spacing), + ] + ) + circ_dd = pm.run(circ) + draw(circ_dd) + """ + + @deprecate_func( + additional_msg=( + "Instead, use :class:`~.PadDynamicalDecoupling`, which performs the same " + "function but requires scheduling and alignment analysis passes to run prior to it." + ), + since="0.21.0", + pending=True, + ) + def __init__( + self, durations, dd_sequence, qubits=None, spacing=None, skip_reset_qubits=True, target=None + ): + """Dynamical decoupling initializer. + + Args: + durations (InstructionDurations): Durations of instructions to be + used in scheduling. + dd_sequence (list[Gate]): sequence of gates to apply in idle spots. + qubits (list[int]): physical qubits on which to apply DD. + If None, all qubits will undergo DD (when possible). + spacing (list[float]): a list of spacings between the DD gates. + The available slack will be divided according to this. + The list length must be one more than the length of dd_sequence, + and the elements must sum to 1. If None, a balanced spacing + will be used [d/2, d, d, ..., d, d, d/2]. + skip_reset_qubits (bool): if True, does not insert DD on idle + periods that immediately follow initialized/reset qubits (as + qubits in the ground state are less susceptile to decoherence). + target (Target): The :class:`~.Target` representing the target backend, if both + ``durations`` and this are specified then this argument will take + precedence and ``durations`` will be ignored. + """ + super().__init__() + self._durations = durations + self._dd_sequence = dd_sequence + self._qubits = qubits + self._spacing = spacing + self._skip_reset_qubits = skip_reset_qubits + self._target = target + if target is not None: + self._durations = target.durations() + for gate in dd_sequence: + if gate.name not in target.operation_names: + raise TranspilerError( + f"{gate.name} in dd_sequence is not supported in the target" + ) + + def run(self, dag): + """Run the DynamicalDecoupling pass on dag. + + Args: + dag (DAGCircuit): a scheduled DAG. + + Returns: + DAGCircuit: equivalent circuit with delays interrupted by DD, + where possible. + + Raises: + TranspilerError: if the circuit is not mapped on physical qubits. + """ + if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None: + raise TranspilerError("DD runs on physical circuits only.") + + if dag.duration is None: + raise TranspilerError("DD runs after circuit is scheduled.") + + num_pulses = len(self._dd_sequence) + sequence_gphase = 0 + if num_pulses != 1: + if num_pulses % 2 != 0: + raise TranspilerError("DD sequence must contain an even number of gates (or 1).") + noop = np.eye(2) + for gate in self._dd_sequence: + noop = noop.dot(gate.to_matrix()) + if not matrix_equal(noop, IGate().to_matrix(), ignore_phase=True): + raise TranspilerError("The DD sequence does not make an identity operation.") + sequence_gphase = np.angle(noop[0][0]) + + if self._qubits is None: + self._qubits = set(range(dag.num_qubits())) + else: + self._qubits = set(self._qubits) + + if self._spacing: + if sum(self._spacing) != 1 or any(a < 0 for a in self._spacing): + raise TranspilerError( + "The spacings must be given in terms of fractions " + "of the slack period and sum to 1." + ) + else: # default to balanced spacing + mid = 1 / num_pulses + end = mid / 2 + self._spacing = [end] + [mid] * (num_pulses - 1) + [end] + + for qarg in list(self._qubits): + for gate in self._dd_sequence: + if not self.__gate_supported(gate, qarg): + self._qubits.discard(qarg) + break + + index_sequence_duration_map = {} + for physical_qubit in self._qubits: + dd_sequence_duration = 0 + for index, gate in enumerate(self._dd_sequence): + gate = gate.to_mutable() + self._dd_sequence[index] = gate + gate.duration = self._durations.get(gate, physical_qubit) + + dd_sequence_duration += gate.duration + index_sequence_duration_map[physical_qubit] = dd_sequence_duration + + new_dag = dag.copy_empty_like() + + for nd in dag.topological_op_nodes(): + if not isinstance(nd.op, Delay): + new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False) + continue + + dag_qubit = nd.qargs[0] + physical_qubit = dag.find_bit(dag_qubit).index + if physical_qubit not in self._qubits: # skip unwanted qubits + new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False) + continue + + pred = next(dag.predecessors(nd)) + succ = next(dag.successors(nd)) + if self._skip_reset_qubits: # discount initial delays + if isinstance(pred, DAGInNode) or isinstance(pred.op, Reset): + new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False) + continue + + dd_sequence_duration = index_sequence_duration_map[physical_qubit] + slack = nd.op.duration - dd_sequence_duration + if slack <= 0: # dd doesn't fit + new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False) + continue + + if num_pulses == 1: # special case of using a single gate for DD + u_inv = self._dd_sequence[0].inverse().to_matrix() + theta, phi, lam, phase = OneQubitEulerDecomposer().angles_and_phase(u_inv) + # absorb the inverse into the successor (from left in circuit) + if isinstance(succ, DAGOpNode) and isinstance(succ.op, (UGate, U3Gate)): + theta_r, phi_r, lam_r = succ.op.params + succ.op.params = Optimize1qGates.compose_u3( + theta_r, phi_r, lam_r, theta, phi, lam + ) + sequence_gphase += phase + # absorb the inverse into the predecessor (from right in circuit) + elif isinstance(pred, DAGOpNode) and isinstance(pred.op, (UGate, U3Gate)): + theta_l, phi_l, lam_l = pred.op.params + pred.op.params = Optimize1qGates.compose_u3( + theta, phi, lam, theta_l, phi_l, lam_l + ) + sequence_gphase += phase + # don't do anything if there's no single-qubit gate to absorb the inverse + else: + new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False) + continue + + # insert the actual DD sequence + taus = [int(slack * a) for a in self._spacing] + unused_slack = slack - sum(taus) # unused, due to rounding to int multiples of dt + middle_index = int((len(taus) - 1) / 2) # arbitrary: redistribute to middle + taus[middle_index] += unused_slack # now we add up to original delay duration + + for tau, gate in itertools.zip_longest(taus, self._dd_sequence): + if tau > 0: + new_dag.apply_operation_back(Delay(tau), [dag_qubit], check=False) + if gate is not None: + new_dag.apply_operation_back(gate, [dag_qubit], check=False) + + new_dag.global_phase = new_dag.global_phase + sequence_gphase + + return new_dag + + def __gate_supported(self, gate: Gate, qarg: int) -> bool: + """A gate is supported on the qubit (qarg) or not.""" + if self._target is None or self._target.instruction_supported(gate.name, qargs=(qarg,)): + return True + return False diff --git a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py index 2d4114f3cfc7..97946c55df8b 100644 --- a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +++ b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py @@ -42,7 +42,7 @@ class PadDynamicalDecoupling(BasePadding): so do not alter the logical action of the circuit, but have the effect of mitigating decoherence in those idle periods. - As a special case, the pass allows a length-1 sequence (e.g. [XGate()]). + As a special case, the pass allows a length-1 sequence (e.g. ``[XGate()]``). In this case the DD insertion happens only when the gate inverse can be absorbed into a neighboring gate in the circuit (so we would still be replacing Delay with something that is equivalent to the identity). @@ -394,8 +394,7 @@ def _constrained_length(values): gate_length = self._dd_sequence_lengths[qubit][dd_ind] self._apply_scheduled_op(dag, idle_after, gate, qubit) idle_after += gate_length - - dag.global_phase = self._mod_2pi(dag.global_phase + sequence_gphase) + dag.global_phase = dag.global_phase + sequence_gphase @staticmethod def _resolve_params(gate: Gate) -> tuple: @@ -407,11 +406,3 @@ def _resolve_params(gate: Gate) -> tuple: else: params.append(p) return tuple(params) - - @staticmethod - def _mod_2pi(angle: float, atol: float = 0): - """Wrap angle into interval [-π,π). If within atol of the endpoint, clamp to -π""" - wrapped = (angle + np.pi) % (2 * np.pi) - np.pi - if abs(wrapped - np.pi) < atol: - wrapped = -np.pi - return wrapped diff --git a/qiskit/transpiler/passes/scheduling/padding/pad_delay.py b/qiskit/transpiler/passes/scheduling/padding/pad_delay.py index f1517900874f..886bb6a37974 100644 --- a/qiskit/transpiler/passes/scheduling/padding/pad_delay.py +++ b/qiskit/transpiler/passes/scheduling/padding/pad_delay.py @@ -57,7 +57,7 @@ def __init__(self, fill_very_end: bool = True, target: Target = None): Args: fill_very_end: Set ``True`` to fill the end of circuit with delay. target: The :class:`~.Target` representing the target backend. - If it supplied and it does not support delay instruction on a qubit, + If it is supplied and does not support delay instruction on a qubit, padding passes do not pad any idle time of the qubit. """ super().__init__(target=target) diff --git a/qiskit/transpiler/passes/scheduling/scheduling/asap.py b/qiskit/transpiler/passes/scheduling/scheduling/asap.py index ce0e1d1948a0..fa07ae0c5f61 100644 --- a/qiskit/transpiler/passes/scheduling/scheduling/asap.py +++ b/qiskit/transpiler/passes/scheduling/scheduling/asap.py @@ -18,7 +18,7 @@ class ASAPScheduleAnalysis(BaseScheduler): - """ASAP Scheduling pass, which schedules the start time of instructions as early as possible.. + """ASAP Scheduling pass, which schedules the start time of instructions as early as possible. See the :ref:`scheduling_stage` section in the :mod:`qiskit.transpiler` module documentation for the detailed behavior of the control flow diff --git a/qiskit/transpiler/passes/scheduling/scheduling/set_io_latency.py b/qiskit/transpiler/passes/scheduling/scheduling/set_io_latency.py index 1116123cfa51..3b0ed1692f07 100644 --- a/qiskit/transpiler/passes/scheduling/scheduling/set_io_latency.py +++ b/qiskit/transpiler/passes/scheduling/scheduling/set_io_latency.py @@ -19,7 +19,7 @@ class SetIOLatency(AnalysisPass): """Set IOLatency information to the input circuit. The ``clbit_write_latency`` and ``conditional_latency`` are added to - the property set of pass manager. These information can be shared among the passes + the property set of pass manager. This information can be shared among the passes that perform scheduling on instructions acting on classical registers. Once these latencies are added to the property set, this information diff --git a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py index c75e22f285b8..d53c3fc4ef6a 100644 --- a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py +++ b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py @@ -25,15 +25,15 @@ class TimeUnitConversion(TransformationPass): """Choose a time unit to be used in the following time-aware passes, and make all circuit time units consistent with that. - This pass will add a .duration metadata to each op whose duration is known, + This pass will add a :attr:`.Instruction.duration` metadata to each op whose duration is known which will be used by subsequent scheduling passes for scheduling. - If dt (dt in seconds) is known to transpiler, the unit 'dt' is chosen. Otherwise, + If ``dt`` (in seconds) is known to transpiler, the unit ``'dt'`` is chosen. Otherwise, the unit to be selected depends on what units are used in delays and instruction durations: - * 's': if they are all in SI units. - * 'dt': if they are all in the unit 'dt'. - * raise error: if they are a mix of SI units and 'dt'. + * ``'s'``: if they are all in SI units. + * ``'dt'``: if they are all in the unit ``'dt'``. + * raise error: if they are a mix of SI units and ``'dt'``. """ def __init__(self, inst_durations: InstructionDurations = None, target: Target = None): @@ -42,7 +42,7 @@ def __init__(self, inst_durations: InstructionDurations = None, target: Target = Args: inst_durations (InstructionDurations): A dictionary of durations of instructions. target: The :class:`~.Target` representing the target backend, if both - ``inst_durations`` and this are specified then this argument will take + ``inst_durations`` and ``target`` are specified then this argument will take precedence and ``inst_durations`` will be ignored. diff --git a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py index 22bcf5abb127..2843ef077378 100644 --- a/qiskit/transpiler/passes/synthesis/high_level_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/high_level_synthesis.py @@ -74,8 +74,8 @@ class HLSConfig: hls_config = HLSConfig(permutation=[(ACGSynthesisPermutation(), {})]) hls_config = HLSConfig(permutation=[ACGSynthesisPermutation()]) - The names of the synthesis algorithms should be declared in ``entry_points`` for - ``qiskit.synthesis`` in ``setup.py``, in the form + The names of the synthesis algorithms should be declared in ``entry-points`` table for + ``qiskit.synthesis`` in ``pyproject.toml``, in the form .. The standard higher-level-objects are recommended to have a synthesis method diff --git a/qiskit/transpiler/passes/synthesis/linear_functions_synthesis.py b/qiskit/transpiler/passes/synthesis/linear_functions_synthesis.py index 4d31be6bff12..eb773a985ddb 100644 --- a/qiskit/transpiler/passes/synthesis/linear_functions_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/linear_functions_synthesis.py @@ -30,6 +30,7 @@ class LinearFunctionsSynthesis(HighLevelSynthesis): @deprecate_func( additional_msg="Instead, use :class:`~.HighLevelSynthesis`.", since="0.23.0", + package_name="qiskit-terra", ) def __init__(self): # This config synthesizes only linear functions using the "default" method. diff --git a/qiskit/transpiler/passes/synthesis/plugin.py b/qiskit/transpiler/passes/synthesis/plugin.py index 2aaf96c59eab..4154c0186430 100644 --- a/qiskit/transpiler/passes/synthesis/plugin.py +++ b/qiskit/transpiler/passes/synthesis/plugin.py @@ -130,18 +130,15 @@ def run(self, unitary, **options): The second step is to expose the :class:`~qiskit.transpiler.passes.synthesis.plugin.UnitarySynthesisPlugin` as a setuptools entry point in the package metadata. This is done by simply adding -an ``entry_points`` entry to the ``setuptools.setup`` call in the ``setup.py`` -for the plugin package with the necessary entry points under the -``qiskit.unitary_synthesis`` namespace. For example:: - - entry_points = { - 'qiskit.unitary_synthesis': [ - 'special = qiskit_plugin_pkg.module.plugin:SpecialUnitarySynthesis', - ] - }, - -(note that the entry point ``name = path`` is a single string not a Python -expression). There isn't a limit to the number of plugins a single package can +an ``entry-points`` table in ``pyproject.toml`` for the plugin package with the necessary entry +points under the ``qiskit.unitary_synthesis`` namespace. For example: + +.. code-block:: toml + + [project.entry-points."qiskit.unitary-synthesis"] + "special" = "qiskit_plugin_pkg.module.plugin:SpecialUnitarySynthesis" + +There isn't a limit to the number of plugins a single package can include as long as each plugin has a unique name. So a single package can expose multiple plugins if necessary. The name ``default`` is used by Qiskit itself and can't be used in a plugin. @@ -218,19 +215,15 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, ** The second step is to expose the :class:`~qiskit.transpiler.passes.synthesis.plugin.HighLevelSynthesisPlugin` as a setuptools entry point in the package metadata. This is done by adding -an ``entry_points`` entry to the ``setuptools.setup`` call in the ``setup.py`` -for the plugin package with the necessary entry points under the -``qiskit.synthesis`` namespace. For example:: - - entry_points = { - 'qiskit.synthesis': [ - 'clifford.special = qiskit_plugin_pkg.module.plugin:SpecialSynthesisClifford', - ] - }, - -(note that the entry point ``name = path`` is a single string not a Python -expression). The ``name`` consists of two parts separated by dot ".": the -name of the +an ``entry-points`` table in ``pyproject.toml`` for the plugin package with the necessary entry +points under the ``qiskit.unitary_synthesis`` namespace. For example: + +.. code-block:: toml + + [project.entry-points."qiskit.synthesis"] + "clifford.special" = "qiskit_plugin_pkg.module.plugin:SpecialSynthesisClifford" + +The ``name`` consists of two parts separated by dot ".": the name of the type of :class:`~qiskit.circuit.Operation` to which the synthesis plugin applies (``clifford``), and the name of the plugin (``special``). There isn't a limit to the number of plugins a single package can diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py index 145cebd5c366..f7f30fc284c7 100644 --- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py @@ -331,6 +331,11 @@ def __init__( target: The optional :class:`~.Target` for the target device the pass is compiling for. If specified this will supersede the values set for ``basis_gates``, ``coupling_map``, and ``backend_props``. + + Raises: + TranspilerError: if ``method`` was specified but is not found in the + installed plugins list. The list of installed plugins can be queried with + :func:`~qiskit.transpiler.passes.synthesis.plugin.unitary_synthesis_plugin_names` """ super().__init__() self._basis_gates = set(basis_gates or ()) @@ -358,6 +363,9 @@ def __init__( self._synth_gates = set(self._synth_gates) - self._basis_gates + if self.method != "default" and self.method not in self.plugins.ext_plugins: + raise TranspilerError(f"Specified method '{self.method}' not found in plugin list") + def run(self, dag: DAGCircuit) -> DAGCircuit: """Run the UnitarySynthesis pass on ``dag``. @@ -366,15 +374,7 @@ def run(self, dag: DAGCircuit) -> DAGCircuit: Returns: Output dag with UnitaryGates synthesized to target basis. - - Raises: - TranspilerError: if ``method`` was specified for the class and is not - found in the installed plugins list. The list of installed - plugins can be queried with - :func:`~qiskit.transpiler.passes.synthesis.plugin.unitary_synthesis_plugin_names` """ - if self.method != "default" and self.method not in self.plugins.ext_plugins: - raise TranspilerError("Specified method: %s not found in plugin list" % self.method) # If there aren't any gates to synthesize in the circuit we can skip all the iteration # and just return. diff --git a/qiskit/transpiler/passes/utils/__init__.py b/qiskit/transpiler/passes/utils/__init__.py index c5d605f7d732..7227409a213c 100644 --- a/qiskit/transpiler/passes/utils/__init__.py +++ b/qiskit/transpiler/passes/utils/__init__.py @@ -27,6 +27,7 @@ from .convert_conditions_to_if_ops import ConvertConditionsToIfOps from .unroll_forloops import UnrollForLoops from .minimum_point import MinimumPoint +from .filter_op_nodes import FilterOpNodes # Utility functions from . import control_flow diff --git a/qiskit/transpiler/passes/utils/barrier_before_final_measurements.py b/qiskit/transpiler/passes/utils/barrier_before_final_measurements.py index 2222ef064010..4633cc57af54 100644 --- a/qiskit/transpiler/passes/utils/barrier_before_final_measurements.py +++ b/qiskit/transpiler/passes/utils/barrier_before_final_measurements.py @@ -27,6 +27,10 @@ class BarrierBeforeFinalMeasurements(TransformationPass): other measurements or barriers.) """ + def __init__(self, label=None): + super().__init__() + self.label = label + def run(self, dag): """Run the BarrierBeforeFinalMeasurements pass on `dag`.""" # Collect DAG nodes which are followed only by barriers or other measures. @@ -64,7 +68,7 @@ def run(self, dag): final_qubits = dag.qubits barrier_layer.apply_operation_back( - Barrier(len(final_qubits)), final_qubits, (), check=False + Barrier(len(final_qubits), label=self.label), final_qubits, (), check=False ) # Preserve order of final ops collected earlier from the original DAG. @@ -83,6 +87,9 @@ def run(self, dag): dag.compose(barrier_layer) - # Merge the new barrier into any other barriers - adjacent_pass = MergeAdjacentBarriers() - return adjacent_pass.run(dag) + if self.label is None: + # Merge the new barrier into any other barriers + adjacent_pass = MergeAdjacentBarriers() + return adjacent_pass.run(dag) + else: + return dag diff --git a/qiskit/transpiler/passes/utils/check_map.py b/qiskit/transpiler/passes/utils/check_map.py index d56a709253c9..61ddc71d131e 100644 --- a/qiskit/transpiler/passes/utils/check_map.py +++ b/qiskit/transpiler/passes/utils/check_map.py @@ -21,7 +21,7 @@ class CheckMap(AnalysisPass): """Check if a DAG circuit is already mapped to a coupling map. - Check if a DAGCircuit is mapped to `coupling_map` by checking that all + Check if a DAGCircuit is mapped to ``coupling_map`` by checking that all 2-qubit interactions are laid out to be on adjacent qubits in the global coupling map of the device, setting the property set field (either specified with ``property_set_field`` or the default ``is_swap_mapped``) to ``True`` or ``False`` accordingly. Note this does not diff --git a/qiskit/transpiler/passes/utils/dag_fixed_point.py b/qiskit/transpiler/passes/utils/dag_fixed_point.py index 2f9f486545b5..60da251b3099 100644 --- a/qiskit/transpiler/passes/utils/dag_fixed_point.py +++ b/qiskit/transpiler/passes/utils/dag_fixed_point.py @@ -21,7 +21,7 @@ class DAGFixedPoint(AnalysisPass): """Check if the DAG has reached a fixed point. A dummy analysis pass that checks if the DAG a fixed point (the DAG is not - modified anymore). The results is saved in + modified anymore). The result is saved in ``property_set['dag_fixed_point']`` as a boolean. """ diff --git a/qiskit/transpiler/passes/utils/error.py b/qiskit/transpiler/passes/utils/error.py index 445b1d21d565..f2659ec052ff 100644 --- a/qiskit/transpiler/passes/utils/error.py +++ b/qiskit/transpiler/passes/utils/error.py @@ -31,9 +31,9 @@ def __init__(self, msg=None, action="raise"): will be used. This can be either a raw string, or a callback function that accepts the current ``property_set`` and returns the desired message. action (str): the action to perform. Default: 'raise'. The options are: - * 'raise': Raises a `TranspilerError` exception with msg - * 'warn': Raises a non-fatal warning with msg - * 'log': logs in `logging.getLogger(__name__)` + * ``'raise'``: Raises a ``TranspilerError`` exception with msg + * ``'warn'``: Raises a non-fatal warning with msg + * ``'log'``: logs in ``logging.getLogger(__name__)`` Raises: TranspilerError: if action is not valid. diff --git a/qiskit/transpiler/passes/utils/filter_op_nodes.py b/qiskit/transpiler/passes/utils/filter_op_nodes.py new file mode 100644 index 000000000000..344d2280e3f4 --- /dev/null +++ b/qiskit/transpiler/passes/utils/filter_op_nodes.py @@ -0,0 +1,65 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Filter ops from a circuit""" + +from typing import Callable + +from qiskit.dagcircuit import DAGCircuit, DAGOpNode +from qiskit.transpiler.basepasses import TransformationPass +from qiskit.transpiler.passes.utils import control_flow + + +class FilterOpNodes(TransformationPass): + """Remove all operations that match a filter function + + This transformation pass is used to remove any operations that matches a + the provided filter function. + + Args: + predicate: A given callable that will be passed the :class:`.DAGOpNode` + for each node in the :class:`.DAGCircuit`. If the callable returns + ``True`` the :class:`.DAGOpNode` is retained in the circuit and if it + returns ``False`` it is removed from the circuit. + + Example: + + Filter out operations that are labelled ``"foo"`` + + .. plot:: + :include-source: + + from qiskit import QuantumCircuit + from qiskit.transpiler.passes import FilterOpNodes + + circuit = QuantumCircuit(1) + circuit.x(0, label='foo') + circuit.barrier() + circuit.h(0) + + circuit = FilterOpNodes( + lambda node: getattr(node.op, "label") != "foo" + )(circuit) + circuit.draw('mpl') + """ + + def __init__(self, predicate: Callable[[DAGOpNode], bool]): + super().__init__() + self.predicate = predicate + + @control_flow.trivial_recurse + def run(self, dag: DAGCircuit) -> DAGCircuit: + """Run the RemoveBarriers pass on `dag`.""" + for node in dag.op_nodes(): + if not self.predicate(node): + dag.remove_op_node(node) + return dag diff --git a/qiskit/transpiler/passes/utils/fixed_point.py b/qiskit/transpiler/passes/utils/fixed_point.py index 34fa1faabe3b..fbef9d0a85ef 100644 --- a/qiskit/transpiler/passes/utils/fixed_point.py +++ b/qiskit/transpiler/passes/utils/fixed_point.py @@ -21,7 +21,7 @@ class FixedPoint(AnalysisPass): """Check if a property reached a fixed point. A dummy analysis pass that checks if a property reached a fixed point. - The results is saved in ``property_set['_fixed_point']`` + The result is saved in ``property_set['_fixed_point']`` as a boolean. """ diff --git a/qiskit/transpiler/passes/utils/minimum_point.py b/qiskit/transpiler/passes/utils/minimum_point.py index 8ccfa4a7f951..1dcef0db2181 100644 --- a/qiskit/transpiler/passes/utils/minimum_point.py +++ b/qiskit/transpiler/passes/utils/minimum_point.py @@ -44,7 +44,7 @@ class MinimumPoint(TransformationPass): Fields used by this pass in the property set are (all relative to the ``prefix`` argument): - * ``{prefix}_minimum_point_state`` - Used to track the state of the minimpoint search + * ``{prefix}_minimum_point_state`` - Used to track the state of the minimum point search * ``{prefix}_minimum_point`` - This value gets set to ``True`` when either a fixed point is reached over the ``backtrack_depth`` executions, or ``backtrack_depth`` was exceeded and an earlier minimum is restored. diff --git a/qiskit/transpiler/passes/utils/remove_final_measurements.py b/qiskit/transpiler/passes/utils/remove_final_measurements.py index 3c60adeabbba..c3179e204cd4 100644 --- a/qiskit/transpiler/passes/utils/remove_final_measurements.py +++ b/qiskit/transpiler/passes/utils/remove_final_measurements.py @@ -26,7 +26,7 @@ class RemoveFinalMeasurements(TransformationPass): Classical registers are removed iff they reference at least one bit that has become unused by the circuit as a result of the operation, and all - of their other bits are also unused. Seperately, classical bits are removed + of their other bits are also unused. Separately, classical bits are removed iff they have become unused by the circuit as a result of the operation, or they appear in a removed classical register, but do not appear in a classical register that will remain. diff --git a/qiskit/transpiler/passes/utils/unroll_forloops.py b/qiskit/transpiler/passes/utils/unroll_forloops.py index 8cde3874adcb..b8ebe853f1e0 100644 --- a/qiskit/transpiler/passes/utils/unroll_forloops.py +++ b/qiskit/transpiler/passes/utils/unroll_forloops.py @@ -22,8 +22,8 @@ class UnrollForLoops(TransformationPass): """``UnrollForLoops`` transpilation pass unrolls for-loops when possible.""" def __init__(self, max_target_depth=-1): - """Things like `for x in {0, 3, 4} {rx(x) qr[1];}` will turn into - `rx(0) qr[1]; rx(3) qr[1]; rx(4) qr[1];`. + """Things like ``for x in {0, 3, 4} {rx(x) qr[1];}`` will turn into + ``rx(0) qr[1]; rx(3) qr[1]; rx(4) qr[1];``. .. note:: The ``UnrollForLoops`` unrolls only one level of block depth. No inner loop will @@ -38,7 +38,7 @@ def __init__(self, max_target_depth=-1): @control_flow.trivial_recurse def run(self, dag): - """Run the UnrollForLoops pass on `dag`. + """Run the UnrollForLoops pass on ``dag``. Args: dag (DAGCircuit): the directed acyclic graph to run on. diff --git a/qiskit/transpiler/passmanager.py b/qiskit/transpiler/passmanager.py index 93eee91e540a..cf7c44d69a7d 100644 --- a/qiskit/transpiler/passmanager.py +++ b/qiskit/transpiler/passmanager.py @@ -65,7 +65,7 @@ def _passmanager_frontend( input_program: QuantumCircuit, **kwargs, ) -> DAGCircuit: - return circuit_to_dag(input_program, copy_operations=False) + return circuit_to_dag(input_program, copy_operations=True) def _passmanager_backend( self, @@ -73,7 +73,7 @@ def _passmanager_backend( in_program: QuantumCircuit, **kwargs, ) -> QuantumCircuit: - out_program = dag_to_circuit(passmanager_ir) + out_program = dag_to_circuit(passmanager_ir, copy_operations=False) out_name = kwargs.get("output_name", None) if out_name is not None: @@ -107,6 +107,7 @@ def _passmanager_backend( since="0.25", additional_msg="'max_iteration' can be set in the constructor.", pending=True, + package_name="qiskit-terra", ) def append( self, @@ -163,6 +164,7 @@ def append( since="0.25", additional_msg="'max_iteration' can be set in the constructor.", pending=True, + package_name="qiskit-terra", ) def replace( self, @@ -336,7 +338,7 @@ def passes(self) -> list[dict[str, BasePass]]: class StagedPassManager(PassManager): - """A Pass manager pipeline built up of individual stages + """A pass manager pipeline built from individual stages. This class enables building a compilation pipeline out of fixed stages. Each ``StagedPassManager`` defines a list of stages which are executed in @@ -347,21 +349,23 @@ class StagedPassManager(PassManager): pass manager you are not able to modify the individual passes and are only able to modify stages. - By default instances of ``StagedPassManager`` define a typical full compilation + By default, instances of ``StagedPassManager`` define a typical full compilation pipeline from an abstract virtual circuit to one that is optimized and capable of running on the specified backend. The default pre-defined stages are: - #. ``init`` - any initial passes that are run before we start embedding the circuit to the backend - #. ``layout`` - This stage runs layout and maps the virtual qubits in the - circuit to the physical qubits on a backend - #. ``routing`` - This stage runs after a layout has been run and will insert any - necessary gates to move the qubit states around until it can be run on - backend's coupling map. - #. ``translation`` - Perform the basis gate translation, in other words translate the gates - in the circuit to the target backend's basis set - #. ``optimization`` - The main optimization loop, this will typically run in a loop trying to - optimize the circuit until a condition (such as fixed depth) is reached. - #. ``scheduling`` - Any hardware aware scheduling passes + #. ``init`` - Initial passes to run before embedding the circuit to the backend. + #. ``layout`` - Maps the virtual qubits in the circuit to the physical qubits on + the backend. + #. ``routing`` - Inserts gates as needed to move the qubit states around until + the circuit can be run with the chosen layout on the backend's coupling map. + #. ``translation`` - Translates the gates in the circuit to the target backend's + basis gate set. + #. ``optimization`` - Optimizes the circuit to reduce the cost of executing it. + These passes will typically run in a loop until a convergence criteria is met. + For example, the convergence criteria might be that the circuit depth does not + decrease in successive iterations. + #. ``scheduling`` - Hardware-aware passes that schedule the operations in the + circuit. .. note:: diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index 3dc89850e37a..9bdcab38a043 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -39,7 +39,7 @@ CommutativeCancellation, Collect2qBlocks, ConsolidateBlocks, - CXCancellation, + InverseCancellation, ) from qiskit.transpiler.passes import Depth, Size, FixedPoint, MinimumPoint from qiskit.transpiler.passes.utils.gates_basis import GatesInBasis @@ -47,13 +47,30 @@ from qiskit.passmanager.flow_controllers import ConditionalController from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason +from qiskit.circuit.library.standard_gates import ( + CXGate, + ECRGate, + CZGate, + XGate, + YGate, + ZGate, + TGate, + TdgGate, + SwapGate, + SGate, + SdgGate, + HGate, + CYGate, + SXGate, + SXdgGate, +) class DefaultInitPassManager(PassManagerStagePlugin): """Plugin class for default init stage.""" def pass_manager(self, pass_manager_config, optimization_level=None) -> PassManager: - if optimization_level in {1, 2, 0}: + if optimization_level == 0: init = None if ( pass_manager_config.initial_layout @@ -71,6 +88,43 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana pass_manager_config.unitary_synthesis_plugin_config, pass_manager_config.hls_config, ) + elif optimization_level in {1, 2}: + init = PassManager() + if ( + pass_manager_config.initial_layout + or pass_manager_config.coupling_map + or ( + pass_manager_config.target is not None + and pass_manager_config.target.build_coupling_map() is not None + ) + ): + init += common.generate_unroll_3q( + pass_manager_config.target, + pass_manager_config.basis_gates, + pass_manager_config.approximation_degree, + pass_manager_config.unitary_synthesis_method, + pass_manager_config.unitary_synthesis_plugin_config, + pass_manager_config.hls_config, + ) + init.append( + InverseCancellation( + [ + CXGate(), + ECRGate(), + CZGate(), + CYGate(), + XGate(), + YGate(), + ZGate(), + HGate(), + SwapGate(), + (TGate(), TdgGate()), + (SGate(), SdgGate()), + (SXGate(), SXdgGate()), + ] + ) + ) + elif optimization_level == 3: init = common.generate_unroll_3q( pass_manager_config.target, @@ -82,6 +136,25 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana ) init.append(OptimizeSwapBeforeMeasure()) init.append(RemoveDiagonalGatesBeforeMeasure()) + init.append( + InverseCancellation( + [ + CXGate(), + ECRGate(), + CZGate(), + CYGate(), + XGate(), + YGate(), + ZGate(), + HGate(), + SwapGate(), + (TGate(), TdgGate()), + (SGate(), SdgGate()), + (SXGate(), SXdgGate()), + ] + ) + ) + else: return TranspilerError(f"Invalid optimization level {optimization_level}") return init @@ -468,7 +541,22 @@ def _opt_control(property_set): Optimize1qGatesDecomposition( basis=pass_manager_config.basis_gates, target=pass_manager_config.target ), - CXCancellation(), + InverseCancellation( + [ + CXGate(), + ECRGate(), + CZGate(), + CYGate(), + XGate(), + YGate(), + ZGate(), + HGate(), + SwapGate(), + (TGate(), TdgGate()), + (SGate(), SdgGate()), + (SXGate(), SXdgGate()), + ] + ), ] elif optimization_level == 2: # Steps for optimization level 2 @@ -652,7 +740,13 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [BarrierBeforeFinalMeasurements(), choose_layout_2], condition=_vf2_match_not_found + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_2, + ], + condition=_vf2_match_not_found, ) elif optimization_level == 2: choose_layout_0 = VF2Layout( @@ -674,7 +768,13 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [BarrierBeforeFinalMeasurements(), choose_layout_1], condition=_vf2_match_not_found + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_1, + ], + condition=_vf2_match_not_found, ) elif optimization_level == 3: choose_layout_0 = VF2Layout( @@ -696,7 +796,13 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [BarrierBeforeFinalMeasurements(), choose_layout_1], condition=_vf2_match_not_found + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_1, + ], + condition=_vf2_match_not_found, ) else: raise TranspilerError(f"Invalid optimization level: {optimization_level}") @@ -850,7 +956,13 @@ def _swap_mapped(property_set): else: raise TranspilerError(f"Invalid optimization level: {optimization_level}") layout.append( - [BarrierBeforeFinalMeasurements(), layout_pass], condition=_choose_layout_condition + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + layout_pass, + ], + condition=_choose_layout_condition, ) embed = common.generate_embed_passmanager(coupling_map) layout.append( diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index e9bf85283fe3..c5402855a2ba 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -41,6 +41,7 @@ from qiskit.transpiler.passes import EnlargeWithAncilla from qiskit.transpiler.passes import ApplyLayout from qiskit.transpiler.passes import RemoveResetInZeroState +from qiskit.transpiler.passes import FilterOpNodes from qiskit.transpiler.passes import ValidatePulseGates from qiskit.transpiler.passes import PadDelay from qiskit.transpiler.passes import InstructionDurationCheck @@ -328,7 +329,15 @@ def _swap_condition(property_set): return not property_set["routing_not_needed"] if use_barrier_before_measurement: - routing.append([BarrierBeforeFinalMeasurements(), routing_pass], condition=_swap_condition) + routing.append( + [ + BarrierBeforeFinalMeasurements( + label="qiskit.transpiler.internal.routing.protection.barrier" + ), + routing_pass, + ], + condition=_swap_condition, + ) else: routing.append([routing_pass], condition=_swap_condition) @@ -348,6 +357,14 @@ def _swap_condition(property_set): ) routing.append(ApplyLayout(), condition=_apply_post_layout_condition) + def filter_fn(node): + return ( + getattr(node.op, "label", None) + != "qiskit.transpiler.internal.routing.protection.barrier" + ) + + routing.append([FilterOpNodes(filter_fn)]) + return routing @@ -581,6 +598,7 @@ def _require_alignment(property_set): @deprecate_func( additional_msg="Instead, use :func:`~qiskit.transpiler.preset_passmanagers.common.get_vf2_limits`.", since="0.25.0", + package_name="qiskit-terra", ) def get_vf2_call_limit( optimization_level: int, diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index e0fabffc9d85..e80bfd92078b 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -134,23 +134,19 @@ def pass_manager(self, pass_manager_config, optimization_level): The second step is to expose the :class:`~.PassManagerStagePlugin` subclass as a setuptools entry point in the package metadata. This can be done -by simply adding an ``entry_points`` entry to the ``setuptools.setup`` call in -the ``setup.py`` or the plugin package with the necessary entry points under the -appropriate namespace for the stage your plugin is for. You can see the list -of stages, entry points, and expectations from the stage in :ref:`stage_table`. -For example, continuing from the example plugin above:: - - entry_points = { - 'qiskit.transpiler.layout': [ - 'vf2 = qiskit_plugin_pkg.module.plugin:VF2LayoutPlugin', - ] - }, - -Note that the entry point ``name = path`` is a single string not a Python -expression. There isn't a limit to the number of plugins a single package can -include as long as each plugin has a unique name. So a single package can -expose multiple plugins if necessary. Refer to :ref:`stage_table` for a list -of reserved names for each stage. +an ``entry-points`` table in ``pyproject.toml`` for the plugin package with the necessary entry +points under the appropriate namespace for the stage your plugin is for. You can see the list of +stages, entry points, and expectations from the stage in :ref:`stage_table`. For example, +continuing from the example plugin above:: + +.. code-block:: toml + + [project.entry-points."qiskit.transpiler.layout"] + "vf2" = "qiskit_plugin_pkg.module.plugin:VF2LayoutPlugin" + +There isn't a limit to the number of plugins a single package can include as long as each plugin has +a unique name. So a single package can expose multiple plugins if necessary. Refer to +:ref:`stage_table` for a list of reserved names for each stage. Plugin API ========== diff --git a/qiskit/transpiler/synthesis/aqc/aqc.py b/qiskit/transpiler/synthesis/aqc/aqc.py index e90fd5612f97..4ced39a7e4ac 100644 --- a/qiskit/transpiler/synthesis/aqc/aqc.py +++ b/qiskit/transpiler/synthesis/aqc/aqc.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -10,30 +10,82 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """A generic implementation of Approximate Quantum Compiler.""" -from typing import Optional +from __future__ import annotations + +from functools import partial + +from collections.abc import Callable +from typing import Protocol import numpy as np +from scipy.optimize import OptimizeResult, minimize -from qiskit.algorithms.optimizers import L_BFGS_B, Optimizer from qiskit.quantum_info import Operator + from .approximate import ApproximateCircuit, ApproximatingObjective +class Minimizer(Protocol): + """Callable Protocol for minimizer. + + This interface is based on `SciPy's optimize module + `__. + + This protocol defines a callable taking the following parameters: + + fun + The objective function to minimize. + x0 + The initial point for the optimization. + jac + The gradient of the objective function. + bounds + Parameters bounds for the optimization. Note that these might not be supported + by all optimizers. + + and which returns a SciPy minimization result object. + """ + + def __call__( + self, + fun: Callable[[np.ndarray], float], + x0: np.ndarray, # pylint: disable=invalid-name + jac: Callable[[np.ndarray], np.ndarray] | None = None, + bounds: list[tuple[float, float]] | None = None, + ) -> OptimizeResult: + """Minimize the objective function. + + This interface is based on `SciPy's optimize module `__. + + Args: + fun: The objective function to minimize. + x0: The initial point for the optimization. + jac: The gradient of the objective function. + bounds: Parameters bounds for the optimization. Note that these might not be supported + by all optimizers. + + Returns: + The SciPy minimization result object. + """ + ... # pylint: disable=unnecessary-ellipsis + + class AQC: """ - A generic implementation of Approximate Quantum Compiler. This implementation is agnostic of + A generic implementation of the Approximate Quantum Compiler. This implementation is agnostic of the underlying implementation of the approximate circuit, objective, and optimizer. Users may pass corresponding implementations of the abstract classes: - * Optimizer is an instance of :class:`~qiskit.algorithms.optimizers.Optimizer` and used to run - the optimization process. A choice of optimizer may affect overall convergence, required time + * The *optimizer* is an implementation of the :class:`~.Minimizer` protocol, a callable used to run + the optimization process. The choice of optimizer may affect overall convergence, required time for the optimization process and achieved objective value. - * Approximate circuit represents a template which parameters we want to optimize. Currently, + * The *approximate circuit* represents a template which parameters we want to optimize. Currently, there's only one implementation based on 4-rotations CNOT unit blocks: :class:`.CNOTUnitCircuit`. See the paper for more details. - * Approximate objective is tightly coupled with the approximate circuit implementation and + * The *approximate objective* is tightly coupled with the approximate circuit implementation and provides two methods for computing objective function and gradient with respect to approximate circuit parameters. This objective is passed to the optimizer. Currently, there are two implementations based on 4-rotations CNOT unit blocks: :class:`.DefaultCNOTUnitObjective` and @@ -50,18 +102,21 @@ class AQC: def __init__( self, - optimizer: Optional[Optimizer] = None, - seed: Optional[int] = None, + optimizer: Minimizer | None = None, + seed: int | None = None, ): """ Args: optimizer: an optimizer to be used in the optimization procedure of the search for - the best approximate circuit. By default, :obj:`.L_BFGS_B` is used with max - iterations set to 1000. - seed: a seed value to be user by a random number generator. + the best approximate circuit. By default, the scipy minimizer with the + ``L-BFGS-B`` method is used with max iterations set to 1000. + seed: a seed value to be used by a random number generator. """ super().__init__() - self._optimizer = optimizer + self._optimizer = optimizer or partial( + minimize, args=(), method="L-BFGS-B", options={"maxiter": 1000} + ) + self._seed = seed def compile_unitary( @@ -69,7 +124,7 @@ def compile_unitary( target_matrix: np.ndarray, approximate_circuit: ApproximateCircuit, approximating_objective: ApproximatingObjective, - initial_point: Optional[np.ndarray] = None, + initial_point: np.ndarray | None = None, ) -> None: """ Approximately compiles a circuit represented as a unitary matrix by solving an optimization @@ -96,13 +151,11 @@ def compile_unitary( # set the matrix to approximate in the algorithm approximating_objective.target_matrix = su_matrix - optimizer = self._optimizer or L_BFGS_B(maxiter=1000) - if initial_point is None: np.random.seed(self._seed) initial_point = np.random.uniform(0, 2 * np.pi, approximating_objective.num_thetas) - opt_result = optimizer.minimize( + opt_result = self._optimizer( fun=approximating_objective.objective, x0=initial_point, jac=approximating_objective.gradient, diff --git a/qiskit/transpiler/synthesis/aqc/aqc_plugin.py b/qiskit/transpiler/synthesis/aqc/aqc_plugin.py index 0138d8e7b97a..0fa153566557 100644 --- a/qiskit/transpiler/synthesis/aqc/aqc_plugin.py +++ b/qiskit/transpiler/synthesis/aqc/aqc_plugin.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,6 +12,7 @@ """ An AQC synthesis plugin to Qiskit's transpiler. """ +from functools import partial import numpy as np from qiskit.converters import circuit_to_dag @@ -43,8 +44,8 @@ class AQCSynthesisPlugin(UnitarySynthesisPlugin): depth of the CNOT-network, i.e. the number of layers, where each layer consists of a single CNOT-block. - optimizer (:class:`~qiskit.algorithms.optimizers.Optimizer`) - An instance of optimizer to be used in the optimization process. + optimizer (:class:`~.Minimizer`) + An implementation of the ``Minimizer`` protocol to be used in the optimization process. seed (int) A random seed. @@ -104,7 +105,7 @@ def run(self, unitary, **options): # Runtime imports to avoid the overhead of these imports for # plugin discovery and only use them if the plugin is run/used - from qiskit.algorithms.optimizers import L_BFGS_B + from scipy.optimize import minimize from qiskit.transpiler.synthesis.aqc.aqc import AQC from qiskit.transpiler.synthesis.aqc.cnot_structures import make_cnot_network from qiskit.transpiler.synthesis.aqc.cnot_unit_circuit import CNOTUnitCircuit @@ -125,7 +126,8 @@ def run(self, unitary, **options): depth=depth, ) - optimizer = config.get("optimizer", L_BFGS_B(maxiter=1000)) + default_optimizer = partial(minimize, args=(), method="L-BFGS-B", options={"maxiter": 1000}) + optimizer = config.get("optimizer", default_optimizer) seed = config.get("seed") aqc = AQC(optimizer, seed) diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 8a895f0d9ba3..bc42a9c11e99 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -173,11 +173,11 @@ class Target(Mapping): } gmap.add_instruction(CXGate(), cx_props) - Each instruction in the Target is indexed by a unique string name that uniquely + Each instruction in the ``Target`` is indexed by a unique string name that uniquely identifies that instance of an :class:`~qiskit.circuit.Instruction` object in the Target. There is a 1:1 mapping between a name and an :class:`~qiskit.circuit.Instruction` instance in the target and each name must - be unique. By default the name is the :attr:`~qiskit.circuit.Instruction.name` + be unique. By default, the name is the :attr:`~qiskit.circuit.Instruction.name` attribute of the instruction, but can be set to anything. This lets a single target have multiple instances of the same instruction class with different parameters. For example, if a backend target has two instances of an @@ -242,7 +242,12 @@ class Target(Mapping): "concurrent_measurements", ) - @deprecate_arg("aquire_alignment", new_alias="acquire_alignment", since="0.23.0") + @deprecate_arg( + "aquire_alignment", + new_alias="acquire_alignment", + since="0.23.0", + package_name="qiskit-terra", + ) def __init__( self, description=None, @@ -256,7 +261,7 @@ def __init__( concurrent_measurements=None, ): """ - Create a new Target object + Create a new ``Target`` object Args: description (str): An optional string to describe the Target. @@ -291,10 +296,11 @@ def __init__( ``None`` concurrent_measurements(list): A list of sets of qubits that must be measured together. This must be provided - as a nested list like [[0, 1], [2, 3, 4]]. + as a nested list like ``[[0, 1], [2, 3, 4]]``. + Raises: ValueError: If both ``num_qubits`` and ``qubit_properties`` are both - defined and the value of ``num_qubits`` differs from the length of - ``qubit_properties``. + defined and the value of ``num_qubits`` differs from the length of + ``qubit_properties``. """ self.num_qubits = num_qubits # A mapping of gate name -> gate instance @@ -332,7 +338,7 @@ def add_instruction(self, instruction, properties=None, name=None): """Add a new instruction to the :class:`~qiskit.transpiler.Target` As ``Target`` objects are strictly additive this is the primary method - for modifying a ``Target``. Typically you will use this to fully populate + for modifying a ``Target``. Typically, you will use this to fully populate a ``Target`` before using it in :class:`~qiskit.providers.BackendV2`. For example:: @@ -363,7 +369,7 @@ def add_instruction(self, instruction, properties=None, name=None): Args: instruction (qiskit.circuit.Instruction): The operation object to add to the map. If it's - paramerterized any value of the parameter can be set. Optionally for variable width + parameterized any value of the parameter can be set. Optionally for variable width instructions (such as control flow operations such as :class:`~.ForLoop` or :class:`~MCXGate`) you can specify the class. If the class is specified than the ``name`` argument must be specified. When a class is used the gate is treated as global @@ -374,7 +380,7 @@ def add_instruction(self, instruction, properties=None, name=None): for any instruction implementation, if there are no :class:`~qiskit.transpiler.InstructionProperties` available for the backend the value can be None. If there are no constraints on the - instruction (as in a noisless/ideal simulation) this can be set to + instruction (as in a noiseless/ideal simulation) this can be set to ``{None, None}`` which will indicate it runs on all qubits (or all available permutations of qubits for multi-qubit gates). The first ``None`` indicates it applies to all qubits and the second ``None`` @@ -386,7 +392,7 @@ def add_instruction(self, instruction, properties=None, name=None): specified the :attr:`~qiskit.circuit.Instruction.name` attribute of ``gate`` will be used. All gates in the ``Target`` need unique names. Backends can differentiate between different - parameterizations of a single gate by providing a unique name for + parameterization of a single gate by providing a unique name for each (e.g. `"rx30"`, `"rx60", ``"rx90"`` similar to the example in the documentation for the :class:`~qiskit.transpiler.Target` class). Raises: @@ -613,7 +619,7 @@ def timing_constraints(self): """Get an :class:`~qiskit.transpiler.TimingConstraints` object from the target Returns: - TimingConstraints: The timing constraints represented in the Target + TimingConstraints: The timing constraints represented in the ``Target`` """ return TimingConstraints( self.granularity, self.min_length, self.pulse_alignment, self.acquire_alignment @@ -686,16 +692,15 @@ def operation_names_for_qargs(self, qargs): """Get the operation names for a specified qargs tuple Args: - qargs (tuple): A qargs tuple of the qubits to get the gates that apply + qargs (tuple): A ``qargs`` tuple of the qubits to get the gates that apply to it. For example, ``(0,)`` will return the set of all instructions that apply to qubit 0. If set to ``None`` this will return the names for any globally defined operations in the target. Returns: - set: The set of operation names that apply to the specified - `qargs``. + set: The set of operation names that apply to the specified ``qargs``. Raises: - KeyError: If qargs is not in target + KeyError: If ``qargs`` is not in target """ if qargs is not None and any(x not in range(0, self.num_qubits) for x in qargs): raise KeyError(f"{qargs} not in target.") @@ -735,7 +740,7 @@ def instruction_supported( is supported on the backend. For example, if you wanted to check whether a :class:`~.RXGate` was supported on a specific qubit with a fixed angle. That fixed angle variant will - typically have a name different than the object's + typically have a name different from the object's :attr:`~.Instruction.name` attribute (``"rx"``) in the target. This can be used to check if any instances of the class are available in such a case. @@ -757,7 +762,7 @@ def instruction_supported( parameters = [Parameter("theta")] target.instruction_supported("rx", (0,), parameters=parameters) - will return ``True`` if an :class:`~.RXGate` is suporrted on qubit 0 + will return ``True`` if an :class:`~.RXGate` is supported on qubit 0 that will accept any parameter. If you need to check for a fixed numeric value parameter this argument is typically paired with the ``operation_class`` argument. For example:: @@ -787,7 +792,7 @@ def check_obj_params(parameters, obj): if inspect.isclass(obj): if obj != operation_class: continue - # If no qargs a operation class is supported + # If no qargs operation class is supported if qargs is None: return True # If qargs set then validate no duplicates and all indices are valid on device @@ -1016,7 +1021,7 @@ def build_coupling_map(self, two_q_gate=None, filter_idle_qubits=False): Args: two_q_gate (str): An optional gate name for a two qubit gate in - the Target to generate the coupling map for. If specified the + the ``Target`` to generate the coupling map for. If specified the output coupling map will only have edges between qubits where this gate is present. filter_idle_qubits (bool): If set to ``True`` the output :class:`~.CouplingMap` @@ -1024,7 +1029,7 @@ def build_coupling_map(self, two_q_gate=None, filter_idle_qubits=False): target. Note that using this argument will result in an output :class:`~.CouplingMap` object which has holes in its indices which might differ from the assumptions of the class. The typical use - case of this argument is to be paired with with + case of this argument is to be paired with :meth:`.CouplingMap.connected_components` which will handle the holes as expected. Returns: @@ -1034,7 +1039,7 @@ def build_coupling_map(self, two_q_gate=None, filter_idle_qubits=False): Raises: ValueError: If a non-two qubit gate is passed in for ``two_q_gate``. - IndexError: If an Instruction not in the Target is passed in for + IndexError: If an Instruction not in the ``Target`` is passed in for ``two_q_gate``. """ if self.qargs is None: @@ -1089,13 +1094,13 @@ def get_non_global_operation_names(self, strict_direction=False): """Return the non-global operation names for the target The non-global operations are those in the target which don't apply - on all qubits (for single qubit operations) or all multiqubit qargs + on all qubits (for single qubit operations) or all multi-qubit qargs (for multi-qubit operations). Args: strict_direction (bool): If set to ``True`` the multi-qubit operations considered as non-global respect the strict - direction (or order of qubits in the qargs is signifcant). For + direction (or order of qubits in the qargs is significant). For example, if ``cx`` is defined on ``(0, 1)`` and ``ecr`` is defined over ``(1, 0)`` by default neither would be considered non-global, but if ``strict_direction`` is set ``True`` both @@ -1143,6 +1148,7 @@ def get_non_global_operation_names(self, strict_direction=False): additional_msg="Use the property ``acquire_alignment`` instead.", since="0.24.0", is_property=True, + package_name="qiskit-terra", ) def aquire_alignment(self): """Alias of deprecated name. This will be removed.""" @@ -1153,6 +1159,7 @@ def aquire_alignment(self): additional_msg="Use the property ``acquire_alignment`` instead.", since="0.24.0", is_property=True, + package_name="qiskit-terra", ) def aquire_alignment(self, new_value: int): """Alias of deprecated name. This will be removed.""" @@ -1248,7 +1255,7 @@ def from_configuration( Args: basis_gates: The list of basis gate names for the backend. For the target to be created these names must either be in the output - from :func:~.get_standard_gate_name_mapping` or present in the + from :func:`~.get_standard_gate_name_mapping` or present in the specified ``custom_name_mapping`` argument. num_qubits: The number of qubits supported on the backend. coupling_map: The coupling map representing connectivity constraints @@ -1259,7 +1266,7 @@ def from_configuration( is specified ``coupling_map`` must be specified. The ``coupling_map`` is used as the source of truth for connectivity and if ``inst_map`` is used the schedule is looked up based - on the instuctions from the pair of ``basis_gates`` and + on the instructions from the pair of ``basis_gates`` and ``coupling_map``. If you want to define a custom gate for a particular qubit or qubit pair, you can manually build :class:`.Target`. backend_properties: The :class:`~.BackendProperties` object which is @@ -1275,13 +1282,13 @@ def from_configuration( :class:`~InstructionProperties` objects for the instructions in the target. concurrent_measurements(list): A list of sets of qubits that must be measured together. This must be provided - as a nested list like [[0, 1], [2, 3, 4]]. + as a nested list like ``[[0, 1], [2, 3, 4]]``. dt: The system time resolution of input signals in seconds timing_constraints: Optional timing constraints to include in the :class:`~.Target` custom_name_mapping: An optional dictionary that maps custom gate/operation names in ``basis_gates`` to an :class:`~.Operation` object representing that - gate/operation. By default most standard gates names are mapped to the + gate/operation. By default, most standard gates names are mapped to the standard gate object from :mod:`qiskit.circuit.library` this only needs to be specified if the input ``basis_gates`` defines gates in names outside that set. @@ -1326,9 +1333,9 @@ def from_configuration( name_mapping.update(custom_name_mapping) # While BackendProperties can also contain coupling information we - # rely solely on CouplingMap to determin connectivity. This is because + # rely solely on CouplingMap to determine connectivity. This is because # in legacy transpiler usage (and implicitly in the BackendV1 data model) - # the coupling map is used to define connecitivity constraints and + # the coupling map is used to define connectivity constraints and # the properties is only used for error rate and duration population. # If coupling map is not specified we ignore the backend_properties if coupling_map is None: @@ -1470,7 +1477,7 @@ def target_to_backend_properties(target: Target): if getattr(props, "duration", None) is not None: property_list.append( { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "gate_length", "unit": "s", "value": props.duration, @@ -1479,7 +1486,7 @@ def target_to_backend_properties(target: Target): if getattr(props, "error", None) is not None: property_list.append( { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "gate_error", "unit": "", "value": props.error, @@ -1504,7 +1511,7 @@ def target_to_backend_properties(target: Target): if getattr(props, "error", None) is not None: props_list.append( { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "readout_error", "unit": "", "value": props.error, @@ -1513,7 +1520,7 @@ def target_to_backend_properties(target: Target): if getattr(props, "duration", None) is not None: props_list.append( { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "readout_length", "unit": "s", "value": props.duration, diff --git a/qiskit/utils/__init__.py b/qiskit/utils/__init__.py index 789b8c0da9b0..a9b73b85f95d 100644 --- a/qiskit/utils/__init__.py +++ b/qiskit/utils/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2021. +# (C) Copyright IBM 2018, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -29,29 +29,6 @@ .. autofunction:: detach_prefix .. autofunction:: wrap_method -Algorithm Utilities -=================== - -.. autofunction:: summarize_circuits -.. autofunction:: get_entangler_map -.. autofunction:: validate_entangler_map -.. autofunction:: has_ibmq -.. autofunction:: has_aer -.. autofunction:: name_args -.. autodata:: algorithm_globals - -.. autosummary:: - :toctree: ../stubs/ - :nosignatures: - - QuantumInstance - -A QuantumInstance holds the Qiskit `backend` as well as a number of compile and -runtime parameters controlling circuit compilation and execution. Quantum -:mod:`algorithms ` -are run on a device or simulator by passing a QuantumInstance setup with the desired -backend etc. - Optional Dependency Checkers (:mod:`qiskit.utils.optionals`) ============================================================ @@ -59,7 +36,6 @@ .. automodule:: qiskit.utils.optionals """ -from .quantum_instance import QuantumInstance from .deprecation import ( add_deprecation_to_docstring, deprecate_arg, @@ -75,25 +51,10 @@ from . import optionals -from .circuit_utils import summarize_circuits -from .entangler_map import get_entangler_map, validate_entangler_map -from .backend_utils import has_ibmq, has_aer -from .name_unnamed_args import name_args -from .algorithm_globals import algorithm_globals - - __all__ = [ "LazyDependencyManager", "LazyImportTester", "LazySubprocessTester", - "QuantumInstance", - "summarize_circuits", - "get_entangler_map", - "validate_entangler_map", - "has_ibmq", - "has_aer", - "name_args", - "algorithm_globals", "add_deprecation_to_docstring", "deprecate_arg", "deprecate_arguments", diff --git a/qiskit/utils/algorithm_globals.py b/qiskit/utils/algorithm_globals.py deleted file mode 100644 index eb1dd4f492e0..000000000000 --- a/qiskit/utils/algorithm_globals.py +++ /dev/null @@ -1,170 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Algorithm Globals""" - -from typing import Optional -import logging - -import numpy as np - -from qiskit.tools import parallel -from qiskit.utils.deprecation import deprecate_func -from ..user_config import get_config -from ..exceptions import QiskitError - - -logger = logging.getLogger(__name__) - - -class QiskitAlgorithmGlobals: - """Class for global properties.""" - - CPU_COUNT = parallel.local_hardware_info()["cpus"] - - @deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", - ) - def __init__(self) -> None: - self._random_seed = None # type: Optional[int] - self._num_processes = QiskitAlgorithmGlobals.CPU_COUNT - self._random = None - self._massive = False - try: - settings = get_config() - self.num_processes = settings.get("num_processes", QiskitAlgorithmGlobals.CPU_COUNT) - except Exception as ex: # pylint: disable=broad-except - logger.debug("User Config read error %s", str(ex)) - - @property - @deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", - is_property=True, - ) - def random_seed(self) -> Optional[int]: - """Return random seed.""" - return self._random_seed - - @random_seed.setter - @deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", - is_property=True, - ) - def random_seed(self, seed: Optional[int]) -> None: - """Set random seed.""" - self._random_seed = seed - self._random = None - - @property - @deprecate_func( - additional_msg=( - "This algorithm utility belongs to a legacy workflow and has no replacement." - ), - since="0.45.0", - is_property=True, - ) - def num_processes(self) -> int: - """Return num processes.""" - return self._num_processes - - @num_processes.setter - @deprecate_func( - additional_msg=( - "This algorithm utility belongs to a legacy workflow and has no replacement." - ), - since="0.45.0", - is_property=True, - ) - def num_processes(self, num_processes: Optional[int]) -> None: - """Set num processes. - If 'None' is passed, it resets to QiskitAlgorithmGlobals.CPU_COUNT - """ - if num_processes is None: - num_processes = QiskitAlgorithmGlobals.CPU_COUNT - elif num_processes < 1: - raise QiskitError(f"Invalid Number of Processes {num_processes}.") - elif num_processes > QiskitAlgorithmGlobals.CPU_COUNT: - raise QiskitError( - "Number of Processes {} cannot be greater than cpu count {}.".format( - num_processes, QiskitAlgorithmGlobals.CPU_COUNT - ) - ) - self._num_processes = num_processes - # TODO: change Terra CPU_COUNT until issue - # gets resolved: https://github.com/Qiskit/qiskit-terra/issues/1963 - try: - parallel.CPU_COUNT = self.num_processes - except Exception as ex: # pylint: disable=broad-except - logger.warning( - "Failed to set qiskit.tools.parallel.CPU_COUNT to value: '%s': Error: '%s'", - self.num_processes, - str(ex), - ) - - @property - @deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", - is_property=True, - ) - def random(self) -> np.random.Generator: - """Return a numpy np.random.Generator (default_rng).""" - if self._random is None: - self._random = np.random.default_rng(self._random_seed) - return self._random - - @property - @deprecate_func( - additional_msg=( - "This algorithm utility belongs to a legacy workflow and has no replacement." - ), - since="0.45.0", - is_property=True, - ) - def massive(self) -> bool: - """Return massive to allow processing of large matrices or vectors.""" - return self._massive - - @massive.setter - @deprecate_func( - additional_msg=( - "This algorithm utility belongs to a legacy workflow and has no replacement." - ), - since="0.45.0", - is_property=True, - ) - def massive(self, massive: bool) -> None: - """Set massive to allow processing of large matrices or vectors.""" - self._massive = massive - - -# Global instance to be used as the entry point for globals. -algorithm_globals = QiskitAlgorithmGlobals() diff --git a/qiskit/utils/arithmetic.py b/qiskit/utils/arithmetic.py deleted file mode 100644 index 23a838721f9e..000000000000 --- a/qiskit/utils/arithmetic.py +++ /dev/null @@ -1,152 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Arithmetic Utilities -""" - -from typing import List, Tuple -import numpy as np - - -def normalize_vector(vector): - """ - Normalize the input state vector. - """ - return vector / np.linalg.norm(vector) - - -def is_power_of_2(num): - """ - Check if the input number is a power of 2. - """ - return num != 0 and ((num & (num - 1)) == 0) - - -def log2(num): - """ - Compute the log2 of the input number. Use bit operation if the input is a power of 2. - """ - if is_power_of_2(num): - ret = 0 - while True: - if num >> ret == 1: - return ret - else: - ret += 1 - else: - return np.log2(num) - - -def is_power(num, return_decomposition=False): - """ - Check if num is a perfect power in O(n^3) time, n=ceil(logN) - """ - b = 2 - while (2**b) <= num: - a = 1 - c = num - while (c - a) >= 2: - m = int((a + c) / 2) - - if (m**b) < (num + 1): - p = int(m**b) - else: - p = int(num + 1) - - if int(p) == int(num): - if return_decomposition: - return True, int(m), int(b) - else: - return True - - if p < num: - a = int(m) - else: - c = int(m) - b = b + 1 - if return_decomposition: - return False, num, 1 - else: - return False - - -def next_power_of_2_base(n): - """ - Return the base of the smallest power of 2 no less than the input number - """ - base = 0 - if n and not (n & (n - 1)): # pylint: disable=superfluous-parens - return log2(n) - - while n != 0: - n >>= 1 - base += 1 - - return base - - -def transpositions(permutation: List[int]) -> List[Tuple[int, int]]: - """Return a sequence of transpositions, corresponding to the permutation. - - Args: - permutation: The ``List[int]`` defining the permutation. An element at index ``j`` should be - permuted to index ``permutation[j]``. - - Returns: - List of transpositions, corresponding to the permutation. For permutation = [3, 0, 2, 1], - returns [(0,1), (0,3)] - """ - unchecked = [True] * len(permutation) - cyclic_form = [] - for i in range(len(permutation)): - if unchecked[i]: - cycle = [i] - unchecked[i] = False - j = i - while unchecked[permutation[j]]: - j = permutation[j] - cycle.append(j) - unchecked[j] = False - if len(cycle) > 1: - cyclic_form.append(cycle) - cyclic_form.sort() - res = [] - for x in cyclic_form: - len_x = len(x) - if len_x == 2: - res.append((x[0], x[1])) - elif len_x > 2: - first = x[0] - for y in x[len_x - 1 : 0 : -1]: - res.append((first, y)) - return res - - -def triu_to_dense(triu: np.ndarray) -> np.ndarray: - """Converts upper triangular part of matrix to dense matrix. - - Args: - triu: array in the form [[A, B, C], [D, E], [F]] - - Returns: - Array [[A, B, C], [B, D, E], [C, E, F]] - """ - dim = len(triu) - matrix = np.empty((dim, dim), dtype=complex) - for i in range(dim): - for j in range(dim - i): - matrix[i, i + j] = triu[i][j] - if j != 0: - matrix[i + j, i] = triu[i][j] - - return matrix diff --git a/qiskit/utils/backend_utils.py b/qiskit/utils/backend_utils.py deleted file mode 100644 index df89d7f194da..000000000000 --- a/qiskit/utils/backend_utils.py +++ /dev/null @@ -1,279 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""backend utility functions""" - -import logging -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - -_UNSUPPORTED_BACKENDS = ["unitary_simulator", "clifford_simulator"] - -# pylint: disable=no-name-in-module,unused-import - - -class ProviderCheck: - """Contains Provider verification info.""" - - def __init__(self) -> None: - self.has_ibmq = False - self.checked_ibmq = False - self.has_aer = False - self.checked_aer = False - - -_PROVIDER_CHECK = ProviderCheck() - - -def _get_backend_interface_version(backend): - """Get the backend version int.""" - backend_interface_version = getattr(backend, "version", None) - return backend_interface_version - - -def _get_backend_provider(backend): - backend_interface_version = _get_backend_interface_version(backend) - if backend_interface_version > 1: - provider = backend.provider - else: - provider = backend.provider() - return provider - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def has_ibmq(): - """Check if IBMQ is installed.""" - if not _PROVIDER_CHECK.checked_ibmq: - try: - from qiskit.providers.ibmq import IBMQFactory - from qiskit.providers.ibmq.accountprovider import AccountProvider - - _PROVIDER_CHECK.has_ibmq = True - except Exception as ex: # pylint: disable=broad-except - _PROVIDER_CHECK.has_ibmq = False - logger.debug("IBMQFactory/AccountProvider not loaded: '%s'", str(ex)) - - _PROVIDER_CHECK.checked_ibmq = True - - return _PROVIDER_CHECK.has_ibmq - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def has_aer(): - """Check if Aer is installed.""" - if not _PROVIDER_CHECK.checked_aer: - try: - from qiskit.providers.aer import AerProvider - - _PROVIDER_CHECK.has_aer = True - except Exception as ex: # pylint: disable=broad-except - _PROVIDER_CHECK.has_aer = False - logger.debug("AerProvider not loaded: '%s'", str(ex)) - - _PROVIDER_CHECK.checked_aer = True - - return _PROVIDER_CHECK.has_aer - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def is_aer_provider(backend): - """Detect whether or not backend is from Aer provider. - - Args: - backend (Backend): backend instance - Returns: - bool: True is AerProvider - """ - if has_aer(): - from qiskit.providers.aer import AerProvider - - if isinstance(_get_backend_provider(backend), AerProvider): - return True - from qiskit.providers.aer.backends.aerbackend import AerBackend - - return isinstance(backend, AerBackend) - - return False - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def is_basicaer_provider(backend): - """Detect whether or not backend is from BasicAer provider. - - Args: - backend (Backend): backend instance - Returns: - bool: True is BasicAer - """ - from qiskit.providers.basicaer import BasicAerProvider - - return isinstance(_get_backend_provider(backend), BasicAerProvider) - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def is_ibmq_provider(backend): - """Detect whether or not backend is from IBMQ provider. - - Args: - backend (Backend): backend instance - Returns: - bool: True is IBMQ - """ - if has_ibmq(): - from qiskit.providers.ibmq.accountprovider import AccountProvider - - return isinstance(_get_backend_provider(backend), AccountProvider) - - return False - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def is_aer_statevector_backend(backend): - """ - Return True if backend object is statevector and from Aer provider. - - Args: - backend (Backend): backend instance - Returns: - bool: True is statevector - """ - return is_statevector_backend(backend) and is_aer_provider(backend) - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def is_statevector_backend(backend): - """ - Return True if backend object is statevector. - - Args: - backend (Backend): backend instance - Returns: - bool: True is statevector - """ - if backend is None: - return False - backend_interface_version = _get_backend_interface_version(backend) - if has_aer(): - from qiskit.providers.aer.backends import AerSimulator, StatevectorSimulator - - if isinstance(backend, StatevectorSimulator): - return True - if isinstance(backend, AerSimulator): - if backend_interface_version <= 1: - name = backend.name() - else: - name = backend.name - if "aer_simulator_statevector" in name: - return True - if backend_interface_version <= 1: - return backend.name().startswith("statevector") - else: - return backend.name.startswith("statevector") - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def is_simulator_backend(backend): - """ - Return True if backend is a simulator. - - Args: - backend (Backend): backend instance - Returns: - bool: True is a simulator - """ - backend_interface_version = _get_backend_interface_version(backend) - if backend_interface_version <= 1: - return backend.configuration().simulator - return False - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def is_local_backend(backend): - """ - Return True if backend is a local backend. - - Args: - backend (Backend): backend instance - Returns: - bool: True is a local backend - """ - backend_interface_version = _get_backend_interface_version(backend) - if backend_interface_version <= 1: - return backend.configuration().local - return False - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def is_aer_qasm(backend): - """ - Return True if backend is Aer Qasm simulator - Args: - backend (Backend): backend instance - - Returns: - bool: True is Aer Qasm simulator - """ - ret = False - if is_aer_provider(backend): - if not is_statevector_backend(backend): - ret = True - return ret - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def support_backend_options(backend): - """ - Return True if backend supports backend_options - Args: - backend (Backend): backend instance - - Returns: - bool: True is support backend_options - """ - ret = False - if is_basicaer_provider(backend) or is_aer_provider(backend): - ret = True - return ret diff --git a/qiskit/utils/circuit_utils.py b/qiskit/utils/circuit_utils.py deleted file mode 100644 index 2fe140d3780d..000000000000 --- a/qiskit/utils/circuit_utils.py +++ /dev/null @@ -1,69 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Circuit utility functions""" - -import numpy as np - - -def summarize_circuits(circuits): - """Summarize circuits based on QuantumCircuit, and five metrics are summarized. - - Number of qubits - - Number of classical bits - - Number of operations - - Depth of circuits - - Counts of different gate operations - - The average statistic of the first four is provided if multiple circuits are provided. - - Args: - circuits (QuantumCircuit or [QuantumCircuit]): the to-be-summarized circuits - - Returns: - str: a formatted string records the summary - """ - if not isinstance(circuits, list): - circuits = [circuits] - ret = "" - ret += f"Submitting {len(circuits)} circuits.\n" - ret += "============================================================================\n" - stats = np.zeros(4) - for i, circuit in enumerate(circuits): - depth = circuit.depth() - size = circuit.size() - num_qubits = sum(reg.size for reg in circuit.qregs) - num_clbits = sum(reg.size for reg in circuit.cregs) - op_counts = circuit.count_ops() - stats[0] += num_qubits - stats[1] += num_clbits - stats[2] += size - stats[3] += depth - ret = "".join( - [ - ret, - "{}-th circuit: {} qubits, {} classical bits and {} " - "operations with depth {}\nop_counts: {}\n".format( - i, num_qubits, num_clbits, size, depth, op_counts - ), - ] - ) - if len(circuits) > 1: - stats /= len(circuits) - ret = "".join( - [ - ret, - "Average: {:.2f} qubits, {:.2f} classical bits and {:.2f} " - "operations with depth {:.2f}\n".format(stats[0], stats[1], stats[2], stats[3]), - ] - ) - ret += "============================================================================\n" - return ret diff --git a/qiskit/utils/deprecation.py b/qiskit/utils/deprecation.py index 57fecfd9a449..b37cb1d63dbb 100644 --- a/qiskit/utils/deprecation.py +++ b/qiskit/utils/deprecation.py @@ -26,7 +26,7 @@ def deprecate_func( since: str, additional_msg: str | None = None, pending: bool = False, - package_name: str = "qiskit-terra", + package_name: str = "qiskit", removal_timeline: str = "no earlier than 3 months after the release date", is_property: bool = False, ): @@ -108,7 +108,7 @@ def deprecate_arg( additional_msg: str | None = None, deprecation_description: str | None = None, pending: bool = False, - package_name: str = "qiskit-terra", + package_name: str = "qiskit", new_alias: str | None = None, predicate: Callable[[Any], bool] | None = None, removal_timeline: str = "no earlier than 3 months after the release date", @@ -433,7 +433,7 @@ def add_deprecation_to_docstring( "This is a simplification to facilitate deprecation messages being added to the " "documentation. If you have a compelling reason to need " "new lines, feel free to improve this function or open a request at " - "https://github.com/Qiskit/qiskit-terra/issues." + "https://github.com/Qiskit/qiskit/issues." ) if since is None: diff --git a/qiskit/utils/entangler_map.py b/qiskit/utils/entangler_map.py deleted file mode 100644 index 1cd750398ccb..000000000000 --- a/qiskit/utils/entangler_map.py +++ /dev/null @@ -1,111 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -This module contains the definition of creating and validating entangler map -based on the number of qubits. -""" - - -def get_entangler_map(map_type, num_qubits, offset=0): - """Utility method to get an entangler map among qubits. - - Args: - map_type (str): 'full' entangles each qubit with all the subsequent ones - 'linear' entangles each qubit with the next - 'sca' (shifted circular alternating entanglement) is a - circular entanglement where the 'long' entanglement is - shifted by one position every block and every block the - role or control/target qubits alternate - num_qubits (int): Number of qubits for which the map is needed - offset (int): Some map_types (e.g. 'sca') can shift the gates in - the entangler map by the specified integer offset. - - Returns: - list: A map of qubit index to an array of indexes to which this should be entangled - - Raises: - ValueError: if map_type is not valid. - """ - ret = [] - - if num_qubits > 1: - if map_type == "full": - ret = [[i, j] for i in range(num_qubits) for j in range(i + 1, num_qubits)] - elif map_type == "linear": - ret = [[i, i + 1] for i in range(num_qubits - 1)] - elif map_type == "sca": - offset_idx = offset % num_qubits - if offset_idx % 2 == 0: # even block numbers - for i in reversed(range(offset_idx)): - ret += [[i, i + 1]] - - ret += [[num_qubits - 1, 0]] - - for i in reversed(range(offset_idx + 1, num_qubits)): - ret += [[i - 1, i]] - - else: # odd block numbers - for i in range(num_qubits - offset_idx - 1, num_qubits - 1): - ret += [[i + 1, i]] - - ret += [[0, num_qubits - 1]] - - for i in range(num_qubits - offset_idx - 1): - ret += [[i + 1, i]] - else: - raise ValueError("map_type only supports 'full', 'linear' or 'sca' type.") - return ret - - -def validate_entangler_map(entangler_map, num_qubits, allow_double_entanglement=False): - """Validate a user supplied entangler map and converts entries to ints. - - Args: - entangler_map (list[list]) : An entangler map, keys are source qubit index (int), - value is array - of target qubit index(es) (int) - num_qubits (int) : Number of qubits - allow_double_entanglement (bool): If we allow in two qubits can be entangled each other - - Returns: - list: Validated/converted map - - Raises: - TypeError: entangler map is not list type or list of list - ValueError: the index of entangler map is out of range - ValueError: the qubits are cross-entangled. - - """ - - if isinstance(entangler_map, dict): - raise TypeError("The type of entangler map is changed to list of list.") - - if not isinstance(entangler_map, list): - raise TypeError("Entangler map type 'list' expected") - - for src_to_targ in entangler_map: - if not isinstance(src_to_targ, list): - raise TypeError(f"Entangle index list expected but got {type(src_to_targ)}") - - ret_map = [] - ret_map = [[int(src), int(targ)] for src, targ in entangler_map] - - for src, targ in ret_map: - if src < 0 or src >= num_qubits: - raise ValueError(f"Qubit entangle source value {src} invalid for {num_qubits} qubits") - if targ < 0 or targ >= num_qubits: - raise ValueError(f"Qubit entangle target value {targ} invalid for {num_qubits} qubits") - if not allow_double_entanglement and [targ, src] in ret_map: - raise ValueError(f"Qubit {src} and {targ} cross-entangled.") - - return ret_map diff --git a/qiskit/utils/measurement_error_mitigation.py b/qiskit/utils/measurement_error_mitigation.py deleted file mode 100644 index 0ef09c997ec5..000000000000 --- a/qiskit/utils/measurement_error_mitigation.py +++ /dev/null @@ -1,268 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Measurement error mitigation""" - -import copy -from typing import List, Optional, Tuple, Dict, Callable - -from qiskit import compiler -from qiskit.providers import Backend -from qiskit.circuit import QuantumCircuit -from qiskit.qobj import QasmQobj -from qiskit.assembler.run_config import RunConfig -from qiskit.exceptions import QiskitError -from qiskit.utils.mitigation import ( - complete_meas_cal, - tensored_meas_cal, - CompleteMeasFitter, - TensoredMeasFitter, -) -from qiskit.utils.deprecation import deprecate_func - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def get_measured_qubits( - transpiled_circuits: List[QuantumCircuit], -) -> Tuple[List[int], Dict[str, List[int]]]: - """ - Deprecated: Retrieve the measured qubits from transpiled circuits. - - Args: - transpiled_circuits: a list of transpiled circuits - - Returns: - The used and sorted qubit index - Key is qubit index str connected by '_', - value is the experiment index. {str: list[int]} - Raises: - QiskitError: invalid qubit mapping - """ - qubit_index = None - qubit_mappings = {} - for idx, qc in enumerate(transpiled_circuits): - measured_qubits = [] - for instruction in qc.data: - if instruction.operation.name != "measure": - continue - for qreg in qc.qregs: - if instruction.qubits[0] in qreg: - index = qreg[:].index(instruction.qubits[0]) - measured_qubits.append(index) - break - measured_qubits_str = "_".join([str(x) for x in measured_qubits]) - if measured_qubits_str not in qubit_mappings: - qubit_mappings[measured_qubits_str] = [] - qubit_mappings[measured_qubits_str].append(idx) - if qubit_index is None: - qubit_index = measured_qubits - elif set(qubit_index) != set(measured_qubits): - raise QiskitError( - "The used qubit index are different. ({}) vs ({}).\nCurrently, " - "we only support all circuits using the same set of qubits " - "regardless qubit order.".format(qubit_index, measured_qubits) - ) - - return sorted(qubit_index), qubit_mappings - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def get_measured_qubits_from_qobj(qobj: QasmQobj) -> Tuple[List[int], Dict[str, List[int]]]: - """ - Deprecated: Retrieve the measured qubits from transpiled circuits. - - Args: - qobj: qobj - - Returns: - the used and sorted qubit index - key is qubit index str connected by '_', - value is the experiment index. {str: list[int]} - Raises: - QiskitError: invalid qubit mapping - """ - - qubit_index = None - qubit_mappings = {} - - for idx, exp in enumerate(qobj.experiments): - measured_qubits = [] - for instr in exp.instructions: - if instr.name != "measure": - continue - measured_qubits.append(instr.qubits[0]) - measured_qubits_str = "_".join([str(x) for x in measured_qubits]) - if measured_qubits_str not in qubit_mappings: - qubit_mappings[measured_qubits_str] = [] - qubit_mappings[measured_qubits_str].append(idx) - if qubit_index is None: - qubit_index = measured_qubits - else: - if set(qubit_index) != set(measured_qubits): - raise QiskitError( - "The used qubit index are different. ({}) vs ({}).\nCurrently, " - "we only support all circuits using the same set of qubits " - "regardless qubit order.".format(qubit_index, measured_qubits) - ) - - return sorted(qubit_index), qubit_mappings - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def build_measurement_error_mitigation_circuits( - qubit_list: List[int], - fitter_cls: Callable, - backend: Backend, - backend_config: Optional[Dict] = None, - compile_config: Optional[Dict] = None, - mit_pattern: Optional[List[List[int]]] = None, -) -> Tuple[QuantumCircuit, List[str], List[str]]: - """Deprecated: Build measurement error mitigation circuits - Args: - qubit_list: list of ordered qubits used in the algorithm - fitter_cls: CompleteMeasFitter or TensoredMeasFitter - backend: backend instance - backend_config: configuration for backend - compile_config: configuration for compilation - mit_pattern: Qubits on which to perform the - measurement correction, divided to groups according to tensors. - If `None` and `qr` is given then assumed to be performed over the entire - `qr` as one group (default `None`). - - Returns: - the circuit - the state labels for build MeasFitter - the labels of the calibration circuits - Raises: - QiskitError: when the fitter_cls is not recognizable. - """ - circlabel = "mcal" - - if not qubit_list: - raise QiskitError("The measured qubit list can not be [].") - - run = False - if fitter_cls == CompleteMeasFitter: - meas_calibs_circuits, state_labels = complete_meas_cal( - qubit_list=range(len(qubit_list)), circlabel=circlabel - ) - run = True - elif fitter_cls == TensoredMeasFitter: - meas_calibs_circuits, state_labels = tensored_meas_cal( - mit_pattern=mit_pattern, circlabel=circlabel - ) - run = True - if not run: - try: - from qiskit.ignis.mitigation.measurement import ( - CompleteMeasFitter as CompleteMeasFitter_IG, - TensoredMeasFitter as TensoredMeasFitter_IG, - ) - except ImportError as ex: - # If ignis can't be imported we don't have a valid fitter - # class so just fail here with an appropriate error message - raise QiskitError(f"Unknown fitter {fitter_cls}") from ex - if fitter_cls == CompleteMeasFitter_IG: - meas_calibs_circuits, state_labels = complete_meas_cal( - qubit_list=range(len(qubit_list)), circlabel=circlabel - ) - elif fitter_cls == TensoredMeasFitter_IG: - meas_calibs_circuits, state_labels = tensored_meas_cal( - mit_pattern=mit_pattern, circlabel=circlabel - ) - else: - raise QiskitError(f"Unknown fitter {fitter_cls}") - - # the provided `qubit_list` would be used as the initial layout to - # assure the consistent qubit mapping used in the main circuits. - - tmp_compile_config = copy.deepcopy(compile_config) - tmp_compile_config["initial_layout"] = qubit_list - t_meas_calibs_circuits = compiler.transpile( - meas_calibs_circuits, backend, **backend_config, **tmp_compile_config - ) - return t_meas_calibs_circuits, state_labels, circlabel - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def build_measurement_error_mitigation_qobj( - qubit_list: List[int], - fitter_cls: Callable, - backend: Backend, - backend_config: Optional[Dict] = None, - compile_config: Optional[Dict] = None, - run_config: Optional[RunConfig] = None, - mit_pattern: Optional[List[List[int]]] = None, -) -> Tuple[QasmQobj, List[str], List[str]]: - """ - Args: - qubit_list: list of ordered qubits used in the algorithm - fitter_cls: CompleteMeasFitter or TensoredMeasFitter - backend: backend instance - backend_config: configuration for backend - compile_config: configuration for compilation - run_config: configuration for running a circuit - mit_pattern: Qubits on which to perform the - measurement correction, divided to groups according to tensors. - If `None` and `qr` is given then assumed to be performed over the entire - `qr` as one group (default `None`). - - Returns: - the Qobj with calibration circuits at the beginning - the state labels for build MeasFitter - the labels of the calibration circuits - - Raises: - QiskitError: when the fitter_cls is not recognizable. - MissingOptionalLibraryError: Qiskit-Ignis not installed - """ - - circlabel = "mcal" - - if not qubit_list: - raise QiskitError("The measured qubit list can not be [].") - - if fitter_cls == CompleteMeasFitter: - meas_calibs_circuits, state_labels = complete_meas_cal( - qubit_list=range(len(qubit_list)), circlabel=circlabel - ) - elif fitter_cls == TensoredMeasFitter: - meas_calibs_circuits, state_labels = tensored_meas_cal( - mit_pattern=mit_pattern, circlabel=circlabel - ) - else: - raise QiskitError(f"Unknown fitter {fitter_cls}") - - # the provided `qubit_list` would be used as the initial layout to - # assure the consistent qubit mapping used in the main circuits. - - tmp_compile_config = copy.deepcopy(compile_config) - tmp_compile_config["initial_layout"] = qubit_list - t_meas_calibs_circuits = compiler.transpile( - meas_calibs_circuits, backend, **backend_config, **tmp_compile_config - ) - cals_qobj = compiler.assemble(t_meas_calibs_circuits, backend, **run_config.to_dict()) - if hasattr(cals_qobj.config, "parameterizations"): - del cals_qobj.config.parameterizations - return cals_qobj, state_labels, circlabel diff --git a/qiskit/utils/mitigation/__init__.py b/qiskit/utils/mitigation/__init__.py deleted file mode 100644 index c79b107cdc0f..000000000000 --- a/qiskit/utils/mitigation/__init__.py +++ /dev/null @@ -1,58 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# This code was originally copied from the qiskit-ignis repsoitory see: -# https://github.com/Qiskit/qiskit-ignis/blob/b91066c72171bcd55a70e6e8993b813ec763cf41/qiskit/ignis/mitigation/measurement/__init__.py -# it was migrated as qiskit-ignis is being deprecated - -""" -============================================================= -Measurement Mitigation Utils (:mod:`qiskit.utils.mitigation`) -============================================================= - -.. currentmodule:: qiskit.utils.mitigation - -.. deprecated:: 0.24.0 - This module is deprecated and will be removed no sooner than 3 months - after the release date. For code migration guidelines, - visit https://qisk.it/qi_migration. - -.. warning:: - - The user-facing API stability of this module is not guaranteed except for - its use with the :class:`~qiskit.utils.QuantumInstance` (i.e. using the - :class:`~qiskit.utils.mitigation.CompleteMeasFitter` or - :class:`~qiskit.utils.mitigation.TensoredMeasFitter` classes as values for the - ``meas_error_mitigation_cls``). The rest of this module should be treated as - an internal private API that can not be relied upon. - -Measurement correction -====================== - -The measurement calibration is used to mitigate measurement errors. -The main idea is to prepare all :math:`2^n` basis input states and compute -the probability of measuring counts in the other basis states. -From these calibrations, it is possible to correct the average results -of another experiment of interest. These tools are intended for use solely -with the :class:`~qiskit.utils.QuantumInstance` class as part of -:mod:`qiskit.algorithms` and :mod:`qiskit.opflow`. - -.. autosummary:: - :toctree: ../stubs/ - - CompleteMeasFitter - TensoredMeasFitter -""" - -# Measurement correction functions -from .circuits import complete_meas_cal, tensored_meas_cal -from .fitters import CompleteMeasFitter, TensoredMeasFitter diff --git a/qiskit/utils/mitigation/_filters.py b/qiskit/utils/mitigation/_filters.py deleted file mode 100644 index 5d566f625116..000000000000 --- a/qiskit/utils/mitigation/_filters.py +++ /dev/null @@ -1,508 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# This code was originally copied from the qiskit-ignis see: -# https://github.com/Qiskit/qiskit-ignis/blob/b91066c72171bcd55a70e6e8993b813ec763cf41/qiskit/ignis/mitigation/measurement/filters.py -# it was migrated as qiskit-ignis is being deprecated - -# pylint: disable=cell-var-from-loop - - -""" -Measurement correction filters. - -""" - -from typing import List -from copy import deepcopy - -import numpy as np - -import qiskit -from qiskit import QiskitError -from qiskit.tools import parallel_map -from qiskit.utils.mitigation.circuits import count_keys -from qiskit.utils.deprecation import deprecate_func - - -class MeasurementFilter: - """ - Deprecated: Measurement error mitigation filter. - - Produced from a measurement calibration fitter and can be applied - to data. - - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", - ) - def __init__(self, cal_matrix: np.matrix, state_labels: list): - """ - Initialize a measurement error mitigation filter using the cal_matrix - from a measurement calibration fitter. - - Args: - cal_matrix: the calibration matrix for applying the correction - state_labels: the states for the ordering of the cal matrix - """ - - self._cal_matrix = cal_matrix - self._state_labels = state_labels - - @property - def cal_matrix(self): - """Return cal_matrix.""" - return self._cal_matrix - - @property - def state_labels(self): - """return the state label ordering of the cal matrix""" - return self._state_labels - - @state_labels.setter - def state_labels(self, new_state_labels): - """set the state label ordering of the cal matrix""" - self._state_labels = new_state_labels - - @cal_matrix.setter - def cal_matrix(self, new_cal_matrix): - """Set cal_matrix.""" - self._cal_matrix = new_cal_matrix - - def apply(self, raw_data, method="least_squares"): - """Apply the calibration matrix to results. - - Args: - raw_data (dict or list): The data to be corrected. Can be in a number of forms: - - Form 1: a counts dictionary from results.get_counts - - Form 2: a list of counts of `length==len(state_labels)` - - Form 3: a list of counts of `length==M*len(state_labels)` where M is an - integer (e.g. for use with the tomography data) - - Form 4: a qiskit Result - - method (str): fitting method. If `None`, then least_squares is used. - - ``pseudo_inverse``: direct inversion of the A matrix - - ``least_squares``: constrained to have physical probabilities - - Returns: - dict or list: The corrected data in the same form as `raw_data` - - Raises: - QiskitError: if `raw_data` is not an integer multiple - of the number of calibrated states. - - """ - from scipy.optimize import minimize - from scipy import linalg as la - - # check forms of raw_data - if isinstance(raw_data, dict): - # counts dictionary - for data_label in raw_data.keys(): - if data_label not in self._state_labels: - raise QiskitError( - f"Unexpected state label '{data_label}'." - " Verify the fitter's state labels correspond to the input data." - ) - - data_format = 0 - # convert to form2 - raw_data2 = [np.zeros(len(self._state_labels), dtype=float)] - for stateidx, state in enumerate(self._state_labels): - raw_data2[0][stateidx] = raw_data.get(state, 0) - - elif isinstance(raw_data, list): - size_ratio = len(raw_data) / len(self._state_labels) - if len(raw_data) == len(self._state_labels): - data_format = 1 - raw_data2 = [raw_data] - elif int(size_ratio) == size_ratio: - data_format = 2 - size_ratio = int(size_ratio) - # make the list into chunks the size of state_labels for easier - # processing - raw_data2 = np.zeros([size_ratio, len(self._state_labels)]) - for i in range(size_ratio): - raw_data2[i][:] = raw_data[ - i * len(self._state_labels) : (i + 1) * len(self._state_labels) - ] - else: - raise QiskitError( - "Data list is not an integer multiple of the number of calibrated states" - ) - - elif isinstance(raw_data, qiskit.result.result.Result): - - # extract out all the counts, re-call the function with the - # counts and push back into the new result - new_result = deepcopy(raw_data) - - new_counts_list = parallel_map( - self._apply_correction, - [resultidx for resultidx, _ in enumerate(raw_data.results)], - task_args=(raw_data, method), - ) - - for resultidx, new_counts in new_counts_list: - new_result.results[resultidx].data.counts = new_counts - - return new_result - - else: - raise QiskitError("Unrecognized type for raw_data.") - - if method == "pseudo_inverse": - pinv_cal_mat = la.pinv(self._cal_matrix) - - # Apply the correction - for data_idx, _ in enumerate(raw_data2): - - if method == "pseudo_inverse": - raw_data2[data_idx] = np.dot(pinv_cal_mat, raw_data2[data_idx]) - - elif method == "least_squares": - nshots = sum(raw_data2[data_idx]) - - def fun(x): - return sum((raw_data2[data_idx] - np.dot(self._cal_matrix, x)) ** 2) - - x0 = np.random.rand(len(self._state_labels)) - x0 = x0 / sum(x0) - cons = {"type": "eq", "fun": lambda x: nshots - sum(x)} - bnds = tuple((0, nshots) for x in x0) - res = minimize(fun, x0, method="SLSQP", constraints=cons, bounds=bnds, tol=1e-6) - raw_data2[data_idx] = res.x - - else: - raise QiskitError("Unrecognized method.") - - if data_format == 2: - # flatten back out the list - raw_data2 = raw_data2.flatten() - - elif data_format == 0: - # convert back into a counts dictionary - new_count_dict = {} - for stateidx, state in enumerate(self._state_labels): - if raw_data2[0][stateidx] != 0: - new_count_dict[state] = raw_data2[0][stateidx] - - raw_data2 = new_count_dict - else: - # TODO: should probably change to: - # raw_data2 = raw_data2[0].tolist() - raw_data2 = raw_data2[0] - return raw_data2 - - def _apply_correction(self, resultidx, raw_data, method): - """Wrapper to call apply with a counts dictionary.""" - new_counts = self.apply(raw_data.get_counts(resultidx), method=method) - return resultidx, new_counts - - -class TensoredFilter: - """ - Deprecated: Tensored measurement error mitigation filter. - - Produced from a tensored measurement calibration fitter and can be applied - to data. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", - ) - def __init__(self, cal_matrices: np.matrix, substate_labels_list: list, mit_pattern: list): - """ - Initialize a tensored measurement error mitigation filter using - the cal_matrices from a tensored measurement calibration fitter. - A simple usage this class is explained [here] - (https://qiskit.org/documentation/tutorials/noise/3_measurement_error_mitigation.html). - - Args: - cal_matrices: the calibration matrices for applying the correction. - substate_labels_list: for each calibration matrix - a list of the states (as strings, states in the subspace) - mit_pattern: for each calibration matrix - a list of the logical qubit indices (as int, states in the subspace) - """ - - self._cal_matrices = cal_matrices - self._qubit_list_sizes = [] - self._indices_list = [] - self._substate_labels_list = [] - self.substate_labels_list = substate_labels_list - self._mit_pattern = mit_pattern - - @property - def cal_matrices(self): - """Return cal_matrices.""" - return self._cal_matrices - - @cal_matrices.setter - def cal_matrices(self, new_cal_matrices): - """Set cal_matrices.""" - self._cal_matrices = deepcopy(new_cal_matrices) - - @property - def substate_labels_list(self): - """Return _substate_labels_list""" - return self._substate_labels_list - - @substate_labels_list.setter - def substate_labels_list(self, new_substate_labels_list): - """Return _substate_labels_list""" - self._substate_labels_list = new_substate_labels_list - - # get the number of qubits in each subspace - self._qubit_list_sizes = [] - for _, substate_label_list in enumerate(self._substate_labels_list): - self._qubit_list_sizes.append(int(np.log2(len(substate_label_list)))) - - # get the indices in the calibration matrix - self._indices_list = [] - for _, sub_labels in enumerate(self._substate_labels_list): - - self._indices_list.append({lab: ind for ind, lab in enumerate(sub_labels)}) - - @property - def qubit_list_sizes(self): - """Return _qubit_list_sizes.""" - return self._qubit_list_sizes - - @property - def nqubits(self): - """Return the number of qubits. See also MeasurementFilter.apply()""" - return sum(self._qubit_list_sizes) - - def apply( - self, - raw_data, - method="least_squares", - meas_layout=None, - ): - """ - Apply the calibration matrices to results. - - Args: - raw_data (dict or Result): The data to be corrected. Can be in one of two forms: - - * A counts dictionary from results.get_counts - - * A Qiskit Result - - method (str): fitting method. The following methods are supported: - - * 'pseudo_inverse': direct inversion of the cal matrices. - Mitigated counts can contain negative values - and the sum of counts would not equal to the shots. - Mitigation is conducted qubit wise: - For each qubit, mitigate the whole counts using the calibration matrices - which affect the corresponding qubit. - For example, assume we are mitigating the 3rd bit of the 4-bit counts - using '2\times 2' calibration matrix `A_3`. - When mitigating the count of '0110' in this step, - the following formula is applied: - `count['0110'] = A_3^{-1}[1, 0]*count['0100'] + A_3^{-1}[1, 1]*count['0110']`. - - The total time complexity of this method is `O(m2^{n + t})`, - where `n` is the size of calibrated qubits, - `m` is the number of sets in `mit_pattern`, - and `t` is the size of largest set of mit_pattern. - If the `mit_pattern` is shaped like `[[0], [1], [2], ..., [n-1]]`, - which corresponds to the tensor product noise model without cross-talk, - then the time complexity would be `O(n2^n)`. - If the `mit_pattern` is shaped like `[[0, 1, 2, ..., n-1]]`, - which exactly corresponds to the complete error mitigation, - then the time complexity would be `O(2^(n+n)) = O(4^n)`. - - - * 'least_squares': constrained to have physical probabilities. - Instead of directly applying inverse calibration matrices, - this method solve a constrained optimization problem to find - the closest probability vector to the result from 'pseudo_inverse' method. - Sequential least square quadratic programming (SLSQP) is used - in the internal process. - Every updating step in SLSQP takes `O(m2^{n+t})` time. - Since this method is using the SLSQP optimization over - the vector with lenght `2^n`, the mitigation for 8 bit counts - with the `mit_pattern = [[0], [1], [2], ..., [n-1]]` would - take 10 seconds or more. - - * If `None`, 'least_squares' is used. - - meas_layout (list of int): the mapping from classical registers to qubits - - * If you measure qubit `2` to clbit `0`, `0` to `1`, and `1` to `2`, - the list becomes `[2, 0, 1]` - - * If `None`, flatten(mit_pattern) is used. - - Returns: - dict or Result: The corrected data in the same form as raw_data - - Raises: - QiskitError: if raw_data is not in a one of the defined forms. - """ - from scipy.optimize import minimize - from scipy import linalg as la - - all_states = count_keys(self.nqubits) - num_of_states = 2**self.nqubits - - if meas_layout is None: - meas_layout = [] - for qubits in self._mit_pattern: - meas_layout += qubits - - # check forms of raw_data - if isinstance(raw_data, dict): - # counts dictionary - # convert to list - raw_data2 = [np.zeros(num_of_states, dtype=float)] - for state, count in raw_data.items(): - stateidx = int(state, 2) - raw_data2[0][stateidx] = count - - elif isinstance(raw_data, qiskit.result.result.Result): - - # extract out all the counts, re-call the function with the - # counts and push back into the new result - new_result = deepcopy(raw_data) - - new_counts_list = parallel_map( - self._apply_correction, - [resultidx for resultidx, _ in enumerate(raw_data.results)], - task_args=(raw_data, method, meas_layout), - ) - - for resultidx, new_counts in new_counts_list: - new_result.results[resultidx].data.counts = new_counts - - return new_result - - else: - raise QiskitError("Unrecognized type for raw_data.") - - if method == "pseudo_inverse": - pinv_cal_matrices = [] - for cal_mat in self._cal_matrices: - pinv_cal_matrices.append(la.pinv(cal_mat)) - - meas_layout = meas_layout[::-1] # reverse endian - qubits_to_clbits = [-1 for _ in range(max(meas_layout) + 1)] - for i, qubit in enumerate(meas_layout): - qubits_to_clbits[qubit] = i - - # Apply the correction - for data_idx, _ in enumerate(raw_data2): - - if method == "pseudo_inverse": - for pinv_cal_mat, pos_qubits, indices in zip( - pinv_cal_matrices, self._mit_pattern, self._indices_list - ): - inv_mat_dot_x = np.zeros([num_of_states], dtype=float) - pos_clbits = [qubits_to_clbits[qubit] for qubit in pos_qubits] - for state_idx, state in enumerate(all_states): - first_index = self.compute_index_of_cal_mat(state, pos_clbits, indices) - for i in range(len(pinv_cal_mat)): # i is index of pinv_cal_mat - source_state = self.flip_state(state, i, pos_clbits) - second_index = self.compute_index_of_cal_mat( - source_state, pos_clbits, indices - ) - inv_mat_dot_x[state_idx] += ( - pinv_cal_mat[first_index, second_index] - * raw_data2[data_idx][int(source_state, 2)] - ) - raw_data2[data_idx] = inv_mat_dot_x - - elif method == "least_squares": - - def fun(x): - mat_dot_x = deepcopy(x) - for cal_mat, pos_qubits, indices in zip( - self._cal_matrices, self._mit_pattern, self._indices_list - ): - res_mat_dot_x = np.zeros([num_of_states], dtype=float) - pos_clbits = [qubits_to_clbits[qubit] for qubit in pos_qubits] - for state_idx, state in enumerate(all_states): - second_index = self.compute_index_of_cal_mat(state, pos_clbits, indices) - for i in range(len(cal_mat)): - target_state = self.flip_state(state, i, pos_clbits) - first_index = self.compute_index_of_cal_mat( - target_state, pos_clbits, indices - ) - res_mat_dot_x[int(target_state, 2)] += ( - cal_mat[first_index, second_index] * mat_dot_x[state_idx] - ) - mat_dot_x = res_mat_dot_x - return sum((raw_data2[data_idx] - mat_dot_x) ** 2) - - x0 = np.random.rand(num_of_states) - x0 = x0 / sum(x0) - nshots = sum(raw_data2[data_idx]) - cons = {"type": "eq", "fun": lambda x: nshots - sum(x)} - bnds = tuple((0, nshots) for x in x0) - res = minimize(fun, x0, method="SLSQP", constraints=cons, bounds=bnds, tol=1e-6) - raw_data2[data_idx] = res.x - - else: - raise QiskitError("Unrecognized method.") - - # convert back into a counts dictionary - new_count_dict = {} - for state_idx, state in enumerate(all_states): - if raw_data2[0][state_idx] != 0: - new_count_dict[state] = raw_data2[0][state_idx] - - return new_count_dict - - def flip_state(self, state: str, mat_index: int, flip_poses: List[int]) -> str: - """Flip the state according to the chosen qubit positions""" - flip_poses = [pos for i, pos in enumerate(flip_poses) if (mat_index >> i) & 1] - flip_poses = sorted(flip_poses) - new_state = "" - pos = 0 - for flip_pos in flip_poses: - new_state += state[pos:flip_pos] - new_state += str(int(state[flip_pos], 2) ^ 1) # flip the state - pos = flip_pos + 1 - new_state += state[pos:] - return new_state - - def compute_index_of_cal_mat(self, state: str, pos_qubits: List[int], indices: dict) -> int: - """Return the index of (pseudo inverse) calibration matrix for the input quantum state""" - sub_state = "" - for pos in pos_qubits: - sub_state += state[pos] - return indices[sub_state] - - def _apply_correction( - self, - resultidx, - raw_data, - method, - meas_layout, - ): - """Wrapper to call apply with a counts dictionary.""" - new_counts = self.apply( - raw_data.get_counts(resultidx), method=method, meas_layout=meas_layout - ) - return resultidx, new_counts diff --git a/qiskit/utils/mitigation/circuits.py b/qiskit/utils/mitigation/circuits.py deleted file mode 100644 index 2fdeaa6372a6..000000000000 --- a/qiskit/utils/mitigation/circuits.py +++ /dev/null @@ -1,250 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# This code was originally copied from the qiskit-ignis repsoitory see: -# https://github.com/Qiskit/qiskit-ignis/blob/b91066c72171bcd55a70e6e8993b813ec763cf41/qiskit/ignis/mitigation/measurement/circuits.py -# it was migrated to qiskit-terra as qiskit-ignis is being deprecated - -""" -Measurement calibration circuits. To apply the measurement mitigation -use the fitters to produce a filter. -""" -from typing import List, Tuple, Union -from qiskit.utils.deprecation import deprecate_func - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def count_keys(num_qubits: int) -> List[str]: - """Deprecated: Return ordered count keys. - - Args: - num_qubits: The number of qubits in the generated list. - Returns: - The strings of all 0/1 combinations of the given number of qubits - Example: - >>> count_keys(3) - ['000', '001', '010', '011', '100', '101', '110', '111'] - """ - return [bin(j)[2:].zfill(num_qubits) for j in range(2**num_qubits)] - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def complete_meas_cal( - qubit_list: List[int] = None, - qr: Union[int, List["QuantumRegister"]] = None, - cr: Union[int, List["ClassicalRegister"]] = None, - circlabel: str = "", -) -> Tuple[List["QuantumCircuit"], List[str]]: - """ - Deprecated: Return a list of measurement calibration circuits for the full - Hilbert space. - - If the circuit contains :math:`n` qubits, then :math:`2^n` calibration circuits - are created, each of which creates a basis state. - - Args: - qubit_list: A list of qubits to perform the measurement correction on. - If `None`, and qr is given then assumed to be performed over the entire - qr. The calibration states will be labelled according to this ordering (default `None`). - - qr: Quantum registers (or their size). - If ``None``, one is created (default ``None``). - - cr: Classical registers (or their size). - If ``None``, one is created(default ``None``). - - circlabel: A string to add to the front of circuit names for - unique identification(default ' '). - - Returns: - A list of QuantumCircuit objects containing the calibration circuits. - - A list of calibration state labels. - - Additional Information: - The returned circuits are named circlabel+cal_XXX - where XXX is the basis state, - e.g., cal_1001. - - Pass the results of these circuits to the CompleteMeasurementFitter - constructor. - - Raises: - QiskitError: if both `qubit_list` and `qr` are `None`. - - """ - # Runtime imports to avoid circular imports causeed by QuantumInstance - # getting initialized by imported utils/__init__ which is imported - # by qiskit.circuit - from qiskit.circuit.quantumregister import QuantumRegister - from qiskit.circuit.classicalregister import ClassicalRegister - from qiskit.circuit.exceptions import QiskitError - - if qubit_list is None and qr is None: - raise QiskitError("Must give one of a qubit_list or a qr") - - # Create the registers if not already done - if qr is None: - qr = QuantumRegister(max(qubit_list) + 1) - - if isinstance(qr, int): - qr = QuantumRegister(qr) - - if qubit_list is None: - qubit_list = range(len(qr)) - - if isinstance(cr, int): - cr = ClassicalRegister(cr) - - nqubits = len(qubit_list) - - # labels for 2**n qubit states - state_labels = count_keys(nqubits) - - cal_circuits, _ = tensored_meas_cal([qubit_list], qr, cr, circlabel) - - return cal_circuits, state_labels - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def tensored_meas_cal( - mit_pattern: List[List[int]] = None, - qr: Union[int, List["QuantumRegister"]] = None, - cr: Union[int, List["ClassicalRegister"]] = None, - circlabel: str = "", -) -> Tuple[List["QuantumCircuit"], List[List[int]]]: - """ - Deprecated: Return a list of calibration circuits - - Args: - mit_pattern: Qubits on which to perform the - measurement correction, divided to groups according to tensors. - If `None` and `qr` is given then assumed to be performed over the entire - `qr` as one group (default `None`). - - qr: A quantum register (or its size). - If `None`, one is created (default `None`). - - cr: A classical register (or its size). - If `None`, one is created (default `None`). - - circlabel: A string to add to the front of circuit names for - unique identification (default ' '). - - Returns: - A list of two QuantumCircuit objects containing the calibration circuits - mit_pattern - - Additional Information: - The returned circuits are named circlabel+cal_XXX - where XXX is the basis state, - e.g., cal_000 and cal_111. - - Pass the results of these circuits to the TensoredMeasurementFitter - constructor. - - Raises: - QiskitError: if both `mit_pattern` and `qr` are None. - QiskitError: if a qubit appears more than once in `mit_pattern`. - - """ - # Runtime imports to avoid circular imports causeed by QuantumInstance - # getting initialized by imported utils/__init__ which is imported - # by qiskit.circuit - from qiskit.circuit.quantumregister import QuantumRegister - from qiskit.circuit.classicalregister import ClassicalRegister - from qiskit.circuit.quantumcircuit import QuantumCircuit - from qiskit.circuit.exceptions import QiskitError - - if mit_pattern is None and qr is None: - raise QiskitError("Must give one of mit_pattern or qr") - - if isinstance(qr, int): - qr = QuantumRegister(qr) - - qubits_in_pattern = [] - if mit_pattern is not None: - for qubit_list in mit_pattern: - for qubit in qubit_list: - if qubit in qubits_in_pattern: - raise QiskitError( - "mit_pattern cannot contain multiple instances of the same qubit" - ) - qubits_in_pattern.append(qubit) - - # Create the registers if not already done - if qr is None: - qr = QuantumRegister(max(qubits_in_pattern) + 1) - else: - qubits_in_pattern = range(len(qr)) - mit_pattern = [qubits_in_pattern] - - nqubits = len(qubits_in_pattern) - - # create classical bit registers - if cr is None: - cr = ClassicalRegister(nqubits) - - if isinstance(cr, int): - cr = ClassicalRegister(cr) - - qubits_list_sizes = [len(qubit_list) for qubit_list in mit_pattern] - nqubits = sum(qubits_list_sizes) - size_of_largest_group = max(qubits_list_sizes) - largest_labels = count_keys(size_of_largest_group) - - state_labels = [] - for largest_state in largest_labels: - basis_state = "" - for list_size in qubits_list_sizes: - basis_state = largest_state[:list_size] + basis_state - state_labels.append(basis_state) - - cal_circuits = [] - for basis_state in state_labels: - qc_circuit = QuantumCircuit(qr, cr, name=f"{circlabel}cal_{basis_state}") - - end_index = nqubits - for qubit_list, list_size in zip(mit_pattern, qubits_list_sizes): - - start_index = end_index - list_size - substate = basis_state[start_index:end_index] - - for qind in range(list_size): - if substate[list_size - qind - 1] == "1": - qc_circuit.x(qr[qubit_list[qind]]) - - end_index = start_index - - qc_circuit.barrier(qr) - - # add measurements - end_index = nqubits - for qubit_list, list_size in zip(mit_pattern, qubits_list_sizes): - - for qind in range(list_size): - qc_circuit.measure(qr[qubit_list[qind]], cr[nqubits - (end_index - qind)]) - - end_index -= list_size - - cal_circuits.append(qc_circuit) - - return cal_circuits, mit_pattern diff --git a/qiskit/utils/mitigation/fitters.py b/qiskit/utils/mitigation/fitters.py deleted file mode 100644 index 65357d5dc5b5..000000000000 --- a/qiskit/utils/mitigation/fitters.py +++ /dev/null @@ -1,491 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# This code was originally copied from the qiskit-ignis see: -# https://github.com/Qiskit/qiskit-ignis/blob/b91066c72171bcd55a70e6e8993b813ec763cf41/qiskit/ignis/mitigation/measurement/fitters.py -# it was migrated as qiskit-ignis is being deprecated - - -""" -Measurement correction fitters. -""" -from typing import List -import copy -import re - -import numpy as np - -from qiskit import QiskitError -from qiskit.utils.mitigation.circuits import count_keys -from qiskit.utils.mitigation._filters import MeasurementFilter, TensoredFilter -from qiskit.utils.deprecation import deprecate_func - - -class CompleteMeasFitter: - """ - Deprecated: Measurement correction fitter for a full calibration - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", - ) - def __init__( - self, - results, - state_labels: List[str], - qubit_list: List[int] = None, - circlabel: str = "", - ): - """ - Initialize a measurement calibration matrix from the results of running - the circuits returned by `measurement_calibration_circuits` - - A wrapper for the tensored fitter - - .. warning:: - - This class is not a public API. The internals are not stable and will - likely change. It is used solely for the - ``measurement_error_mitigation_cls`` kwarg of the - :class:`~qiskit.utils.QuantumInstance` class's constructor (as - a class not an instance). Anything outside of that usage does - not have the normal user-facing API stability. - - Args: - results: the results of running the measurement calibration - circuits. If this is `None` the user will set a calibration - matrix later. - state_labels: list of calibration state labels - returned from `measurement_calibration_circuits`. - The output matrix will obey this ordering. - qubit_list: List of the qubits (for reference and if the - subset is needed). If `None`, the qubit_list will be - created according to the length of state_labels[0]. - circlabel: if the qubits were labeled. - """ - if qubit_list is None: - qubit_list = range(len(state_labels[0])) - self._qubit_list = qubit_list - - self._tens_fitt = TensoredMeasFitter(results, [qubit_list], [state_labels], circlabel) - - @property - def cal_matrix(self): - """Return cal_matrix.""" - return self._tens_fitt.cal_matrices[0] - - @cal_matrix.setter - def cal_matrix(self, new_cal_matrix): - """set cal_matrix.""" - self._tens_fitt.cal_matrices = [copy.deepcopy(new_cal_matrix)] - - @property - def state_labels(self): - """Return state_labels.""" - return self._tens_fitt.substate_labels_list[0] - - @property - def qubit_list(self): - """Return list of qubits.""" - return self._qubit_list - - @state_labels.setter - def state_labels(self, new_state_labels): - """Set state label.""" - self._tens_fitt.substate_labels_list[0] = new_state_labels - - @property - def filter(self): - """Return a measurement filter using the cal matrix.""" - return MeasurementFilter(self.cal_matrix, self.state_labels) - - def add_data(self, new_results, rebuild_cal_matrix=True): - """ - Add measurement calibration data - - Args: - new_results (list or qiskit.result.Result): a single result or list - of result objects. - rebuild_cal_matrix (bool): rebuild the calibration matrix - """ - - self._tens_fitt.add_data(new_results, rebuild_cal_matrix) - - def subset_fitter(self, qubit_sublist): - """ - Return a fitter object that is a subset of the qubits in the original - list. - - Args: - qubit_sublist (list): must be a subset of qubit_list - - Returns: - CompleteMeasFitter: A new fitter that has the calibration for a - subset of qubits - - Raises: - QiskitError: If the calibration matrix is not initialized - """ - - if self._tens_fitt.cal_matrices is None: - raise QiskitError("Calibration matrix is not initialized") - - if qubit_sublist is None: - raise QiskitError("Qubit sublist must be specified") - - for qubit in qubit_sublist: - if qubit not in self._qubit_list: - raise QiskitError("Qubit not in the original set of qubits") - - # build state labels - new_state_labels = count_keys(len(qubit_sublist)) - - # mapping between indices in the state_labels and the qubits in - # the sublist - qubit_sublist_ind = [] - for sqb in qubit_sublist: - for qbind, qubit in enumerate(self._qubit_list): - if qubit == sqb: - qubit_sublist_ind.append(qbind) - - # states in the full calibration which correspond - # to the reduced labels - q_q_mapping = [] - state_labels_reduced = [] - for label in self.state_labels: - tmplabel = [label[index] for index in qubit_sublist_ind] - state_labels_reduced.append("".join(tmplabel)) - - for sub_lab_ind, _ in enumerate(new_state_labels): - q_q_mapping.append([]) - for labelind, label in enumerate(state_labels_reduced): - if label == new_state_labels[sub_lab_ind]: - q_q_mapping[-1].append(labelind) - - new_fitter = CompleteMeasFitter( - results=None, state_labels=new_state_labels, qubit_list=qubit_sublist - ) - - new_cal_matrix = np.zeros([len(new_state_labels), len(new_state_labels)]) - - # do a partial trace - for i in range(len(new_state_labels)): - for j in range(len(new_state_labels)): - - for q_q_i_map in q_q_mapping[i]: - for q_q_j_map in q_q_mapping[j]: - new_cal_matrix[i, j] += self.cal_matrix[q_q_i_map, q_q_j_map] - - new_cal_matrix[i, j] /= len(q_q_mapping[i]) - - new_fitter.cal_matrix = new_cal_matrix - - return new_fitter - - def readout_fidelity(self, label_list=None): - """ - Based on the results, output the readout fidelity which is the - normalized trace of the calibration matrix - - Args: - label_list (bool): If `None`, returns the average assignment fidelity - of a single state. Otherwise it returns the assignment fidelity - to be in any one of these states averaged over the second - index. - - Returns: - numpy.array: readout fidelity (assignment fidelity) - - Additional Information: - The on-diagonal elements of the calibration matrix are the - probabilities of measuring state 'x' given preparation of state - 'x' and so the normalized trace is the average assignment fidelity - """ - return self._tens_fitt.readout_fidelity(0, label_list) - - -class TensoredMeasFitter: - """ - Deprecated: Measurement correction fitter for a tensored calibration. - """ - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", - ) - def __init__( - self, - results, - mit_pattern: List[List[int]], - substate_labels_list: List[List[str]] = None, - circlabel: str = "", - ): - """ - Initialize a measurement calibration matrix from the results of running - the circuits returned by `measurement_calibration_circuits`. - - .. warning:: - - This class is not a public API. The internals are not stable and will - likely change. It is used solely for the - ``measurement_error_mitigation_cls`` kwarg of the - :class:`~qiskit.utils.QuantumInstance` class's constructor (as - a class not an instance). Anything outside of that usage does - not have the normal user-facing API stability. - - Args: - results: the results of running the measurement calibration - circuits. If this is `None`, the user will set calibration - matrices later. - - mit_pattern: qubits to perform the - measurement correction on, divided to groups according to - tensors - - substate_labels_list: for each - calibration matrix, the labels of its rows and columns. - If `None`, the labels are ordered lexicographically - - circlabel: if the qubits were labeled - - Raises: - ValueError: if the mit_pattern doesn't match the - substate_labels_list - """ - - self._result_list = [] - self._cal_matrices = None - self._circlabel = circlabel - self._mit_pattern = mit_pattern - - self._qubit_list_sizes = [len(qubit_list) for qubit_list in mit_pattern] - - self._indices_list = [] - if substate_labels_list is None: - self._substate_labels_list = [] - for list_size in self._qubit_list_sizes: - self._substate_labels_list.append(count_keys(list_size)) - else: - self._substate_labels_list = substate_labels_list - if len(self._qubit_list_sizes) != len(substate_labels_list): - raise ValueError("mit_pattern does not match substate_labels_list") - - self._indices_list = [] - for _, sub_labels in enumerate(self._substate_labels_list): - self._indices_list.append({lab: ind for ind, lab in enumerate(sub_labels)}) - - self.add_data(results) - - @property - def cal_matrices(self): - """Return cal_matrices.""" - return self._cal_matrices - - @cal_matrices.setter - def cal_matrices(self, new_cal_matrices): - """Set _cal_matrices.""" - self._cal_matrices = copy.deepcopy(new_cal_matrices) - - @property - def substate_labels_list(self): - """Return _substate_labels_list.""" - return self._substate_labels_list - - @property - def filter(self): - """Return a measurement filter using the cal matrices.""" - return TensoredFilter(self._cal_matrices, self._substate_labels_list, self._mit_pattern) - - @property - def nqubits(self): - """Return _qubit_list_sizes.""" - return sum(self._qubit_list_sizes) - - def add_data(self, new_results, rebuild_cal_matrix=True): - """ - Add measurement calibration data - - Args: - new_results (list or qiskit.result.Result): a single result or list - of Result objects. - rebuild_cal_matrix (bool): rebuild the calibration matrix - """ - - if new_results is None: - return - - if not isinstance(new_results, list): - new_results = [new_results] - - for result in new_results: - self._result_list.append(result) - - if rebuild_cal_matrix: - self._build_calibration_matrices() - - def readout_fidelity(self, cal_index=0, label_list=None): - """ - Based on the results, output the readout fidelity, which is the average - of the diagonal entries in the calibration matrices. - - Args: - cal_index(integer): readout fidelity for this index in _cal_matrices - label_list (list): Returns the average fidelity over of the groups - f states. In the form of a list of lists of states. If `None`, - then each state used in the construction of the calibration - matrices forms a group of size 1 - - Returns: - numpy.array: The readout fidelity (assignment fidelity) - - Raises: - QiskitError: If the calibration matrix has not been set for the - object. - - Additional Information: - The on-diagonal elements of the calibration matrices are the - probabilities of measuring state 'x' given preparation of state - 'x'. - """ - - if self._cal_matrices is None: - raise QiskitError("Cal matrix has not been set") - - if label_list is None: - label_list = [[label] for label in self._substate_labels_list[cal_index]] - - state_labels = self._substate_labels_list[cal_index] - fidelity_label_list = [] - if label_list is None: - fidelity_label_list = [[label] for label in state_labels] - else: - for fid_sublist in label_list: - fidelity_label_list.append([]) - for fid_statelabl in fid_sublist: - for label_idx, label in enumerate(state_labels): - if fid_statelabl == label: - fidelity_label_list[-1].append(label_idx) - continue - - # fidelity_label_list is a 2D list of indices in the - # cal_matrix, we find the assignment fidelity of each - # row and average over the list - assign_fid_list = [] - - for fid_label_sublist in fidelity_label_list: - assign_fid_list.append(0) - for state_idx_i in fid_label_sublist: - for state_idx_j in fid_label_sublist: - assign_fid_list[-1] += self._cal_matrices[cal_index][state_idx_i][state_idx_j] - assign_fid_list[-1] /= len(fid_label_sublist) - - return np.mean(assign_fid_list) - - def _build_calibration_matrices(self): - """ - Build the measurement calibration matrices from the results of running - the circuits returned by `measurement_calibration`. - """ - - # initialize the set of empty calibration matrices - self._cal_matrices = [] - for list_size in self._qubit_list_sizes: - self._cal_matrices.append(np.zeros([2**list_size, 2**list_size], dtype=float)) - - # go through for each calibration experiment - for result in self._result_list: - for experiment in result.results: - circ_name = experiment.header.name - # extract the state from the circuit name - # this was the prepared state - circ_search = re.search("(?<=" + self._circlabel + "cal_)\\w+", circ_name) - - # this experiment is not one of the calcs so skip - if circ_search is None: - continue - - state = circ_search.group(0) - - # get the counts from the result - state_cnts = result.get_counts(circ_name) - for measured_state, counts in state_cnts.items(): - end_index = self.nqubits - for cal_ind, cal_mat in enumerate(self._cal_matrices): - - start_index = end_index - self._qubit_list_sizes[cal_ind] - - substate_index = self._indices_list[cal_ind][state[start_index:end_index]] - measured_substate_index = self._indices_list[cal_ind][ - measured_state[start_index:end_index] - ] - end_index = start_index - - cal_mat[measured_substate_index][substate_index] += counts - - for mat_index, _ in enumerate(self._cal_matrices): - sums_of_columns = np.sum(self._cal_matrices[mat_index], axis=0) - self._cal_matrices[mat_index] = np.divide( - self._cal_matrices[mat_index], - sums_of_columns, - out=np.zeros_like(self._cal_matrices[mat_index]), - where=sums_of_columns != 0, - ) - - def subset_fitter(self, qubit_sublist): - """Return a fitter object that is a subset of the qubits in the original list. - - This is only a partial implementation of the ``subset_fitter`` method since only - mitigation patterns of length 1 are supported. This corresponds to patterns of the - form ``[[0], [1], [2], ...]``. Note however, that such patterns are a good first - approximation to mitigate readout errors on large quantum circuits. - - Args: - qubit_sublist (list): must be a subset of qubit_list - - Returns: - TensoredMeasFitter: A new fitter that has the calibration for a - subset of qubits - - Raises: - QiskitError: If the calibration matrix is not initialized - QiskitError: If the mit pattern is not a tensor of single-qubit - measurement error mitigation. - QiskitError: If a qubit in the given ``qubit_sublist`` is not in the list of - qubits in the mit. pattern. - """ - if self._cal_matrices is None: - raise QiskitError("Calibration matrices are not initialized.") - - if qubit_sublist is None: - raise QiskitError("Qubit sublist must be specified.") - - if not all(len(tensor) == 1 for tensor in self._mit_pattern): - raise QiskitError( - f"Each element in the mit pattern should have length 1. Found {self._mit_pattern}." - ) - - supported_qubits = {tensor[0] for tensor in self._mit_pattern} - for qubit in qubit_sublist: - if qubit not in supported_qubits: - raise QiskitError(f"Qubit {qubit} is not in the mit pattern {self._mit_pattern}.") - - new_mit_pattern = [[idx] for idx in qubit_sublist] - new_substate_labels_list = [self._substate_labels_list[idx] for idx in qubit_sublist] - - new_fitter = TensoredMeasFitter( - results=None, mit_pattern=new_mit_pattern, substate_labels_list=new_substate_labels_list - ) - - new_fitter.cal_matrices = [self._cal_matrices[idx] for idx in qubit_sublist] - - return new_fitter diff --git a/qiskit/utils/name_unnamed_args.py b/qiskit/utils/name_unnamed_args.py deleted file mode 100644 index 4e153dcfefd2..000000000000 --- a/qiskit/utils/name_unnamed_args.py +++ /dev/null @@ -1,73 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tool to name unnamed arguments.""" - -import functools - - -def name_args(mapping, skip=0): - """Decorator to convert unnamed arguments to named ones. - - Can be used to deprecate old signatures of a function, e.g. - - .. code-block:: - - old_f(a: TypeA, b: TypeB, c: TypeC) - new_f(a: TypeA, d: TypeD, b: TypeB=None, c: TypeC=None) - - Then, to support the old signature this decorator can be used as - - .. code-block:: - - @name_args([ - ('a'), # stays the same - ('d', {TypeB: 'b'}), # if arg is of type TypeB, call if 'b' else 'd' - ('b', {TypeC: 'c'}) - ]) - def new_f(a: TypeA, d: TypeD, b: TypeB=None, c: TypeC=None): - if b is not None: - # raise warning, this is deprecated! - if c is not None: - # raise warning, this is deprecated! - - """ - - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - # turn args into kwargs - for arg, replacement in zip(args[skip:], mapping): - default_name = replacement[0] - if len(replacement) == 1: # just renaming, no special cases - if default_name in kwargs: - raise ValueError(f"Name collapse on {default_name}") - kwargs[default_name] = arg - else: - # check if we find a special name - name = None - for special_type, special_name in replacement[1].items(): - if isinstance(arg, special_type): - name = special_name - break - if name is None: - name = default_name - - if name in kwargs: - raise ValueError(f"Name collapse on {default_name}") - kwargs[name] = arg - - return func(*args[:skip], **kwargs) - - return wrapper - - return decorator diff --git a/qiskit/utils/optionals.py b/qiskit/utils/optionals.py index 0321a1cbc7e5..cb69c6b5136c 100644 --- a/qiskit/utils/optionals.py +++ b/qiskit/utils/optionals.py @@ -103,7 +103,7 @@ `__ library as a core dependency, and during the change-over period, it was sometimes convenient to convert things into the Python-only `NetworkX `__ format. Some tests of application modules, such as - `Qiskit Nature `__ still use NetworkX. + `Qiskit Nature `__ still use NetworkX. * - .. py:data:: HAS_NLOPT - `NLopt `__ is a nonlinear optimization library, @@ -212,7 +212,7 @@ """ # NOTE: If you're changing this file, sync it with `requirements-optional.txt` and potentially -# `setup.py` as well. +# `pyproject.toml` as well. import logging as _logging diff --git a/qiskit/utils/quantum_instance.py b/qiskit/utils/quantum_instance.py deleted file mode 100644 index 94c1bed535d4..000000000000 --- a/qiskit/utils/quantum_instance.py +++ /dev/null @@ -1,946 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Quantum Instance module""" - -from typing import Optional, List, Union, Dict, Callable, Tuple -from enum import Enum -import copy -import logging -import time -import warnings - -import numpy as np - -from qiskit.qobj import QasmQobj, PulseQobj -from qiskit.utils import circuit_utils -from qiskit.exceptions import QiskitError -from qiskit.utils.backend_utils import ( - is_ibmq_provider, - is_statevector_backend, - is_simulator_backend, - is_local_backend, - is_basicaer_provider, - support_backend_options, - _get_backend_provider, - _get_backend_interface_version, -) -from qiskit.utils.mitigation import ( - CompleteMeasFitter, - TensoredMeasFitter, -) -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) - - -class _MeasFitterType(Enum): - """Meas Fitter Type.""" - - COMPLETE_MEAS_FITTER = 0 - TENSORED_MEAS_FITTER = 1 - - @staticmethod - def type_from_class(meas_class): - """ - Returns fitter type from class - """ - if meas_class == CompleteMeasFitter: - return _MeasFitterType.COMPLETE_MEAS_FITTER - elif meas_class == TensoredMeasFitter: - return _MeasFitterType.TENSORED_MEAS_FITTER - try: - from qiskit.ignis.mitigation.measurement import ( - CompleteMeasFitter as CompleteMeasFitter_IG, - TensoredMeasFitter as TensoredMeasFitter_IG, - ) - except ImportError: - pass - if meas_class == CompleteMeasFitter_IG: - warnings.warn( - "The use of qiskit-ignis for measurement mitigation is " - "deprecated and will be removed in a future release. Instead " - "use the CompleteMeasFitter class from qiskit.utils.mitigation", - DeprecationWarning, - stacklevel=3, - ) - return _MeasFitterType.COMPLETE_MEAS_FITTER - elif meas_class == TensoredMeasFitter_IG: - warnings.warn( - "The use of qiskit-ignis for measurement mitigation is " - "deprecated and will be removed in a future release. Instead " - "use the TensoredMeasFitter class from qiskit.utils.mitigation", - DeprecationWarning, - stacklevel=3, - ) - return _MeasFitterType.TENSORED_MEAS_FITTER - else: - raise QiskitError(f"Unknown fitter {meas_class}") - - @staticmethod - def type_from_instance(meas_instance): - """ - Returns fitter type from instance - """ - if isinstance(meas_instance, CompleteMeasFitter): - return _MeasFitterType.COMPLETE_MEAS_FITTER - elif isinstance(meas_instance, TensoredMeasFitter): - return _MeasFitterType.TENSORED_MEAS_FITTER - try: - from qiskit.ignis.mitigation.measurement import ( - CompleteMeasFitter as CompleteMeasFitter_IG, - TensoredMeasFitter as TensoredMeasFitter_IG, - ) - except ImportError: - pass - if isinstance(meas_instance, CompleteMeasFitter_IG): - warnings.warn( - "The use of qiskit-ignis for measurement mitigation is " - "deprecated and will be removed in a future release. Instead " - "use the CompleteMeasFitter class from qiskit.utils.mitigation", - DeprecationWarning, - stacklevel=3, - ) - return _MeasFitterType.COMPLETE_MEAS_FITTER - elif isinstance(meas_instance, TensoredMeasFitter_IG): - warnings.warn( - "The use of qiskit-ignis for measurement mitigation is " - "deprecated and will be removed in a future release. Instead " - "use the TensoredMeasFitter class from qiskit.utils.mitigation", - DeprecationWarning, - stacklevel=3, - ) - return _MeasFitterType.TENSORED_MEAS_FITTER - else: - raise QiskitError(f"Unknown fitter {meas_instance}") - - -class QuantumInstance: - """Deprecated: Quantum Backend including execution setting.""" - - _BACKEND_CONFIG = ["basis_gates", "coupling_map"] - _COMPILE_CONFIG = ["initial_layout", "seed_transpiler", "optimization_level"] - _RUN_CONFIG = ["shots", "memory", "seed_simulator"] - _QJOB_CONFIG = ["timeout", "wait"] - _NOISE_CONFIG = ["noise_model"] - - # https://github.com/Qiskit/qiskit-aer/blob/master/qiskit/providers/aer/backends/qasm_simulator.py - _BACKEND_OPTIONS_QASM_ONLY = ["statevector_sample_measure_opt", "max_parallel_shots"] - _BACKEND_OPTIONS = [ - "initial_statevector", - "chop_threshold", - "max_parallel_threads", - "max_parallel_experiments", - "statevector_parallel_threshold", - "statevector_hpc_gate_opt", - ] + _BACKEND_OPTIONS_QASM_ONLY - - @deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", - ) - def __init__( - self, - backend, - # run config - shots: Optional[int] = None, - seed_simulator: Optional[int] = None, - # backend properties - basis_gates: Optional[List[str]] = None, - coupling_map=None, - # transpile - initial_layout=None, - pass_manager=None, - bound_pass_manager=None, - seed_transpiler: Optional[int] = None, - optimization_level: Optional[int] = None, - # simulation - backend_options: Optional[Dict] = None, - noise_model=None, - # job - timeout: Optional[float] = None, - wait: float = 5.0, - # others - skip_qobj_validation: bool = True, - measurement_error_mitigation_cls: Optional[Callable] = None, - cals_matrix_refresh_period: int = 30, - measurement_error_mitigation_shots: Optional[int] = None, - job_callback: Optional[Callable] = None, - mit_pattern: Optional[List[List[int]]] = None, - max_job_retries: int = 50, - ) -> None: - """ - Quantum Instance holds a Qiskit Terra backend as well as configuration for circuit - transpilation and execution. When provided to an Aqua algorithm the algorithm will - execute the circuits it needs to run using the instance. - - Args: - backend (Backend): Instance of selected backend - shots: Number of repetitions of each circuit, for sampling. If None, the shots are - extracted from the backend. If the backend has none set, the default is 1024. - seed_simulator: Random seed for simulators - basis_gates: List of basis gate names supported by the - target. Defaults to basis gates of the backend. - coupling_map (Optional[Union['CouplingMap', List[List]]]): - Coupling map (perhaps custom) to target in mapping - initial_layout (Optional[Union['Layout', Dict, List]]): - Initial layout of qubits in mapping - pass_manager (Optional['PassManager']): Pass manager to handle how to compile the circuits. - To run only this pass manager and not the ``bound_pass_manager``, call the - :meth:`~qiskit.utils.QuantumInstance.transpile` method with the argument - ``pass_manager=quantum_instance.unbound_pass_manager``. - bound_pass_manager (Optional['PassManager']): A second pass manager to apply on bound - circuits only, that is, circuits without any free parameters. To only run this pass - manager and not ``pass_manager`` call the - :meth:`~qiskit.utils.QuantumInstance.transpile` method with the argument - ``pass_manager=quantum_instance.bound_pass_manager``. - manager should also be run. - seed_transpiler: The random seed for circuit mapper - optimization_level: How much optimization to perform on the circuits. - Higher levels generate more optimized circuits, at the expense of longer - transpilation time. - backend_options: All running options for backend, please refer - to the provider of the backend for information as to what options it supports. - noise_model (Optional['NoiseModel']): noise model for simulator - timeout: Seconds to wait for job. If None, wait indefinitely. - wait: Seconds between queries for job result - skip_qobj_validation: Bypass Qobj validation to decrease circuit - processing time during submission to backend. - measurement_error_mitigation_cls: The approach to mitigate - measurement errors. The classes :class:`~qiskit.utils.mitigation.CompleteMeasFitter` - or :class:`~qiskit.utils.mitigation.TensoredMeasFitter` from the - :mod:`qiskit.utils.mitigation` module can be used here as exact values, not - instances. ``TensoredMeasFitter`` doesn't support the ``subset_fitter`` method. - cals_matrix_refresh_period: How often to refresh the calibration - matrix in measurement mitigation. in minutes - measurement_error_mitigation_shots: The number of shots number for - building calibration matrix. If None, the main `shots` parameter value is used. - job_callback: Optional user supplied callback which can be used - to monitor job progress as jobs are submitted for processing by an Aqua algorithm. - The callback is provided the following arguments: `job_id, job_status, - queue_position, job` - mit_pattern: Qubits on which to perform the TensoredMeasFitter - measurement correction, divided to groups according to tensors. - If `None` and `qr` is given then assumed to be performed over the entire - `qr` as one group (default `None`). - max_job_retries(int): positive non-zero number of trials for the job set (-1 for - infinite trials) (default: 50) - - Raises: - QiskitError: the shots exceeds the maximum number of shots - QiskitError: set noise model but the backend does not support that - QiskitError: set backend_options but the backend does not support that - """ - self._backend = backend - self._backend_interface_version = _get_backend_interface_version(self._backend) - self._pass_manager = pass_manager - self._bound_pass_manager = bound_pass_manager - - # if the shots are none, try to get them from the backend - if shots is None: - from qiskit.providers.backend import Backend # pylint: disable=cyclic-import - - if isinstance(backend, Backend): - if hasattr(backend, "options"): # should always be true for V1 - backend_shots = backend.options.get("shots", 1024) - if shots != backend_shots: - logger.info( - "Overwriting the number of shots in the quantum instance with " - "the settings from the backend." - ) - shots = backend_shots - - # safeguard if shots are still not set - if shots is None: - shots = 1024 - - # pylint: disable=cyclic-import - from qiskit.assembler.run_config import RunConfig - - run_config = RunConfig(shots=shots) - if seed_simulator is not None: - run_config.seed_simulator = seed_simulator - - self._run_config = run_config - - # setup backend config - if self._backend_interface_version <= 1: - basis_gates = basis_gates or backend.configuration().basis_gates - coupling_map = coupling_map or getattr(backend.configuration(), "coupling_map", None) - self._backend_config = {"basis_gates": basis_gates, "coupling_map": coupling_map} - else: - self._backend_config = {} - - # setup compile config - self._compile_config = { - "initial_layout": initial_layout, - "seed_transpiler": seed_transpiler, - "optimization_level": optimization_level, - } - - # setup job config - self._qjob_config = ( - {"timeout": timeout} if self.is_local else {"timeout": timeout, "wait": wait} - ) - - # setup noise config - self._noise_config = {} - if noise_model is not None: - if is_simulator_backend(self._backend) and not is_basicaer_provider(self._backend): - self._noise_config = {"noise_model": noise_model} - else: - raise QiskitError( - "The noise model is not supported " - "on the selected backend {} ({}) " - "only certain backends, such as Aer qasm simulator " - "support noise.".format(self.backend_name, _get_backend_provider(self._backend)) - ) - - # setup backend options for run - self._backend_options = {} - if backend_options is not None: - if support_backend_options(self._backend): - self._backend_options = {"backend_options": backend_options} - else: - raise QiskitError( - "backend_options can not used with the backends in IBMQ provider." - ) - - # setup measurement error mitigation - self._meas_error_mitigation_cls = None - if self.is_statevector: - if measurement_error_mitigation_cls is not None: - raise QiskitError( - "Measurement error mitigation does not work with the statevector simulation." - ) - else: - self._meas_error_mitigation_cls = measurement_error_mitigation_cls - self._meas_error_mitigation_fitters: Dict[str, Tuple[np.ndarray, float]] = {} - # TODO: support different fitting method in error mitigation? - self._meas_error_mitigation_method = "least_squares" - self._cals_matrix_refresh_period = cals_matrix_refresh_period - self._meas_error_mitigation_shots = measurement_error_mitigation_shots - self._mit_pattern = mit_pattern - - if self._meas_error_mitigation_cls is not None: - logger.info( - "The measurement error mitigation is enabled. " - "It will automatically submit an additional job to help " - "calibrate the result of other jobs. " - "The current approach will submit a job with 2^N circuits " - "to build the calibration matrix, " - "where N is the number of measured qubits. " - "Furthermore, Aqua will re-use the calibration matrix for %s minutes " - "and re-build it after that.", - self._cals_matrix_refresh_period, - ) - - # setup others - if is_ibmq_provider(self._backend): - if skip_qobj_validation: - logger.info( - "skip_qobj_validation was set True but this setting is not " - "supported by IBMQ provider and has been ignored." - ) - skip_qobj_validation = False - self._skip_qobj_validation = skip_qobj_validation - self._circuit_summary = False - self._job_callback = job_callback - self._time_taken = 0.0 - self._max_job_retries = max_job_retries - logger.info(self) - - def __str__(self) -> str: - """Overload string. - - Returns: - str: the info of the object. - """ - from qiskit import __version__ as terra_version - - info = f"\nQiskit Terra version: {terra_version}\n" - info += "Backend: '{} ({})', with following setting:\n{}\n{}\n{}\n{}\n{}\n{}".format( - self.backend_name, - _get_backend_provider(self._backend), - self._backend_config, - self._compile_config, - self._run_config, - self._qjob_config, - self._backend_options, - self._noise_config, - ) - - info += f"\nMeasurement mitigation: {self._meas_error_mitigation_cls}" - - return info - - @property - def unbound_pass_manager(self): - """Return the pass manager for designated for unbound circuits. - - Returns: - Optional['PassManager']: The pass manager for unbound circuits, if it has been set. - """ - return self._pass_manager - - @property - def bound_pass_manager(self): - """Return the pass manager for designated for bound circuits. - - Returns: - Optional['PassManager']: The pass manager for bound circuits, if it has been set. - """ - return self._bound_pass_manager - - def transpile(self, circuits, pass_manager=None): - """A wrapper to transpile circuits to allow algorithm access the transpiled circuits. - - Args: - circuits (Union['QuantumCircuit', List['QuantumCircuit']]): circuits to transpile - pass_manager (Optional['PassManager']): A pass manager to transpile the circuits. If - none is given, but either ``pass_manager`` or ``bound_pass_manager`` has been set - in the initializer, these are run. If none has been provided there either, the - backend and compile configs from the initializer are used. - - Returns: - List['QuantumCircuit']: The transpiled circuits, it is always a list even though - the length is one. - """ - # pylint: disable=cyclic-import - from qiskit import compiler - from qiskit.transpiler import PassManager - - # if no pass manager here is given, check if they have been set in the init - if pass_manager is None: - # if they haven't been set in the init, use the transpile args from the init - if self._pass_manager is None and self._bound_pass_manager is None: - transpiled_circuits = compiler.transpile( - circuits, self._backend, **self._backend_config, **self._compile_config - ) - # it they have been set, run them - else: - pass_manager = PassManager() - if self._pass_manager is not None: - pass_manager += self._pass_manager # check if None - if self._bound_pass_manager is not None: - pass_manager += self._bound_pass_manager - - transpiled_circuits = pass_manager.run(circuits) - # custom pass manager set by user - else: - transpiled_circuits = pass_manager.run(circuits) - - if not isinstance(transpiled_circuits, list): - transpiled_circuits = [transpiled_circuits] - - if logger.isEnabledFor(logging.DEBUG) and self._circuit_summary: - logger.debug("==== Before transpiler ====") - logger.debug(circuit_utils.summarize_circuits(circuits)) - if transpiled_circuits is not None: - logger.debug("==== After transpiler ====") - logger.debug(circuit_utils.summarize_circuits(transpiled_circuits)) - - return transpiled_circuits - - def assemble(self, circuits) -> Union[QasmQobj, PulseQobj]: - """assemble circuits""" - # pylint: disable=cyclic-import - from qiskit import compiler - - return compiler.assemble(circuits, **self._run_config.to_dict()) - - def execute(self, circuits, had_transpiled: bool = False): - """ - A wrapper to interface with quantum backend. - - Args: - circuits (Union['QuantumCircuit', List['QuantumCircuit']]): - circuits to execute - had_transpiled: whether or not circuits had been transpiled - - Raises: - QiskitError: Invalid error mitigation fitter class - QiskitError: TensoredMeasFitter class doesn't support subset fitter - MissingOptionalLibraryError: Ignis not installed - - - Returns: - Result: result object - - TODO: Maybe we can combine the circuits for the main ones and calibration circuits before - assembling to the qobj. - """ - from qiskit.utils.run_circuits import run_circuits - from qiskit.utils.measurement_error_mitigation import ( - get_measured_qubits, - build_measurement_error_mitigation_circuits, - ) - - if had_transpiled: - # Convert to a list or make a copy. - # The measurement mitigation logic expects a list and - # may change it in place. This makes sure that logic works - # and any future logic that may change the input. - # It also makes the code easier: it will always deal with a list. - if isinstance(circuits, list): - circuits = circuits.copy() - else: - circuits = [circuits] - else: - # transpile here, the method always returns a copied list - circuits = self.transpile(circuits) - - if self.is_statevector and "aer_simulator_statevector" in self.backend_name: - try: - from qiskit.providers.aer.library import SaveStatevector - - def _find_save_state(data): - for instruction in reversed(data): - if isinstance(instruction.operation, SaveStatevector): - return True - return False - - if isinstance(circuits, list): - for circuit in circuits: - if not _find_save_state(circuit.data): - circuit.save_statevector() - else: - if not _find_save_state(circuits.data): - circuits.save_statevector() - except ImportError: - pass - - if self._meas_error_mitigation_cls is not None: - qubit_index, qubit_mappings = get_measured_qubits(circuits) - mit_pattern = self._mit_pattern - if mit_pattern is None: - mit_pattern = [[i] for i in range(len(qubit_index))] - qubit_index_str = "_".join([str(x) for x in qubit_index]) + "_{}".format( - self._meas_error_mitigation_shots or self._run_config.shots - ) - meas_error_mitigation_fitter, timestamp = self._meas_error_mitigation_fitters.get( - qubit_index_str, (None, 0.0) - ) - - if meas_error_mitigation_fitter is None: - # check the asked qubit_index are the subset of build matrices - for key, _ in self._meas_error_mitigation_fitters.items(): - stored_qubit_index = [int(x) for x in key.split("_")[:-1]] - stored_shots = int(key.split("_")[-1]) - if len(qubit_index) < len(stored_qubit_index): - tmp = list(set(qubit_index + stored_qubit_index)) - if ( - sorted(tmp) == sorted(stored_qubit_index) - and self._run_config.shots == stored_shots - ): - # the qubit used in current job is the subset and shots are the same - ( - meas_error_mitigation_fitter, - timestamp, - ) = self._meas_error_mitigation_fitters.get(key, (None, 0.0)) - meas_error_mitigation_fitter = ( - meas_error_mitigation_fitter.subset_fitter( - qubit_sublist=qubit_index - ) - ) - logger.info( - "The qubits used in the current job is the subset of " - "previous jobs, " - "reusing the calibration matrix if it is not out-of-date." - ) - - build_cals_matrix = ( - self.maybe_refresh_cals_matrix(timestamp) or meas_error_mitigation_fitter is None - ) - - cal_circuits = None - prepended_calibration_circuits: int = 0 - if build_cals_matrix: - logger.info("Updating to also run measurement error mitigation.") - use_different_shots = not ( - self._meas_error_mitigation_shots is None - or self._meas_error_mitigation_shots == self._run_config.shots - ) - temp_run_config = copy.deepcopy(self._run_config) - if use_different_shots: - temp_run_config.shots = self._meas_error_mitigation_shots - ( - cal_circuits, - state_labels, - circuit_labels, - ) = build_measurement_error_mitigation_circuits( - qubit_index, - self._meas_error_mitigation_cls, - self._backend, - self._backend_config, - self._compile_config, - mit_pattern=mit_pattern, - ) - if use_different_shots: - cals_result = run_circuits( - cal_circuits, - self._backend, - qjob_config=self._qjob_config, - backend_options=self._backend_options, - noise_config=self._noise_config, - run_config=self._run_config.to_dict(), - job_callback=self._job_callback, - max_job_retries=self._max_job_retries, - ) - self._time_taken += cals_result.time_taken - result = run_circuits( - circuits, - self._backend, - qjob_config=self.qjob_config, - backend_options=self.backend_options, - noise_config=self._noise_config, - run_config=self.run_config.to_dict(), - job_callback=self._job_callback, - max_job_retries=self._max_job_retries, - ) - self._time_taken += result.time_taken - else: - circuits[0:0] = cal_circuits - prepended_calibration_circuits = len(cal_circuits) - if hasattr(self.run_config, "parameterizations"): - cal_run_config = copy.deepcopy(self.run_config) - cal_run_config.parameterizations[0:0] = [[]] * len(cal_circuits) - else: - cal_run_config = self.run_config - result = run_circuits( - circuits, - self._backend, - qjob_config=self.qjob_config, - backend_options=self.backend_options, - noise_config=self._noise_config, - run_config=cal_run_config.to_dict(), - job_callback=self._job_callback, - max_job_retries=self._max_job_retries, - ) - self._time_taken += result.time_taken - cals_result = result - logger.info("Building calibration matrix for measurement error mitigation.") - meas_type = _MeasFitterType.type_from_class(self._meas_error_mitigation_cls) - if meas_type == _MeasFitterType.COMPLETE_MEAS_FITTER: - meas_error_mitigation_fitter = self._meas_error_mitigation_cls( - cals_result, state_labels, qubit_list=qubit_index, circlabel=circuit_labels - ) - elif meas_type == _MeasFitterType.TENSORED_MEAS_FITTER: - meas_error_mitigation_fitter = self._meas_error_mitigation_cls( - cals_result, mit_pattern=state_labels, circlabel=circuit_labels - ) - self._meas_error_mitigation_fitters[qubit_index_str] = ( - meas_error_mitigation_fitter, - time.time(), - ) - else: - result = run_circuits( - circuits, - self._backend, - qjob_config=self.qjob_config, - backend_options=self.backend_options, - noise_config=self._noise_config, - run_config=self._run_config.to_dict(), - job_callback=self._job_callback, - max_job_retries=self._max_job_retries, - ) - self._time_taken += result.time_taken - - if meas_error_mitigation_fitter is not None: - logger.info("Performing measurement error mitigation.") - if ( - hasattr(self._run_config, "parameterizations") - and len(self._run_config.parameterizations) > 0 - and len(self._run_config.parameterizations[0]) > 0 - and len(self._run_config.parameterizations[0][0]) > 0 - ): - num_circuit_templates = len(self._run_config.parameterizations) - num_param_variations = len(self._run_config.parameterizations[0][0]) - num_circuits = num_circuit_templates * num_param_variations - else: - input_circuits = circuits[prepended_calibration_circuits:] - num_circuits = len(input_circuits) - skip_num_circuits = len(result.results) - num_circuits - # remove the calibration counts from result object to assure the length of - # ExperimentalResult is equal length to input circuits - result.results = result.results[skip_num_circuits:] - tmp_result = copy.deepcopy(result) - for qubit_index_str, c_idx in qubit_mappings.items(): - curr_qubit_index = [int(x) for x in qubit_index_str.split("_")] - tmp_result.results = [result.results[i] for i in c_idx] - if curr_qubit_index == qubit_index: - tmp_fitter = meas_error_mitigation_fitter - elif isinstance(meas_error_mitigation_fitter, TensoredMeasFitter): - # Different from the complete meas. fitter as only the Terra fitter - # implements the ``subset_fitter`` method. - tmp_fitter = meas_error_mitigation_fitter.subset_fitter(curr_qubit_index) - elif _MeasFitterType.COMPLETE_MEAS_FITTER == _MeasFitterType.type_from_instance( - meas_error_mitigation_fitter - ): - tmp_fitter = meas_error_mitigation_fitter.subset_fitter(curr_qubit_index) - else: - raise QiskitError( - "{} doesn't support subset_fitter.".format( - meas_error_mitigation_fitter.__class__.__name__ - ) - ) - tmp_result = tmp_fitter.filter.apply( - tmp_result, self._meas_error_mitigation_method - ) - for i, n in enumerate(c_idx): - # convert counts to integer and remove 0 values - tmp_result.results[i].data.counts = { - k: round(v) - for k, v in tmp_result.results[i].data.counts.items() - if round(v) != 0 - } - result.results[n] = tmp_result.results[i] - - else: - result = run_circuits( - circuits, - self._backend, - qjob_config=self.qjob_config, - backend_options=self.backend_options, - noise_config=self._noise_config, - run_config=self._run_config.to_dict(), - job_callback=self._job_callback, - max_job_retries=self._max_job_retries, - ) - self._time_taken += result.time_taken - - if self._circuit_summary: - self._circuit_summary = False - - return result - - def set_config(self, **kwargs): - """Set configurations for the quantum instance.""" - for k, v in kwargs.items(): - if k in QuantumInstance._RUN_CONFIG: - setattr(self._run_config, k, v) - elif k in QuantumInstance._QJOB_CONFIG: - self._qjob_config[k] = v - elif k in QuantumInstance._COMPILE_CONFIG: - self._compile_config[k] = v - elif k in QuantumInstance._BACKEND_CONFIG: - self._backend_config[k] = v - elif k in QuantumInstance._BACKEND_OPTIONS: - if not support_backend_options(self._backend): - raise QiskitError( - "backend_options can not be used with this backend " - "{} ({}).".format(self.backend_name, _get_backend_provider(self._backend)) - ) - - if k in QuantumInstance._BACKEND_OPTIONS_QASM_ONLY and self.is_statevector: - raise QiskitError( - "'{}' is only applicable for qasm simulator but " - "statevector simulator is used as the backend." - ) - - if "backend_options" not in self._backend_options: - self._backend_options["backend_options"] = {} - self._backend_options["backend_options"][k] = v - elif k in QuantumInstance._NOISE_CONFIG: - if not is_simulator_backend(self._backend) or is_basicaer_provider(self._backend): - raise QiskitError( - "The noise model is not supported on the selected backend {} ({}) " - "only certain backends, such as Aer qasm support " - "noise.".format(self.backend_name, _get_backend_provider(self._backend)) - ) - - self._noise_config[k] = v - - else: - raise ValueError(f"unknown setting for the key ({k}).") - - @property - def time_taken(self) -> float: - """Accumulated time taken for execution.""" - return self._time_taken - - def reset_execution_results(self) -> None: - """Reset execution results""" - self._time_taken = 0.0 - - @property - def qjob_config(self): - """Getter of qjob_config.""" - return self._qjob_config - - @property - def backend_config(self): - """Getter of backend_config.""" - return self._backend_config - - @property - def compile_config(self): - """Getter of compile_config.""" - return self._compile_config - - @property - def run_config(self): - """Getter of run_config.""" - return self._run_config - - @property - def noise_config(self): - """Getter of noise_config.""" - return self._noise_config - - @property - def backend_options(self): - """Getter of backend_options.""" - return self._backend_options - - @property - def circuit_summary(self): - """Getter of circuit summary.""" - return self._circuit_summary - - @circuit_summary.setter - def circuit_summary(self, new_value): - """sets circuit summary""" - self._circuit_summary = new_value - - @property - def max_job_retries(self): - """Getter of max tries""" - return self._max_job_retries - - @max_job_retries.setter - def max_job_retries(self, new_value): - """Sets the maximum tries""" - if not isinstance(new_value, int): - raise TypeError("max_job_retries parameter must be an integer") - if new_value < -1 or new_value == 0: - raise ValueError( - "max_job_retries must either be a positive integer or -1(for infinite trials)" - ) - if new_value == -1: - self._max_job_retries = int(1e18) - else: - self._max_job_retries = new_value - - @property - def measurement_error_mitigation_cls(self): - """returns measurement error mitigation cls""" - return self._meas_error_mitigation_cls - - @measurement_error_mitigation_cls.setter - def measurement_error_mitigation_cls(self, new_value): - """sets measurement error mitigation cls""" - self._meas_error_mitigation_cls = new_value - - @property - def cals_matrix_refresh_period(self): - """returns matrix refresh period""" - return self._cals_matrix_refresh_period - - @cals_matrix_refresh_period.setter - def cals_matrix_refresh_period(self, new_value): - """sets matrix refresh period""" - self._cals_matrix_refresh_period = new_value - - @property - def measurement_error_mitigation_shots(self): - """returns measurement error mitigation shots""" - return self._meas_error_mitigation_shots - - @measurement_error_mitigation_shots.setter - def measurement_error_mitigation_shots(self, new_value): - """sets measurement error mitigation shots""" - self._meas_error_mitigation_shots = new_value - - @property - def backend(self): - """Return Backend backend object.""" - return self._backend - - @property - def backend_name(self): - """Return backend name.""" - if self._backend_interface_version <= 1: - return self._backend.name() - else: - return self._backend.name - - @property - def is_statevector(self): - """Return True if backend is a statevector-type simulator.""" - return is_statevector_backend(self._backend) - - @property - def is_simulator(self): - """Return True if backend is a simulator.""" - return is_simulator_backend(self._backend) - - @property - def is_local(self): - """Return True if backend is a local backend.""" - return is_local_backend(self._backend) - - @property - def skip_qobj_validation(self): - """checks if skip qobj validation""" - return self._skip_qobj_validation - - @skip_qobj_validation.setter - def skip_qobj_validation(self, new_value): - """sets skip qobj validation flag""" - self._skip_qobj_validation = new_value - - def maybe_refresh_cals_matrix(self, timestamp: Optional[float] = None) -> bool: - """ - Calculate the time difference from the query of last time. - - Args: - timestamp: timestamp - - Returns: - Whether or not refresh the cals_matrix - """ - timestamp = timestamp or 0.0 - ret = False - curr_timestamp = time.time() - difference = int(curr_timestamp - timestamp) / 60.0 - if difference > self._cals_matrix_refresh_period: - ret = True - - return ret - - def cals_matrix( - self, qubit_index: Optional[List[int]] = None - ) -> Optional[Union[Tuple[np.ndarray, float], Dict[str, Tuple[np.ndarray, float]]]]: - """ - Get the stored calibration matrices and its timestamp. - - Args: - qubit_index: the qubit index of corresponding calibration matrix. - If None, return all stored calibration matrices. - - Returns: - The calibration matrix and the creation timestamp if qubit_index - is not None otherwise, return all matrices and their timestamp - in a dictionary. - """ - shots = self._meas_error_mitigation_shots or self._run_config.shots - if qubit_index: - qubit_index_str = "_".join([str(x) for x in qubit_index]) + f"_{shots}" - fitter, timestamp = self._meas_error_mitigation_fitters.get(qubit_index_str, None) - if fitter is not None: - return fitter.cal_matrix, timestamp - else: - return { - k: (v.cal_matrix, t) for k, (v, t) in self._meas_error_mitigation_fitters.items() - } - return None diff --git a/qiskit/utils/run_circuits.py b/qiskit/utils/run_circuits.py deleted file mode 100644 index 9714234ca694..000000000000 --- a/qiskit/utils/run_circuits.py +++ /dev/null @@ -1,411 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""run circuits functions""" - -from typing import Optional, Dict, Callable, List, Union, Tuple -import sys -import logging -import time -import copy -import os - -from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister -from qiskit.providers import Backend, JobStatus, JobError, Job -from qiskit.providers.jobstatus import JOB_FINAL_STATES -from qiskit.result import Result -from qiskit.utils.deprecation import deprecate_func -from ..exceptions import QiskitError, MissingOptionalLibraryError -from .backend_utils import ( - is_aer_provider, - is_basicaer_provider, - is_simulator_backend, - is_local_backend, - is_ibmq_provider, - _get_backend_interface_version, -) - -MAX_CIRCUITS_PER_JOB = os.environ.get("QISKIT_AQUA_MAX_CIRCUITS_PER_JOB", None) -MAX_GATES_PER_JOB = os.environ.get("QISKIT_AQUA_MAX_GATES_PER_JOB", None) - -logger = logging.getLogger(__name__) - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def find_regs_by_name( - circuit: QuantumCircuit, name: str, qreg: bool = True -) -> Optional[Union[QuantumRegister, ClassicalRegister]]: - """Deprecated: Find the registers in the circuits. - - Args: - circuit: the quantum circuit. - name: name of register - qreg: quantum or classical register - - Returns: - if not found, return None. - - """ - found_reg = None - regs = circuit.qregs if qreg else circuit.cregs - for reg in regs: - if reg.name == name: - found_reg = reg - break - return found_reg - - -def _combine_result_objects(results: List[Result]) -> Result: - """Temporary helper function. - - TODO: This function would be removed after Terra supports job with infinite circuits. - """ - if len(results) == 1: - return results[0] - - new_result = copy.deepcopy(results[0]) - - for idx in range(1, len(results)): - new_result.results.extend(results[idx].results) - - return new_result - - -def _safe_get_job_status(job: Job, job_id: str, max_job_retries: int, wait: float) -> JobStatus: - for _ in range(max_job_retries): - try: - job_status = job.status() - break - except JobError as ex: - logger.warning( - "FAILURE: job id: %s, status: 'FAIL_TO_GET_STATUS' Terra job error: %s", - job_id, - ex, - ) - time.sleep(wait) - except Exception as ex: - raise QiskitError( - f"job id: {job_id}, status: 'FAIL_TO_GET_STATUS' Unknown error: ({ex})" - ) from ex - else: - raise QiskitError(f"Max retry limit reached. Failed to get status for job with id {job_id}") - - return job_status - - -@deprecate_func( - since="0.24.0", - additional_msg="For code migration guidelines, visit https://qisk.it/qi_migration.", -) -def run_circuits( - circuits: Union[QuantumCircuit, List[QuantumCircuit]], - backend: Backend, - qjob_config: Dict, - backend_options: Optional[Dict] = None, - noise_config: Optional[Dict] = None, - run_config: Optional[Dict] = None, - job_callback: Optional[Callable] = None, - max_job_retries: int = 50, -) -> Result: - """ - Deprecated: An execution wrapper with Qiskit-Terra, with job auto recover capability. - - The auto-recovery feature is only applied for non-simulator backend. - This wrapper will try to get the result no matter how long it takes. - - Args: - circuits: circuits to execute - backend: backend instance - qjob_config: configuration for quantum job object - backend_options: backend options - noise_config: configuration for noise model - run_config: configuration for run - job_callback: callback used in querying info of the submitted job, and providing the - following arguments: job_id, job_status, queue_position, job. - max_job_retries(int): positive non-zero number of trials for the job set (-1 for infinite - trials) (default: 50) - - Returns: - Result object - - Raises: - QiskitError: Any error except for JobError raised by Qiskit Terra - """ - backend_interface_version = _get_backend_interface_version(backend) - - backend_options = backend_options or {} - noise_config = noise_config or {} - run_config = run_config or {} - if backend_interface_version <= 1: - with_autorecover = not is_simulator_backend(backend) - else: - with_autorecover = False - - if MAX_CIRCUITS_PER_JOB is not None: - max_circuits_per_job = int(MAX_CIRCUITS_PER_JOB) - else: - if backend_interface_version <= 1: - if is_local_backend(backend): - max_circuits_per_job = sys.maxsize - else: - max_circuits_per_job = backend.configuration().max_experiments - else: - if backend.max_circuits is not None: - max_circuits_per_job = backend.max_circuits - else: - max_circuits_per_job = sys.maxsize - - if len(circuits) > max_circuits_per_job: - jobs = [] - job_ids = [] - split_circuits = [] - count = 0 - while count < len(circuits): - some_circuits = circuits[count : count + max_circuits_per_job] - split_circuits.append(some_circuits) - job, job_id = _safe_submit_circuits( - some_circuits, - backend, - qjob_config=qjob_config, - backend_options=backend_options, - noise_config=noise_config, - run_config=run_config, - max_job_retries=max_job_retries, - ) - jobs.append(job) - job_ids.append(job_id) - count += max_circuits_per_job - else: - job, job_id = _safe_submit_circuits( - circuits, - backend, - qjob_config=qjob_config, - backend_options=backend_options, - noise_config=noise_config, - run_config=run_config, - max_job_retries=max_job_retries, - ) - jobs = [job] - job_ids = [job_id] - split_circuits = [circuits] - results = [] - if with_autorecover: - logger.info("Backend status: %s", backend.status()) - logger.info("There are %s jobs are submitted.", len(jobs)) - logger.info("All job ids:\n%s", job_ids) - for idx, _ in enumerate(jobs): - result = None - logger.info("Backend status: %s", backend.status()) - logger.info("There is one jobs are submitted: id: %s", job_id) - job = jobs[idx] - job_id = job_ids[idx] - for _ in range(max_job_retries): - logger.info("Running job id: %s", job_id) - # try to get result if possible - while True: - job_status = _safe_get_job_status( - job, job_id, max_job_retries, qjob_config["wait"] - ) # if the status was broken, an Exception would be raised anyway - queue_position = 0 - if job_status in JOB_FINAL_STATES: - # do callback again after the job is in the final states - if job_callback is not None: - job_callback(job_id, job_status, queue_position, job) - break - if job_status == JobStatus.QUEUED and hasattr(job, "queue_position"): - queue_position = job.queue_position() - logger.info("Job id: %s is queued at position %s", job_id, queue_position) - else: - logger.info("Job id: %s, status: %s", job_id, job_status) - if job_callback is not None: - job_callback(job_id, job_status, queue_position, job) - time.sleep(qjob_config["wait"]) - - # get result after the status is DONE - if job_status == JobStatus.DONE: - for _ in range(max_job_retries): - result = job.result() - if result.success: - results.append(result) - logger.info("COMPLETED the %s-th job, job id: %s", idx, job_id) - break - - logger.warning("FAILURE: Job id: %s", job_id) - logger.warning( - "Job (%s) is completed anyway, retrieve result from backend again.", - job_id, - ) - job = backend.retrieve_job(job_id) - else: - raise QiskitError( - f"Max retry limit reached. Failed to get result for job id {job_id}" - ) - break - # for other cases, resubmit the circuit until the result is available. - # since if there is no result returned, there is no way algorithm can do any process - if job_status == JobStatus.CANCELLED: - logger.warning( - "FAILURE: Job id: %s is cancelled. Re-submit the circuits.", job_id - ) - elif job_status == JobStatus.ERROR: - logger.warning( - "FAILURE: Job id: %s encounters the error. " - "Error is : %s. Re-submit the circuits.", - job_id, - job.error_message(), - ) - else: - logging.warning( - "FAILURE: Job id: %s. Unknown status: %s. Re-submit the circuits.", - job_id, - job_status, - ) - job, job_id = _safe_submit_circuits( - split_circuits[idx], - backend, - qjob_config=qjob_config, - backend_options=backend_options, - noise_config=noise_config, - run_config=run_config, - max_job_retries=max_job_retries, - ) - else: - raise QiskitError( - f"Max retry limit reached. Failed to get result for job with id {job_id} " - ) - else: - results = [] - for job in jobs: - results.append(job.result()) - - result = _combine_result_objects(results) if results else None - # If result was not successful then raise an exception with either the status msg or - # extra information if this was an Aer partial result return - if not result.success: - msg = result.status - if result.status == "PARTIAL COMPLETED": - # Aer can return partial results which Aqua algorithms cannot process and signals - # using partial completed status where each returned result has a success and status. - # We use the status from the first result that was not successful - for res in result.results: - if not res.success: - msg += ", " + res.status - break - raise QiskitError(f"Circuit execution failed: {msg}") - - if not hasattr(result, "time_taken"): - setattr(result, "time_taken", 0.0) - - return result - - -def _safe_submit_circuits( - circuits: Union[QuantumCircuit, List[QuantumCircuit]], - backend: Backend, - qjob_config: Dict, - backend_options: Dict, - noise_config: Dict, - run_config: Dict, - max_job_retries: int, -) -> Tuple[Job, str]: - # assure get job ids - for _ in range(max_job_retries): - try: - job = _run_circuits_on_backend( - backend, - circuits, - backend_options=backend_options, - noise_config=noise_config, - run_config=run_config, - ) - job_id = job.job_id() - break - except QiskitError as ex: - failure_warn = True - if is_ibmq_provider(backend): - try: - from qiskit.providers.ibmq import IBMQBackendJobLimitError - except ImportError as ex1: - raise MissingOptionalLibraryError( - libname="qiskit-ibmq-provider", - name="_safe_submit_circuits", - pip_install="pip install qiskit-ibmq-provider", - ) from ex1 - if isinstance(ex, IBMQBackendJobLimitError): - - oldest_running = backend.jobs( - limit=1, descending=False, status=["QUEUED", "VALIDATING", "RUNNING"] - ) - if oldest_running: - oldest_running = oldest_running[0] - logger.warning( - "Job limit reached, waiting for job %s to finish " - "before submitting the next one.", - oldest_running.job_id(), - ) - failure_warn = False # Don't issue a second warning. - try: - oldest_running.wait_for_final_state( - timeout=qjob_config["timeout"], wait=qjob_config["wait"] - ) - except Exception: # pylint: disable=broad-except - # If the wait somehow fails or times out, we'll just re-try - # the job submit and see if it works now. - pass - if failure_warn: - logger.warning( - "FAILURE: Can not get job id, Resubmit the qobj to get job id. " - "Terra job error: %s ", - ex, - ) - except Exception as ex: # pylint: disable=broad-except - logger.warning( - "FAILURE: Can not get job id, Resubmit the qobj to get job id. Error: %s ", ex - ) - else: - raise QiskitError("Max retry limit reached. Failed to submit the qobj correctly") - - return job, job_id - - -def _run_circuits_on_backend( - backend: Backend, - circuits: Union[QuantumCircuit, List[QuantumCircuit]], - backend_options: Dict, - noise_config: Dict, - run_config: Dict, -) -> Job: - """Run on backend.""" - run_kwargs = {} - if is_aer_provider(backend) or is_basicaer_provider(backend): - for key, value in backend_options.items(): - if key == "backend_options": - for k, v in value.items(): - run_kwargs[k] = v - else: - run_kwargs[key] = value - else: - run_kwargs.update(backend_options) - - run_kwargs.update(noise_config) - run_kwargs.update(run_config) - - if is_basicaer_provider(backend): - # BasicAer emits warning if option is not in its list - for key in list(run_kwargs.keys()): - if not hasattr(backend.options, key): - del run_kwargs[key] - - return backend.run(circuits, **run_kwargs) diff --git a/qiskit/utils/validation.py b/qiskit/utils/validation.py deleted file mode 100644 index 0d3d59340aae..000000000000 --- a/qiskit/utils/validation.py +++ /dev/null @@ -1,211 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Validation module -""" - -from typing import Set -from qiskit.utils.deprecation import deprecate_func - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_in_set(name: str, value: object, values: Set[object]) -> None: - """ - Args: - name: value name. - value: value to check. - values: set that should contain value. - Raises: - ValueError: invalid value - """ - if value not in values: - raise ValueError(f"{name} must be one of '{values}', was '{value}'.") - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_min(name: str, value: float, minimum: float) -> None: - """ - Args: - name: value name. - value: value to check. - minimum: minimum value allowed. - Raises: - ValueError: invalid value - """ - if value < minimum: - raise ValueError(f"{name} must have value >= {minimum}, was {value}") - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_min_exclusive(name: str, value: float, minimum: float) -> None: - """ - Args: - name: value name. - value: value to check. - minimum: minimum value allowed. - Raises: - ValueError: invalid value - """ - if value <= minimum: - raise ValueError(f"{name} must have value > {minimum}, was {value}") - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_max(name: str, value: float, maximum: float) -> None: - """ - Args: - name: value name. - value: value to check. - maximum: maximum value allowed. - Raises: - ValueError: invalid value - """ - if value > maximum: - raise ValueError(f"{name} must have value <= {maximum}, was {value}") - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_max_exclusive(name: str, value: float, maximum: float) -> None: - """ - Args: - name: value name. - value: value to check. - maximum: maximum value allowed. - Raises: - ValueError: invalid value - """ - if value >= maximum: - raise ValueError(f"{name} must have value < {maximum}, was {value}") - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_range(name: str, value: float, minimum: float, maximum: float) -> None: - """ - Args: - name: value name. - value: value to check. - minimum: minimum value allowed. - maximum: maximum value allowed. - Raises: - ValueError: invalid value - """ - if value < minimum or value > maximum: - raise ValueError(f"{name} must have value >= {minimum} and <= {maximum}, was {value}") - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_range_exclusive(name: str, value: float, minimum: float, maximum: float) -> None: - """ - Args: - name: value name. - value: value to check. - minimum: minimum value allowed. - maximum: maximum value allowed. - Raises: - ValueError: invalid value - """ - if value <= minimum or value >= maximum: - raise ValueError(f"{name} must have value > {minimum} and < {maximum}, was {value}") - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_range_exclusive_min(name: str, value: float, minimum: float, maximum: float) -> None: - """ - Args: - name: value name. - value: value to check. - minimum: minimum value allowed. - maximum: maximum value allowed. - Raises: - ValueError: invalid value - """ - if value <= minimum or value > maximum: - raise ValueError(f"{name} must have value > {minimum} and <= {maximum}, was {value}") - - -@deprecate_func( - additional_msg=( - "This algorithm utility has been migrated to an independent package: " - "https://github.com/qiskit-community/qiskit-algorithms. You can run " - "``pip install qiskit_algorithms`` and import ``from qiskit_algorithms.utils`` instead. " - ), - since="0.45.0", -) -def validate_range_exclusive_max(name: str, value: float, minimum: float, maximum: float) -> None: - """ - Args: - name: value name. - value: value to check. - minimum: minimum value allowed. - maximum: maximum value allowed. - Raises: - ValueError: invalid value - """ - if value < minimum or value >= maximum: - raise ValueError(f"{name} must have value >= {minimum} and < {maximum}, was {value}") diff --git a/qiskit/version.py b/qiskit/version.py index 79cadf189742..b460280f58dd 100644 --- a/qiskit/version.py +++ b/qiskit/version.py @@ -16,9 +16,6 @@ import os import subprocess -from collections.abc import Mapping - -import warnings ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -85,97 +82,3 @@ def get_version_info(): __version__ = get_version_info() - - -class QiskitVersion(Mapping): - """DEPRECATED in 0.25.0 use qiskit.__version__""" - - __slots__ = ["_version_dict", "_loaded"] - - def __init__(self): - self._version_dict = { - "qiskit": __version__, - } - self._loaded = False - - def _load_versions(self): - warnings.warn( - "qiskit.__qiskit_version__ is deprecated since " - "Qiskit Terra 0.25.0, and will be removed 3 months or more later. " - "Instead, you should use qiskit.__version__. The other packages listed in the" - "former qiskit.__qiskit_version__ have their own __version__ module level dunder, " - "as standard in PEP 8.", - category=DeprecationWarning, - stacklevel=3, - ) - try: - # TODO: Update to use qiskit_aer instead when we remove the - # namespace redirect - from qiskit.providers import aer - - self._version_dict["qiskit-aer"] = aer.__version__ - except Exception: - self._version_dict["qiskit-aer"] = None - try: - from qiskit import ignis - - self._version_dict["qiskit-ignis"] = ignis.__version__ - except Exception: - self._version_dict["qiskit-ignis"] = None - try: - from qiskit.providers import ibmq - - self._version_dict["qiskit-ibmq-provider"] = ibmq.__version__ - except Exception: - self._version_dict["qiskit-ibmq-provider"] = None - try: - import qiskit_nature - - self._version_dict["qiskit-nature"] = qiskit_nature.__version__ - except Exception: - self._version_dict["qiskit-nature"] = None - try: - import qiskit_finance - - self._version_dict["qiskit-finance"] = qiskit_finance.__version__ - except Exception: - self._version_dict["qiskit-finance"] = None - try: - import qiskit_optimization - - self._version_dict["qiskit-optimization"] = qiskit_optimization.__version__ - except Exception: - self._version_dict["qiskit-optimization"] = None - try: - import qiskit_machine_learning - - self._version_dict["qiskit-machine-learning"] = qiskit_machine_learning.__version__ - except Exception: - self._version_dict["qiskit-machine-learning"] = None - self._loaded = True - - def __repr__(self): - if not self._loaded: - self._load_versions() - return repr(self._version_dict) - - def __str__(self): - if not self._loaded: - self._load_versions() - return str(self._version_dict) - - def __getitem__(self, key): - if not self._loaded: - self._load_versions() - return self._version_dict[key] - - def __iter__(self): - if not self._loaded: - self._load_versions() - return iter(self._version_dict) - - def __len__(self): - return len(self._version_dict) - - -__qiskit_version__ = QiskitVersion() diff --git a/qiskit/visualization/__init__.py b/qiskit/visualization/__init__.py index 29eb5cf2c0cc..ce824d84adb3 100644 --- a/qiskit/visualization/__init__.py +++ b/qiskit/visualization/__init__.py @@ -222,17 +222,6 @@ pass_manager_drawer -Pulse Visualizations -==================== - -.. autosummary:: - :toctree: ../stubs/ - - pulse_drawer - ~qiskit.visualization.pulse.IQXStandard - ~qiskit.visualization.pulse.IQXSimple - ~qiskit.visualization.pulse.IQXDebugging - Timeline Visualizations ======================= @@ -285,8 +274,6 @@ from .pass_manager_visualization import pass_manager_drawer from .pass_manager_visualization import staged_pass_manager_drawer -from .pulse.interpolation import step_wise, linear, cubic_spline -from .pulse.qcstyle import PulseStyle, SchedStyle from .pulse_v2 import draw as pulse_drawer from .timeline import draw as timeline_drawer diff --git a/qiskit/visualization/circuit/_utils.py b/qiskit/visualization/circuit/_utils.py index 13c554e0cc2e..252db40e51c5 100644 --- a/qiskit/visualization/circuit/_utils.py +++ b/qiskit/visualization/circuit/_utils.py @@ -197,7 +197,7 @@ def get_bit_register(circuit, bit): return bit_loc.registers[0][0] if bit_loc.registers else None -@deprecate_arg("reverse_bits", since="0.22.0") +@deprecate_arg("reverse_bits", since="0.22.0", package_name="qiskit-terra") def get_bit_reg_index(circuit, bit, reverse_bits=None): """Get the register for a bit if there is one, and the index of the bit from the top of the circuit, or the index of the bit within a register. @@ -285,7 +285,7 @@ def get_wire_label(drawer, register, index, layout=None, cregbundle=True): return wire_label -@deprecate_arg("reverse_bits", since="0.22.0") +@deprecate_arg("reverse_bits", since="0.22.0", package_name="qiskit-terra") def get_condition_label_val(condition, circuit, cregbundle, reverse_bits=None): """Get the label and value list to display a condition diff --git a/qiskit/visualization/circuit/circuit_visualization.py b/qiskit/visualization/circuit/circuit_visualization.py index 87c3f1799cea..a70ef9c2822b 100644 --- a/qiskit/visualization/circuit/circuit_visualization.py +++ b/qiskit/visualization/circuit/circuit_visualization.py @@ -33,6 +33,7 @@ from qiskit import user_config from qiskit.utils import optionals as _optionals +from qiskit.circuit import ControlFlowOp, Measure from . import latex as _latex from . import text as _text from . import matplotlib as _matplotlib @@ -248,6 +249,28 @@ def circuit_drawer( ) cregbundle = False + def check_clbit_in_inst(circuit, cregbundle): + if cregbundle is False: + return False + for inst in circuit.data: + if isinstance(inst.operation, ControlFlowOp): + for block in inst.operation.blocks: + if check_clbit_in_inst(block, cregbundle) is False: + return False + elif inst.clbits and not isinstance(inst.operation, Measure): + if cregbundle is not False: + warn( + "Cregbundle set to False since an instruction needs to refer" + " to individual classical wire", + RuntimeWarning, + 3, + ) + return False + + return True + + cregbundle = check_clbit_in_inst(circuit, cregbundle) + if output == "text": return _text_circuit_drawer( circuit, diff --git a/qiskit/visualization/circuit/matplotlib.py b/qiskit/visualization/circuit/matplotlib.py index 33bf01f75cf9..f46561c6ed94 100644 --- a/qiskit/visualization/circuit/matplotlib.py +++ b/qiskit/visualization/circuit/matplotlib.py @@ -17,7 +17,6 @@ import collections import itertools import re -from warnings import warn from io import StringIO import numpy as np @@ -135,28 +134,7 @@ def __init__( self._global_phase = self._circuit.global_phase self._calibrations = self._circuit.calibrations self._expr_len = expr_len - - def check_clbit_in_inst(circuit, cregbundle): - if cregbundle is False: - return False - for inst in circuit.data: - if isinstance(inst.operation, ControlFlowOp): - for block in inst.operation.blocks: - if check_clbit_in_inst(block, cregbundle) is False: - return False - elif inst.clbits and not isinstance(inst.operation, Measure): - if cregbundle is not False: - warn( - "Cregbundle set to False since an instruction needs to refer" - " to individual classical wire", - RuntimeWarning, - 3, - ) - return False - - return True - - self._cregbundle = check_clbit_in_inst(circuit, cregbundle) + self._cregbundle = cregbundle self._lwidth1 = 1.0 self._lwidth15 = 1.5 diff --git a/qiskit/visualization/circuit/text.py b/qiskit/visualization/circuit/text.py index c1a28ea37675..8fdc29829693 100644 --- a/qiskit/visualization/circuit/text.py +++ b/qiskit/visualization/circuit/text.py @@ -735,28 +735,7 @@ def __init__( raise ValueError("Vertical compression can only be 'high', 'medium', or 'low'") self.vertical_compression = vertical_compression self._wire_map = {} - - def check_clbit_in_inst(circuit, cregbundle): - if cregbundle is False: - return False - for inst in circuit.data: - if isinstance(inst.operation, ControlFlowOp): - for block in inst.operation.blocks: - if check_clbit_in_inst(block, cregbundle) is False: - return False - elif inst.clbits and not isinstance(inst.operation, Measure): - if cregbundle is not False: - warn( - "Cregbundle set to False since an instruction needs to refer" - " to individual classical wire", - RuntimeWarning, - 3, - ) - return False - - return True - - self.cregbundle = check_clbit_in_inst(circuit, cregbundle) + self.cregbundle = cregbundle if encoding: self.encoding = encoding diff --git a/qiskit/visualization/counts_visualization.py b/qiskit/visualization/counts_visualization.py index fb7ddba13185..a0e4b64a4aa2 100644 --- a/qiskit/visualization/counts_visualization.py +++ b/qiskit/visualization/counts_visualization.py @@ -67,6 +67,7 @@ def _is_deprecated_data_format(data) -> bool: additional_msg="Instead, use ``plot_distribution()``.", predicate=_is_deprecated_data_format, pending=True, + package_name="qiskit-terra", ) def plot_histogram( data, diff --git a/qiskit/visualization/dag_visualization.py b/qiskit/visualization/dag_visualization.py index 8e3de6e1186a..961087facc54 100644 --- a/qiskit/visualization/dag_visualization.py +++ b/qiskit/visualization/dag_visualization.py @@ -18,7 +18,9 @@ from rustworkx.visualization import graphviz_draw from qiskit.dagcircuit.dagnode import DAGOpNode, DAGInNode, DAGOutNode -from qiskit.circuit import Qubit +from qiskit.circuit import Qubit, Clbit, ClassicalRegister +from qiskit.circuit.classical import expr +from qiskit.converters import dagdependency_to_circuit from qiskit.utils import optionals as _optionals from qiskit.exceptions import InvalidFileError from .exceptions import VisualizationError @@ -72,32 +74,83 @@ def dag_drawer(dag, scale=0.7, filename=None, style="color"): # the two tradeoffs ere that it will not handle subclasses and it is # slower (which doesn't matter for a visualization function) type_str = str(type(dag)) + register_bit_labels = { + bit: f"{reg.name}[{idx}]" + for reg in list(dag.qregs.values()) + list(dag.cregs.values()) + for (idx, bit) in enumerate(reg) + } if "DAGDependency" in type_str: + # pylint: disable=cyclic-import + from qiskit.visualization.circuit._utils import get_bit_reg_index + + qubit_indices = {bit: index for index, bit in enumerate(dag.qubits)} + clbit_indices = {bit: index for index, bit in enumerate(dag.clbits)} graph_attrs = {"dpi": str(100 * scale)} + dag_dep_circ = dagdependency_to_circuit(dag) def node_attr_func(node): if style == "plain": return {} if style == "color": n = {} - n["label"] = str(node.node_id) + ": " + str(node.name) - if node.name == "measure": - n["color"] = "blue" - n["style"] = "filled" - n["fillcolor"] = "lightblue" + args = [] + for count, arg in enumerate(node.qargs + node.cargs): + if count > 4: + args.append("...") + break + if isinstance(arg, Qubit): + f_str = f"q_{qubit_indices[arg]}" + elif isinstance(arg, Clbit): + f_str = f"c_{clbit_indices[arg]}" + else: + f_str = f"{arg.index}" + arg_str = register_bit_labels.get(arg, f_str) + args.append(arg_str) + + n["color"] = "black" + n["label"] = ( + str(node.node_id) + + ": " + + str(node.name) + + " (" + + str(args)[1:-1].replace("'", "") + + ")" + ) if node.name == "barrier": - n["color"] = "black" n["style"] = "filled" - n["fillcolor"] = "green" - if getattr(node.op, "_directive", False): - n["color"] = "black" + n["fillcolor"] = "grey" + elif getattr(node.op, "_directive", False): n["style"] = "filled" n["fillcolor"] = "red" - if getattr(node.op, "condition", None): - n["label"] = str(node.node_id) + ": " + str(node.name) + " (conditional)" - n["color"] = "black" + elif getattr(node.op, "condition", None): + condition = node.op.condition + if isinstance(condition, expr.Expr): + cond_txt = " (cond: [Expr]) (" + elif isinstance(condition[0], ClassicalRegister): + cond_txt = f" (cond: {condition[0].name}, {int(condition[1])}) (" + else: + register, bit_index, reg_index = get_bit_reg_index( + dag_dep_circ, condition[0] + ) + if register is not None: + cond_txt = ( + f" (cond: {register.name}[{reg_index}], {int(condition[1])}) (" + ) + else: + cond_txt = f" (cond: {bit_index}, {int(condition[1])}) (" n["style"] = "filled" - n["fillcolor"] = "lightgreen" + n["fillcolor"] = "green" + n["label"] = ( + str(node.node_id) + + ": " + + str(node.name) + + cond_txt + + str(args)[1:-1].replace("'", "") + + ")" + ) + elif node.name != "measure": # measure is unfilled + n["style"] = "filled" + n["fillcolor"] = "lightblue" return n else: raise VisualizationError("Unrecognized style %s for the dag_drawer." % style) @@ -105,12 +158,6 @@ def node_attr_func(node): edge_attr_func = None else: - register_bit_labels = { - bit: f"{reg.name}[{idx}]" - for reg in list(dag.qregs.values()) + list(dag.cregs.values()) - for (idx, bit) in enumerate(reg) - } - graph_attrs = {"dpi": str(100 * scale)} def node_attr_func(node): diff --git a/qiskit/visualization/pulse/__init__.py b/qiskit/visualization/pulse/__init__.py deleted file mode 100644 index fdfde35ff835..000000000000 --- a/qiskit/visualization/pulse/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -""" -Pulse visualization module. -""" - -# These imports are preparing for the renaming of `pulse_v2` back to `pulse` in a future Terra. -from qiskit.visualization.pulse_v2 import * diff --git a/qiskit/visualization/pulse/interpolation.py b/qiskit/visualization/pulse/interpolation.py deleted file mode 100644 index b5d12a19cbe9..000000000000 --- a/qiskit/visualization/pulse/interpolation.py +++ /dev/null @@ -1,121 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=bad-docstring-quotes - -""" -Deprecated. - -Interpolation module for pulse visualization. -""" -from __future__ import annotations -from functools import partial - -import numpy as np - -from qiskit.utils.deprecation import deprecate_func - - -@deprecate_func( - additional_msg=( - "Instead, use the new interface in ``qiskit.visualization.pulse_drawer`` for " - "pulse visualization." - ), - since="0.23.0", - removal_timeline="no earlier than 6 months after the release date", -) -def interp1d( - time: np.ndarray, samples: np.ndarray, nop: int, kind: str = "linear" -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - """Deprecated. - - Scipy interpolation wrapper. - - Args: - time: Time vector with length of ``samples`` + 1. - samples: Complex pulse envelope. - nop: Number of data points for interpolation. - kind: Scipy interpolation type. - See ``scipy.interpolate.interp1d`` documentation for more information. - Returns: - Interpolated time vector and real and imaginary part of waveform. - """ - from scipy import interpolate - - re_y = np.real(samples) - im_y = np.imag(samples) - - dt = time[1] - time[0] - - time += 0.5 * dt - cs_ry = interpolate.interp1d(time[:-1], re_y, kind=kind, bounds_error=False) - cs_iy = interpolate.interp1d(time[:-1], im_y, kind=kind, bounds_error=False) - - time_ = np.linspace(time[0], time[-1] * dt, nop) - - return time_, cs_ry(time_), cs_iy(time_) - - -@deprecate_func( - additional_msg=( - "Instead, use the new interface in ``qiskit.visualization.pulse_drawer`` for " - "pulse visualization." - ), - since="0.23.0", - removal_timeline="no earlier than 6 months after the release date", -) -def step_wise( - time: np.ndarray, samples: np.ndarray, nop: int -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - # pylint: disable=unused-argument - """Deprecated. - - Keep uniform variation between sample values. No interpolation is applied. - Args: - time: Time vector with length of ``samples`` + 1. - samples: Complex pulse envelope. - nop: This argument is not used. - Returns: - Time vector and real and imaginary part of waveform. - """ - samples_ = np.repeat(samples, 2) - re_y_ = np.real(samples_) - im_y_ = np.imag(samples_) - time__: np.ndarray = np.concatenate(([time[0]], np.repeat(time[1:-1], 2), [time[-1]])) - return time__, re_y_, im_y_ - - -linear = partial(interp1d, kind="linear") -linear.__doc__ = """Deprecated. - -Apply linear interpolation between sampling points. - -Args: - time: Time vector with length of ``samples`` + 1. - samples: Complex pulse envelope. - nop: Number of data points for interpolation. -Returns: - Interpolated time vector and real and imaginary part of waveform. -""" - -cubic_spline = partial(interp1d, kind="cubic") -cubic_spline.__doc__ = """Deprecated. - -Apply cubic interpolation between sampling points. - -Args: - time: Time vector with length of ``samples`` + 1. - samples: Complex pulse envelope. - nop: Number of data points for interpolation. -Returns: - Interpolated time vector and real and imaginary part of waveform. -""" diff --git a/qiskit/visualization/pulse/matplotlib.py b/qiskit/visualization/pulse/matplotlib.py deleted file mode 100644 index 619677161ab2..000000000000 --- a/qiskit/visualization/pulse/matplotlib.py +++ /dev/null @@ -1,1019 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=invalid-name,bad-docstring-quotes - -"""Matplotlib classes for pulse visualization.""" - -from __future__ import annotations -import collections -from collections.abc import Callable, Sequence -from typing import Any - -import numpy as np - -from qiskit.utils import optionals as _optionals -from qiskit.visualization.pulse.qcstyle import PulseStyle, SchedStyle -from qiskit.visualization.pulse.interpolation import step_wise -from qiskit.pulse.channels import ( - DriveChannel, - ControlChannel, - MeasureChannel, - AcquireChannel, - SnapshotChannel, - Channel, -) -from qiskit.pulse import ( - Waveform, - Snapshot, - Play, - Acquire, - PulseError, - ParametricPulse, - SetFrequency, - ShiftPhase, - Instruction, - ShiftFrequency, - SetPhase, -) -from qiskit.pulse.schedule import ScheduleComponent -from qiskit.utils.deprecation import deprecate_func - - -class EventsOutputChannels: - """Pulse dataset for channel.""" - - @deprecate_func( - additional_msg=( - "Instead, use the new interface in ``qiskit.visualization.pulse_drawer`` for " - "pulse visualization." - ), - since="0.23.0", - removal_timeline="no earlier than 6 months after the release date", - ) - def __init__(self, t0: int, tf: int): - """Create new channel dataset. - - TODO: remove PV - - Args: - t0: starting time of plot - tf: ending time of plot - """ - self.pulses: dict[int, Instruction] = {} - self.t0 = t0 - self.tf = tf - - self._waveform: np.ndarray | None = None - self._framechanges = None - self._setphase = None - self._frequencychanges = None - self._conditionals = None - self._snapshots = None - self._labels = None - self.enable = False - - def add_instruction(self, start_time: int, instruction: Instruction): - """Add new pulse instruction to channel. - - Args: - start_time: Starting time of instruction - instruction: Instruction object to be added - """ - if isinstance(instruction, Play): - pulse = instruction.pulse - else: - pulse = instruction - if start_time in self.pulses: - self.pulses[start_time].append(pulse) - else: - self.pulses[start_time] = [pulse] - - @property - def waveform(self) -> np.ndarray: - """Get waveform.""" - if self._waveform is None: - self._build_waveform() - - return self._waveform[self.t0 : self.tf] - - @property - def framechanges(self) -> dict[int, ShiftPhase]: - """Get frame changes.""" - if self._framechanges is None: - self._build_waveform() - - return self._trim(self._framechanges) - - @property - def setphase(self) -> dict[int, SetPhase]: - """Get the SetPhase phase values.""" - if self._setphase is None: - self._build_waveform() - - return self._trim(self._setphase) - - @property - def frequencychanges(self) -> dict[int, SetFrequency]: - """Get the frequency changes.""" - if self._frequencychanges is None: - self._build_waveform() - - return self._trim(self._frequencychanges) - - @property - def frequencyshift(self) -> dict[int, ShiftFrequency]: - """Set the frequency changes.""" - if self._frequencychanges is None: - self._build_waveform() - - return self._trim(self._frequencychanges) - - @property - def conditionals(self) -> dict[int, str]: - """Get conditionals.""" - if self._conditionals is None: - self._build_waveform() - - return self._trim(self._conditionals) - - @property - def snapshots(self) -> dict[int, Snapshot]: - """Get snapshots.""" - if self._snapshots is None: - self._build_waveform() - - return self._trim(self._snapshots) - - @property - def labels(self) -> dict[int, Waveform | Acquire]: - """Get labels.""" - if self._labels is None: - self._build_waveform() - - return self._trim(self._labels) - - def is_empty(self) -> bool: - """Return if pulse is empty. - - Returns: - bool: if the channel has nothing to plot - """ - if ( - any(self.waveform) - or self.framechanges - or self.setphase - or self.conditionals - or self.snapshots - ): - return False - - return True - - def to_table(self, name: str) -> list[tuple[int, str, str]]: - """Get table contains. - - Args: - name (str): name of channel - - Returns: - A list of events in the channel - """ - time_event = [] - - framechanges = self.framechanges - setphase = self.setphase - conditionals = self.conditionals - snapshots = self.snapshots - frequencychanges = self.frequencychanges - - for key, val in framechanges.items(): - data_str = "shift phase: %.2f" % val - time_event.append((key, name, data_str)) - for key, val in setphase.items(): - data_str = "set phase: %.2f" % val - time_event.append((key, name, data_str)) - for key, val in conditionals.items(): - data_str = "conditional, %s" % val - time_event.append((key, name, data_str)) - for key, val in snapshots.items(): - data_str = "snapshot: %s" % val - time_event.append((key, name, data_str)) - for key, val in frequencychanges.items(): - data_str = "frequency: %.4e" % val - time_event.append((key, name, data_str)) - - return time_event - - def _build_waveform(self): - """Create waveform from stored pulses.""" - self._framechanges = {} - self._setphase = {} - self._frequencychanges = {} - self._conditionals = {} - self._snapshots = {} - self._labels = {} - fc = 0 - pv = np.zeros(self.tf + 1, dtype=np.complex128) - wf = np.zeros(self.tf + 1, dtype=np.complex128) - for time, commands in sorted(self.pulses.items()): - if time > self.tf: - break - tmp_fc = 0 - tmp_set_phase = 0 - tmp_sf = None - for command in commands: - if isinstance(command, ShiftPhase): - tmp_fc += command.phase - pv[time:] = 0 - elif isinstance(command, SetPhase): - tmp_set_phase = command.phase - pv[time:] = 0 - elif isinstance(command, SetFrequency): - tmp_sf = command.frequency - elif isinstance(command, ShiftFrequency): - tmp_sf = command.frequency - elif isinstance(command, Snapshot): - self._snapshots[time] = command.name - if tmp_fc != 0: - self._framechanges[time] = tmp_fc - fc += tmp_fc - if tmp_set_phase != 0: - self._setphase[time] = tmp_set_phase - fc = tmp_set_phase - if tmp_sf is not None: - self._frequencychanges[time] = tmp_sf - - for command in commands: - duration = command.duration - tf = min(time + duration, self.tf) - if isinstance(command, ParametricPulse): - command = command.get_waveform() - if isinstance(command, Waveform): - wf[time:tf] = np.exp(1j * fc) * command.samples[: tf - time] - pv[time:] = 0 - self._labels[time] = (tf, command) - - elif isinstance(command, Acquire): - wf[time:tf] = np.ones(tf - time) - self._labels[time] = (tf, command) - self._waveform = wf + pv - - def _trim(self, events: dict[int, Any]) -> dict[int, Any]: - """Return events during given `time_range`. - - Args: - events: time and operation of events. - - Returns: - Events within the specified time range. - """ - events_in_time_range = {} - - for k, v in events.items(): - if self.t0 <= k <= self.tf: - events_in_time_range[k] = v - - return events_in_time_range - - -class WaveformDrawer: - """A class to create figure for sample pulse.""" - - @deprecate_func( - additional_msg=( - "Instead, use the new interface in ``qiskit.visualization.pulse_drawer`` for " - "pulse visualization." - ), - since="0.23.0", - removal_timeline="no earlier than 6 months after the release date", - ) - def __init__(self, style: PulseStyle): - """Create new figure. - - Args: - style: Style sheet for pulse visualization. - """ - self.style = style or PulseStyle() - - @_optionals.HAS_MATPLOTLIB.require_in_call("waveform drawer") - def draw( - self, - pulse: Waveform, - dt: float = 1.0, - interp_method: Callable = None, - scale: float = 1, - draw_title: bool = False, - ): - """Draw figure. - - Args: - pulse: Waveform to draw. - dt: time interval. - interp_method: interpolation function. - scale: Relative visual scaling of waveform amplitudes. - draw_title: Add a title to the plot when set to ``True``. - - Returns: - matplotlib.figure.Figure: A matplotlib figure object of the pulse envelope. - - Raises: - MissingOptionalLibraryError: If matplotlib is not installed - """ - from matplotlib import pyplot as plt - - # If these self.style.dpi or self.style.figsize are None, they will - # revert back to their default rcParam keys. - figure = plt.figure(dpi=self.style.dpi, figsize=self.style.figsize) - - interp_method = interp_method or step_wise - - ax = figure.add_subplot(111) - ax.set_facecolor(self.style.bg_color) - - samples = pulse.samples - time = np.arange(0, len(samples) + 1, dtype=float) * dt - - time, re, im = interp_method(time, samples, self.style.num_points) - - # plot - ax.fill_between( - x=time, - y1=re, - y2=np.zeros_like(time), - facecolor=self.style.wave_color[0], - alpha=0.3, - edgecolor=self.style.wave_color[0], - linewidth=1.5, - label="real part", - ) - ax.fill_between( - x=time, - y1=im, - y2=np.zeros_like(time), - facecolor=self.style.wave_color[1], - alpha=0.3, - edgecolor=self.style.wave_color[1], - linewidth=1.5, - label="imaginary part", - ) - - ax.set_xlim(0, pulse.duration * dt) - if scale: - ax.set_ylim(-1 / scale, 1 / scale) - else: - v_max = max(max(np.abs(re)), max(np.abs(im))) - ax.set_ylim(-1.2 * v_max, 1.2 * v_max) - - bbox = ax.get_position() - - # This check is here for backwards compatibility. Before, the check was around - # the suptitle line, however since the font style can take on a type of None - # we need to unfortunately check both the type and the value of the object. - if isinstance(self.style.title_font_size, int) and self.style.title_font_size > 0: - if draw_title: - figure.suptitle( - pulse.name, fontsize=self.style.title_font_size, y=bbox.y1 + 0.02, va="bottom" - ) - - return figure - - -@_optionals.HAS_MATPLOTLIB.require_in_instance -class ScheduleDrawer: - """A class to create figure for schedule and channel.""" - - @deprecate_func( - additional_msg=( - "Instead, use the new interface in ``qiskit.visualization.pulse_drawer`` for " - "pulse visualization." - ), - since="0.23.0", - removal_timeline="no earlier than 6 months after the release date", - ) - def __init__(self, style: SchedStyle): - """Create new figure. - - Args: - style: Style sheet for pulse schedule visualization. - Raises: - MissingOptionalLibraryError: If matplotlib is not installed - """ - from matplotlib import pyplot as plt - from matplotlib import gridspec - - self.plt_mod = plt - self.gridspec_mod = gridspec - self.style = style or SchedStyle() - - def _build_channels( - self, - schedule: ScheduleComponent, - channels: list[Channel], - t0: int, - tf: int, - show_framechange_channels: bool = True, - ) -> tuple[ - dict[Channel, EventsOutputChannels], - dict[Channel, EventsOutputChannels], - dict[Channel, EventsOutputChannels], - ]: - """Create event table of each pulse channels in the given schedule. - - Args: - schedule: Schedule object to plot. - channels: Channels to plot. - t0: Start time of plot. - tf: End time of plot. - show_framechange_channels: Plot channels only with FrameChanges (ShiftPhase). - - Returns: - channels: All channels. - output_channels: All (D, M, U, A) channels. - snapshot_channels: Snapshots. - """ - # prepare waveform channels - drive_channels: dict[Channel, EventsOutputChannels] = collections.OrderedDict() - measure_channels: dict[Channel, EventsOutputChannels] = collections.OrderedDict() - control_channels: dict[Channel, EventsOutputChannels] = collections.OrderedDict() - acquire_channels: dict[Channel, EventsOutputChannels] = collections.OrderedDict() - snapshot_channels: dict[Channel, EventsOutputChannels] = collections.OrderedDict() - _channels: set[Channel] = set() - if show_framechange_channels: - _channels.update(schedule.channels) - # take channels that do not only contain framechanges - else: - for start_time, instruction in schedule.instructions: - if not isinstance(instruction, (ShiftPhase, SetPhase)): - _channels.update(instruction.channels) - - _channels.update(channels) - for chan in _channels: - if isinstance(chan, DriveChannel): - try: - drive_channels[chan] = EventsOutputChannels(t0, tf) - except PulseError: - pass - elif isinstance(chan, MeasureChannel): - try: - measure_channels[chan] = EventsOutputChannels(t0, tf) - except PulseError: - pass - elif isinstance(chan, ControlChannel): - try: - control_channels[chan] = EventsOutputChannels(t0, tf) - except PulseError: - pass - elif isinstance(chan, AcquireChannel): - try: - acquire_channels[chan] = EventsOutputChannels(t0, tf) - except PulseError: - pass - elif isinstance(chan, SnapshotChannel): - try: - snapshot_channels[chan] = EventsOutputChannels(t0, tf) - except PulseError: - pass - - output_channels = { - **drive_channels, - **measure_channels, - **control_channels, - **acquire_channels, - } - channels = {**output_channels, **snapshot_channels} - # sort by index then name to group qubits together. - output_channels = collections.OrderedDict( - sorted(output_channels.items(), key=lambda x: (x[0].index, x[0].name)) - ) - channels = collections.OrderedDict( - sorted(channels.items(), key=lambda x: (x[0].index, x[0].name)) - ) - - for start_time, instruction in schedule.instructions: - for channel in instruction.channels: - if channel in output_channels: - output_channels[channel].add_instruction(start_time, instruction) - elif channel in snapshot_channels: - snapshot_channels[channel].add_instruction(start_time, instruction) - return channels, output_channels, snapshot_channels - - @staticmethod - def _scale_channels( - output_channels: dict[Channel, EventsOutputChannels], - scale: float, - channel_scales: dict[Channel, float] = None, - channels: list[Channel] = None, - plot_all: bool = False, - ) -> dict[Channel, float]: - """Count number of channels that contains any instruction to show - and find scale factor of that channel. - - Args: - output_channels: Event table of channels to show. - scale: Global scale factor. - channel_scales: Channel specific scale factors. - channels: Specified channels to plot. - plot_all: Plot empty channel. - - Returns: - scale_dict: Scale factor of each channel. - """ - # count numbers of valid waveform - scale_dict: dict[Channel, float] = {chan: 0.0 for chan in output_channels.keys()} - for channel, events in output_channels.items(): - v_max = 0 - if channels: - if channel in channels: - waveform = events.waveform - v_max = max( - v_max, max(np.abs(np.real(waveform))), max(np.abs(np.imag(waveform))) - ) - events.enable = True - else: - if not events.is_empty() or plot_all: - waveform = events.waveform - v_max = max( - v_max, max(np.abs(np.real(waveform))), max(np.abs(np.imag(waveform))) - ) - events.enable = True - - scale_val = channel_scales.get(channel, scale) - if not scale_val: - # when input schedule is empty or comprises only frame changes, - # we need to overwrite maximum amplitude by a value greater than zero, - # otherwise auto axis scaling will fail with zero division. - v_max = v_max or 1 - scale_dict[channel] = 1 / v_max - else: - scale_dict[channel] = scale_val - - return scale_dict - - def _draw_table(self, figure, channels: dict[Channel, EventsOutputChannels], dt: float): - """Draw event table if events exist. - - Args: - figure (matplotlib.figure.Figure): Figure object - channels: Dictionary of channel and event table - dt: Time interval - - Returns: - tuple[matplotlib.axes.Axes]: Axis objects for table and canvas of pulses. - """ - # create table - table_data = [] - if self.style.use_table: - for channel, events in channels.items(): - if events.enable: - table_data.extend(events.to_table(channel.name)) - table_data = sorted(table_data, key=lambda x: x[0]) - - # plot table - if table_data: - # table area size - ncols = self.style.table_columns - nrows = int(np.ceil(len(table_data) / ncols)) - max_size = self.style.max_table_ratio * figure.get_size_inches()[1] - max_rows = np.floor(max_size / self.style.fig_unit_h_table / ncols) - nrows = int(min(nrows, max_rows)) - # don't overflow plot with table data - table_data = table_data[: int(nrows * ncols)] - # fig size - h_table = nrows * self.style.fig_unit_h_table - h_waves = figure.get_size_inches()[1] - h_table - - # create subplots - gs = self.gridspec_mod.GridSpec(2, 1, height_ratios=[h_table, h_waves], hspace=0) - tb = self.plt_mod.subplot(gs[0]) - ax = self.plt_mod.subplot(gs[1]) - - # configure each cell - tb.axis("off") - cell_value = [["" for _kk in range(ncols * 3)] for _jj in range(nrows)] - cell_color = [self.style.table_color * ncols for _jj in range(nrows)] - cell_width = [*([0.2, 0.2, 0.5] * ncols)] - for ii, data in enumerate(table_data): - # pylint: disable=unbalanced-tuple-unpacking - r, c = np.unravel_index(ii, (nrows, ncols), order="f") - # pylint: enable=unbalanced-tuple-unpacking - time, ch_name, data_str = data - # item - cell_value[r][3 * c + 0] = "t = %s" % (time * dt) - cell_value[r][3 * c + 1] = "ch %s" % ch_name - cell_value[r][3 * c + 2] = data_str - table = tb.table( - cellText=cell_value, - cellLoc="left", - rowLoc="center", - colWidths=cell_width, - bbox=[0, 0, 1, 1], - cellColours=cell_color, - ) - table.auto_set_font_size(False) - table.set_fontsize = self.style.table_font_size - else: - tb = None - ax = figure.add_subplot(111) - - return tb, ax - - @staticmethod - def _draw_snapshots( - ax, snapshot_channels: dict[Channel, EventsOutputChannels], y0: float - ) -> None: - """Draw snapshots to given mpl axis. - - Args: - ax (matplotlib.axes.Axes): axis object to draw snapshots. - snapshot_channels: Event table of snapshots. - y0: vertical position to draw the snapshots. - """ - for events in snapshot_channels.values(): - snapshots = events.snapshots - if snapshots: - for time in snapshots: - ax.annotate( - s="\u25D8", - xy=(time, y0), - xytext=(time, y0 + 0.08), - arrowprops={"arrowstyle": "wedge"}, - ha="center", - ) - - def _draw_framechanges(self, ax, fcs: dict[int, ShiftPhase], y0: float) -> None: - """Draw frame change of given channel to given mpl axis. - - Args: - ax (matplotlib.axes.Axes): axis object to draw frame changes. - fcs: Event table of frame changes. - y0: vertical position to draw the frame changes. - """ - for time in fcs.keys(): - ax.text( - x=time, - y=y0, - s=r"$\circlearrowleft$", - fontsize=self.style.icon_font_size, - ha="center", - va="center", - ) - - def _draw_frequency_changes(self, ax, sf: dict[int, SetFrequency], y0: float) -> None: - """Draw set frequency of given channel to given mpl axis. - - Args: - ax (matplotlib.axes.Axes): axis object to draw frame changes. - sf: Event table of set frequency. - y0: vertical position to draw the frame changes. - """ - for time in sf.keys(): - ax.text( - x=time, - y=y0, - s=r"$\leftrightsquigarrow$", - fontsize=self.style.icon_font_size, - ha="center", - va="center", - rotation=90, - ) - - def _get_channel_color(self, channel: Channel) -> str: - """Lookup table for waveform color. - - Args: - channel: Type of channel. - - Return: - Color code or name of color. - """ - # choose color - if isinstance(channel, DriveChannel): - color = self.style.d_ch_color - elif isinstance(channel, ControlChannel): - color = self.style.u_ch_color - elif isinstance(channel, MeasureChannel): - color = self.style.m_ch_color - elif isinstance(channel, AcquireChannel): - color = self.style.a_ch_color - else: - color = "black" - return color - - @staticmethod - def _prev_label_at_time(prev_labels: list[dict[int, Waveform | Acquire]], time: int) -> bool: - """Check overlap of pulses with previous channels. - - Args: - prev_labels: List of labels in previous channels. - time: Start time of current pulse instruction. - - Returns: - `True` if current instruction overlaps with others. - """ - for labels in prev_labels: - for t0, (tf, _) in labels.items(): - if time in (t0, tf): - return True - return False - - def _draw_labels( - self, - ax, - labels: dict[int, Waveform | Acquire], - prev_labels: list[dict[int, Waveform | Acquire]], - y0: float, - ) -> None: - """Draw label of pulse instructions on given mpl axis. - - Args: - ax (matplotlib.axes.Axes): axis object to draw labels. - labels: Pulse labels of channel. - prev_labels: Pulse labels of previous channels. - y0: vertical position to draw the labels. - """ - for t0, (tf, cmd) in labels.items(): - if isinstance(cmd, Acquire): - name = cmd.name if cmd.name else "acquire" - else: - name = cmd.name - - ax.annotate( - r"%s" % name, - xy=((t0 + tf) // 2, y0), - xytext=((t0 + tf) // 2, y0 - 0.07), - fontsize=self.style.label_font_size, - ha="center", - va="center", - ) - - linestyle = self.style.label_ch_linestyle - alpha = self.style.label_ch_alpha - color = self.style.label_ch_color - - if not self._prev_label_at_time(prev_labels, t0): - ax.axvline(t0, -1, 1, color=color, linestyle=linestyle, alpha=alpha) - if not (self._prev_label_at_time(prev_labels, tf) or tf in labels): - ax.axvline(tf, -1, 1, color=color, linestyle=linestyle, alpha=alpha) - - def _draw_channels( - self, - ax, - output_channels: dict[Channel, EventsOutputChannels], - interp_method: Callable, - t0: int, - tf: int, - scale_dict: dict[Channel, float], - label: bool = False, - framechange: bool = True, - frequencychange: bool = True, - ) -> float: - """Draw pulse instructions on given mpl axis. - - Args: - ax (matplotlib.axes.Axes): axis object to draw pulses. - output_channels: Event table of channels. - interp_method: Callback function for waveform interpolation. - t0: Start time of schedule. - tf: End time of schedule. - scale_dict: Scale factor for each channel. - label: When set `True` draw labels. - framechange: When set `True` draw frame change symbols. - frequencychange: When set `True` draw frequency change symbols. - - Return: - Value of final vertical axis of canvas. - """ - y0 = 0 - prev_labels: list[dict[int, Waveform | Acquire]] = [] - for channel, events in output_channels.items(): - if events.enable: - # scaling value of this channel - scale = 0.5 * scale_dict.get(channel, 0.5) - # plot waveform - waveform = events.waveform - time = np.arange(t0, tf + 1, dtype=float) - if waveform.any(): - time, re, im = interp_method(time, waveform, self.style.num_points) - else: - # when input schedule is empty or comprises only frame changes, - # we should avoid interpolation due to lack of data points. - # instead, it just returns vector of zero. - re, im = np.zeros_like(time), np.zeros_like(time) - color = self._get_channel_color(channel) - # Minimum amplitude scaled - amp_min = scale * abs(min(0, np.nanmin(re), np.nanmin(im))) - # scaling and offset - re = scale * re + y0 - im = scale * im + y0 - offset = np.zeros_like(time) + y0 - # plot - ax.fill_between( - x=time, - y1=re, - y2=offset, - facecolor=color[0], - alpha=0.3, - edgecolor=color[0], - linewidth=1.5, - label="real part", - ) - ax.fill_between( - x=time, - y1=im, - y2=offset, - facecolor=color[1], - alpha=0.3, - edgecolor=color[1], - linewidth=1.5, - label="imaginary part", - ) - ax.plot((t0, tf), (y0, y0), color="#000000", linewidth=1.0) - - # plot frame changes - fcs = events.framechanges - if fcs and framechange: - self._draw_framechanges(ax, fcs, y0) - # plot frequency changes - sf = events.frequencychanges - if sf and frequencychange: - self._draw_frequency_changes(ax, sf, y0 + 0.05) - # plot labels - labels = events.labels - if labels and label: - self._draw_labels(ax, labels, prev_labels, y0) - prev_labels.append(labels) - - else: - continue - - # plot label - ax.text( - x=t0, - y=y0, - s=channel.name, - fontsize=self.style.axis_font_size, - ha="right", - va="center", - ) - # show scaling factor - ax.text( - x=t0, - y=y0 - 0.1, - s="x%.1f" % (2 * scale), - fontsize=0.7 * self.style.axis_font_size, - ha="right", - va="top", - ) - - # change the y0 offset for removing spacing when a channel has negative values - if self.style.remove_spacing: - y0 -= 0.5 + amp_min - else: - y0 -= 1 - return y0 - - def draw( - self, - schedule: ScheduleComponent, - dt: float, - interp_method: Callable, - plot_range: tuple[float, float], - scale: float | None = None, - channel_scales: dict[Channel, float] | None = None, - plot_all: bool = True, - table: bool = False, - label: bool = False, - framechange: bool = True, - channels: Sequence[Channel] | None = None, - show_framechange_channels: bool = True, - draw_title: bool = False, - ): - """Draw figure. - - Args: - schedule: schedule object to plot. - dt: Time interval of samples. Pulses are visualized in the unit of - cycle time if not provided. - interp_method: Interpolation function. See example. - Interpolation is disabled in default. - See `qiskit.visualization.pulse.interpolation` for more information. - plot_range: A tuple of time range to plot. - scale: Scaling of waveform amplitude. Pulses are automatically - scaled channel by channel if not provided. - channel_scales: Dictionary of scale factor for specific channels. - Scale of channels not specified here is overwritten by `scale`. - plot_all: When set `True` plot empty channels. - table: When set `True` draw event table for supported commands. - label: When set `True` draw label for individual instructions. - framechange: When set `True` draw framechange indicators. - channels: A list of channel names to plot. - All non-empty channels are shown if not provided. - show_framechange_channels: When set `True` plot channels - with only framechange instructions. - draw_title: Add a title to the plot when set to ``True``. - - Returns: - matplotlib.figure.Figure: A matplotlib figure object for the pulse envelope. - - Raises: - VisualizationError: When schedule cannot be drawn - """ - figure = self.plt_mod.figure(dpi=self.style.dpi, figsize=self.style.figsize) - - if channels is None: - channels = [] - interp_method = interp_method or step_wise - - if channel_scales is None: - channel_scales = {} - - # setup plot range - if plot_range: - t0 = int(np.floor(plot_range[0])) - tf = int(np.floor(plot_range[1])) - else: - t0 = 0 - # when input schedule is empty or comprises only frame changes, - # we need to overwrite pulse duration by an integer greater than zero, - # otherwise waveform returns empty array and matplotlib will be crashed. - if channels: - tf = schedule.ch_duration(*channels) - else: - tf = schedule.stop_time - tf = tf or 1 - - # prepare waveform channels - (schedule_channels, output_channels, snapshot_channels) = self._build_channels( - schedule, channels, t0, tf, show_framechange_channels - ) - - # count numbers of valid waveform - scale_dict = self._scale_channels( - output_channels, - scale=scale, - channel_scales=channel_scales, - channels=channels, - plot_all=plot_all, - ) - - if table: - tb, ax = self._draw_table(figure, schedule_channels, dt) - else: - tb = None - ax = figure.add_subplot(111) - - ax.set_facecolor(self.style.bg_color) - - y0 = self._draw_channels( - ax, - output_channels, - interp_method, - t0, - tf, - scale_dict, - label=label, - framechange=framechange, - ) - - y_ub = 0.5 + self.style.vertical_span - y_lb = y0 + 0.5 - self.style.vertical_span - - self._draw_snapshots(ax, snapshot_channels, y_lb) - - ax.set_xlim(t0, tf) - tick_labels = np.linspace(t0, tf, 5) - ax.set_xticks(tick_labels) - ax.set_xticklabels( - [self.style.axis_formatter % label for label in tick_labels * dt], - fontsize=self.style.axis_font_size, - ) - ax.set_ylim(y_lb, y_ub) - ax.set_yticklabels([]) - - if tb is not None: - bbox = tb.get_position() - else: - bbox = ax.get_position() - - # This check is here for backwards compatibility. Before, the check was around - # the suptitle line, however since the font style can take on a type of None - # we need to unfortunately check both the type and the value of the object. - if isinstance(self.style.title_font_size, int) and self.style.title_font_size > 0: - if draw_title: - figure.suptitle( - schedule.name, - fontsize=self.style.title_font_size, - y=bbox.y1 + 0.02, - va="bottom", - ) - return figure diff --git a/qiskit/visualization/pulse/qcstyle.py b/qiskit/visualization/pulse/qcstyle.py deleted file mode 100644 index 5ef6ba33510f..000000000000 --- a/qiskit/visualization/pulse/qcstyle.py +++ /dev/null @@ -1,224 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Deprecated. - -Style sheets for pulse visualization. -""" -from __future__ import annotations -import logging -from collections import namedtuple - -from qiskit.utils.deprecation import deprecate_func - -logger = logging.getLogger(__name__) -ComplexColors = namedtuple("ComplexColors", ["real", "imaginary"]) -SchedTableColors = namedtuple("SchedTableColors", ["time", "channel", "event"]) - - -class SchedStyle: - """Style sheet for Qiskit-Pulse schedule drawer.""" - - @deprecate_func( - additional_msg=( - "In addition to this stylesheet being deprecated, the legacy pulse drawer is too. " - "Instead, use the new drawer ``qiskit.visualization.pulse_drawer`` " - "with new stylesheet classes provided by ``qiskit.visualization.pulse_v2``. " - "You can choose one of ``IQXStandard``, ``IQXSimple``, ``IQXDebugging``." - ), - since="0.23.0", - ) - def __init__( - self, - figsize: tuple[float, float] | None = (10.0, 12.0), - fig_unit_h_table: float = 0.4, - use_table: bool = True, - table_columns: int = 2, - table_font_size: int = 10, - axis_font_size: int = 18, - label_font_size: int = 10, - icon_font_size: int = 18, - title_font_size: int | None = 25, - label_ch_linestyle: str = "--", - label_ch_color: str = "#222222", - label_ch_alpha: float = 0.3, - d_ch_color: ComplexColors = ComplexColors("#648fff", "#002999"), - u_ch_color: ComplexColors = ComplexColors("#ffb000", "#994A00"), - m_ch_color: ComplexColors = ComplexColors("#dc267f", "#760019"), - s_ch_color: str = "#7da781", - s_ch_linestyle: str = "-", - table_color: SchedTableColors = SchedTableColors("#e0e0e0", "#f6f6f6", "#f6f6f6"), - bg_color: str = "#f2f3f4", - num_points: int = 1000, - dpi: int | None = 150, - remove_spacing: bool = True, - max_table_ratio: float = 0.5, - vertical_span: float = 0.2, - axis_formatter: str = "%s", - ): - """Deprecated. - - Create new style sheet. - - For any of the Optional fields, if that field is None then it will revert to its - matplotlib.rcParams counterpart. See for usage on rcParams. Each argument that - is optional also describes it's mapped rcParam key below. - - Args: - figsize: Size of the figure. - If ``None``, will default to the figure size of the drawing backend. - If the output is ``matplotlib``, the default - parameter is ``rcParams['figure.figsize']``. - fig_unit_h_table: Height of row of event table. See Example. - use_table: When set `True` use event table. - table_columns: Number of event table columns. - table_font_size: Font size of event table. - axis_font_size: Font size of channel aliases. - If ``None``, will revert to the axis label size of the drawing backend. - If the output is ``matplotlib``, the default parameter is - ``rcParams['axes.titlesize']``. - label_font_size: Font size of labels in canvas. - icon_font_size: Size of symbols. - title_font_size: Font size of schedule name in title. - If ``None``, will default to the title font size of the drawing backend. - If the output is ``matplotlib``, the default - parameter is ``rcParams['figure.titlesize']``. - label_ch_linestyle: Line style for channel pulse label line. - label_ch_color: Color code or name of color for channel pulse label line. - label_ch_alpha: Transparency for channel pulse label line from 0 to 1. - d_ch_color: Color code or name of colors for real and imaginary part - of waveform at d channels. - u_ch_color: Color code or name of colors for real and imaginary part - of waveform at u channels. - m_ch_color: Color code or name of colors for real and imaginary part - of waveform at m channels. - s_ch_color: Color code or name of color for snapshot channel line. - s_ch_linestyle: Line style for snapshot line. - table_color: Color code or name of color for event table columns of - time, channel, event information. - bg_color: Color code or name of color for canvas background. - num_points: Number of points for interpolation of each channel. - dpi: Resolution in the unit of dot per inch to save image. - If ``None``, will revert to the DPI setting of the drawing backend. - If the output is ``matplotlib``, the default - parameter is ``rcParams['figure.dpi']``. - remove_spacing: Remove redundant spacing - when the waveform has no negative values. - max_table_ratio: Maximum portion of the plot the table can take up. - Limited to range between 0 and 1. - vertical_span: Spacing on top and bottom of pulse canvas. - axis_formatter: Format of horizontal axis of the plot. This is convenient when - you set ``dt`` option for the drawer. For example, formatter of ``%.3e`` gives you - horizontal axis values in the scientific notation with 3 digits. - - Example: - Height of the event table is decided by multiple parameters.:: - - figsize = (10.0, 12.0) - fig_unit_h_table = 0.4 - table_columns = 2 - max_table_ratio = 0.5 - - With this setup, events are shown in double-column style with - each line height of 0.4 inch and the table cannot exceed 5 inch. - Thus 12 lines are maximum and up to 24 events can be shown. - If you want to show more events, increase figure height or - reduce size of line height and table font size. - """ - self.figsize = figsize - self.fig_unit_h_table = fig_unit_h_table - self.use_table = use_table - self.table_columns = table_columns - self.table_font_size = table_font_size - self.axis_font_size = axis_font_size - self.label_font_size = label_font_size - self.icon_font_size = icon_font_size - self.title_font_size = title_font_size - self.d_ch_color = d_ch_color - self.label_ch_linestyle = label_ch_linestyle - self.label_ch_color = label_ch_color - self.label_ch_alpha = label_ch_alpha - self.u_ch_color = u_ch_color - self.m_ch_color = m_ch_color - self.a_ch_color = m_ch_color - self.s_ch_color = s_ch_color - self.s_ch_linestyle = s_ch_linestyle - self.table_color = table_color - self.bg_color = bg_color - self.num_points = num_points - self.dpi = dpi - self.remove_spacing = remove_spacing - - if max_table_ratio < 0.0 or max_table_ratio > 1.0: - logger.warning( - "max_table_ratio of %.2f is not in range [0.0, 1.0], clamping...", max_table_ratio - ) - - self.max_table_ratio = max(min(max_table_ratio, 0.0), 1.0) - self.vertical_span = vertical_span - self.axis_formatter = axis_formatter - - -class PulseStyle: - """Deprecated. - - Style sheet for Qiskit-Pulse sample pulse drawer.""" - - @deprecate_func( - additional_msg=( - "In addition to this stylesheet being deprecated, the legacy pulse drawer is too. " - "Instead, use the new drawer ``qiskit.visualization.pulse_drawer`` " - "with new stylesheet classes provided by ``qiskit.visualization.pulse_v2``. " - "You can choose one of ``IQXStandard``, ``IQXSimple``, ``IQXDebugging``." - ), - since="0.23.0", - ) - def __init__( - self, - figsize: tuple[float, float] | None = (7.0, 5.0), - title_font_size: int | None = 18, - wave_color: ComplexColors = ComplexColors("#ff0000", "#0000ff"), - bg_color: str = "#f2f3f4", - num_points: int = 1000, - dpi: int | None = None, - ): - """Create new style sheet. - - For any of the Optional fields, if that field is None then it will revert to its - matplotlib.rcParams counterpart. See for usage on rcParams. Each argument that - is optional also describes it's mapped rcParam key below. - - Args: - figsize: Size of the figure. - If ``None``, will default to the figure size of the drawing backend. - If the output is ``matplotlib``, the default - parameter is ``rcParams['figure.figsize']``. - title_font_size: Font size of schedule name in title. - If ``None``, will default to the title font size of the drawing backend. - If the output is ``matplotlib``, the default - parameter is ``rcParams['figure.titlesize']``. - wave_color: Color code or name of colors for - the real and imaginary parts of the waveform. - bg_color: Color code or name of color for pulse canvas background. - num_points: Number of points for interpolation. - dpi: Resolution in the unit of dot per inch to save image. - If ``None``, will revert to the DPI setting of the drawing backend. - If the output is ``matplotlib``, the default - parameter is ``rcParams['figure.dpi']``. - """ - self.figsize = figsize - self.title_font_size = title_font_size - self.wave_color = wave_color - self.bg_color = bg_color - self.num_points = num_points - self.dpi = dpi diff --git a/qiskit/visualization/pulse_v2/core.py b/qiskit/visualization/pulse_v2/core.py index 42cfa224c5d4..d60f2db030d0 100644 --- a/qiskit/visualization/pulse_v2/core.py +++ b/qiskit/visualization/pulse_v2/core.py @@ -205,11 +205,7 @@ def time_breaks(self, new_breaks: list[tuple[int, int]]): def load_program( self, - program: pulse.Waveform - | pulse.ParametricPulse - | pulse.SymbolicPulse - | pulse.Schedule - | pulse.ScheduleBlock, + program: pulse.Waveform | pulse.SymbolicPulse | pulse.Schedule | pulse.ScheduleBlock, ): """Load a program to draw. @@ -221,7 +217,7 @@ def load_program( """ if isinstance(program, (pulse.Schedule, pulse.ScheduleBlock)): self._schedule_loader(program) - elif isinstance(program, (pulse.Waveform, pulse.ParametricPulse, pulse.SymbolicPulse)): + elif isinstance(program, (pulse.Waveform, pulse.SymbolicPulse)): self._waveform_loader(program) else: raise VisualizationError("Data type %s is not supported." % type(program)) @@ -234,7 +230,7 @@ def load_program( def _waveform_loader( self, - program: pulse.Waveform | pulse.ParametricPulse | pulse.SymbolicPulse, + program: pulse.Waveform | pulse.SymbolicPulse, ): """Load Waveform instance. diff --git a/qiskit/visualization/pulse_v2/generators/waveform.py b/qiskit/visualization/pulse_v2/generators/waveform.py index 7d14b522957f..b0d90b895c7a 100644 --- a/qiskit/visualization/pulse_v2/generators/waveform.py +++ b/qiskit/visualization/pulse_v2/generators/waveform.py @@ -271,7 +271,7 @@ def gen_waveform_max_value( if isinstance(data.inst, instructions.Play): # pulse operand = data.inst.pulse - if isinstance(operand, (pulse.ParametricPulse, pulse.SymbolicPulse)): + if isinstance(operand, pulse.SymbolicPulse): pulse_data = operand.get_waveform() else: pulse_data = operand @@ -563,7 +563,7 @@ def _parse_waveform( if isinstance(inst, instructions.Play): # pulse operand = inst.pulse - if isinstance(operand, (pulse.ParametricPulse, pulse.SymbolicPulse)): + if isinstance(operand, pulse.SymbolicPulse): # parametric pulse params = operand.parameters duration = params.pop("duration", None) diff --git a/qiskit/visualization/pulse_v2/interface.py b/qiskit/visualization/pulse_v2/interface.py index 70b3922d8501..d11a02863d2e 100644 --- a/qiskit/visualization/pulse_v2/interface.py +++ b/qiskit/visualization/pulse_v2/interface.py @@ -22,7 +22,7 @@ from typing import Union, Optional, Dict, Any, Tuple, List from qiskit.providers import Backend -from qiskit.pulse import Waveform, ParametricPulse, SymbolicPulse, Schedule, ScheduleBlock +from qiskit.pulse import Waveform, SymbolicPulse, Schedule, ScheduleBlock from qiskit.pulse.channels import Channel from qiskit.visualization.exceptions import VisualizationError from qiskit.visualization.pulse_v2 import core, device_info, stylesheet, types @@ -30,7 +30,7 @@ def draw( - program: Union[Waveform, ParametricPulse, SymbolicPulse, Schedule, ScheduleBlock], + program: Union[Waveform, SymbolicPulse, Schedule, ScheduleBlock], style: Optional[Dict[str, Any]] = None, backend: Optional[Backend] = None, time_range: Optional[Tuple[int, int]] = None, @@ -47,9 +47,8 @@ def draw( Args: program: Program to visualize. This program can be arbitrary Qiskit Pulse program, - such as :py:class:`~qiskit.pulse.Waveform`, :py:class:`~qiskit.pulse.ParametricPulse`, - :py:class:`~qiskit.pulse.SymbolicPulse`, :py:class:`~qiskit.pulse.Schedule` - and :py:class:`~qiskit.pulse.ScheduleBlock`. + such as :py:class:`~qiskit.pulse.Waveform`, :py:class:`~qiskit.pulse.SymbolicPulse`, + :py:class:`~qiskit.pulse.Schedule` and :py:class:`~qiskit.pulse.ScheduleBlock`. style: Stylesheet options. This can be dictionary or preset stylesheet classes. See :py:class:`~qiskit.visualization.pulse_v2.stylesheets.IQXStandard`, :py:class:`~qiskit.visualization.pulse_v2.stylesheets.IQXSimple`, and diff --git a/qiskit/visualization/pulse_v2/layouts.py b/qiskit/visualization/pulse_v2/layouts.py index 9aa368466215..6b39dceaf567 100644 --- a/qiskit/visualization/pulse_v2/layouts.py +++ b/qiskit/visualization/pulse_v2/layouts.py @@ -67,7 +67,7 @@ def my_horizontal_axis(time_window: Tuple[int, int], This data provides input program and backend system configurations. ```python - def my_figure_title(program: Union[pulse.Waveform, pulse.ParametricPulse, pulse.Schedule], + def my_figure_title(program: Union[pulse.Waveform, pulse.Schedule], device: DrawerBackendInfo) -> str: return 'title' @@ -361,9 +361,7 @@ def time_map_in_ns( ) -def detail_title( - program: Union[pulse.Waveform, pulse.ParametricPulse, pulse.Schedule], device: DrawerBackendInfo -) -> str: +def detail_title(program: Union[pulse.Waveform, pulse.Schedule], device: DrawerBackendInfo) -> str: """Layout function for generating figure title. This layout writes program name, program duration, and backend name in the title. @@ -388,8 +386,6 @@ def detail_title( return ", ".join(title_str) -def empty_title( - program: Union[pulse.Waveform, pulse.ParametricPulse, pulse.Schedule], device: DrawerBackendInfo -) -> str: +def empty_title(program: Union[pulse.Waveform, pulse.Schedule], device: DrawerBackendInfo) -> str: """Layout function for generating an empty figure title.""" return "" diff --git a/qiskit/visualization/state_visualization.py b/qiskit/visualization/state_visualization.py index 15e1d1d4c3de..26d3c792986c 100644 --- a/qiskit/visualization/state_visualization.py +++ b/qiskit/visualization/state_visualization.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2018. +# (C) Copyright IBM 2017, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -17,7 +17,7 @@ Visualization functions for quantum states. """ -from typing import Optional, List, Union +from typing import List, Union from functools import reduce import colorsys @@ -27,7 +27,6 @@ from qiskit.quantum_info.operators.operator import Operator from qiskit.quantum_info.operators.symplectic import PauliList, SparsePauliOp from qiskit.quantum_info.states.densitymatrix import DensityMatrix -from qiskit.utils.deprecation import deprecate_func from qiskit.utils import optionals as _optionals from qiskit.circuit.tools.pi_check import pi_check @@ -1280,48 +1279,6 @@ def state_to_latex( return prefix + latex_str + suffix -@deprecate_func( - additional_msg="For similar functionality, see sympy's ``nsimplify`` and ``latex`` functions.", - since="0.23.0", -) -def num_to_latex_ket(raw_value: complex, first_term: bool, decimals: int = 10) -> Optional[str]: - """Convert a complex number to latex code suitable for a ket expression - - Args: - raw_value: Value to convert - first_term: If True then generate latex code for the first term in an expression - decimals: Number of decimal places to round to (default: 10). - Returns: - String with latex code or None if no term is required - """ - if np.around(np.abs(raw_value), decimals=decimals) == 0: - return None - return _num_to_latex(raw_value, first_term=first_term, decimals=decimals, coefficient=True) - - -@deprecate_func( - additional_msg="For similar functionality, see sympy's ``nsimplify`` and ``latex`` functions.", - since="0.23.0", -) -def numbers_to_latex_terms(numbers: List[complex], decimals: int = 10) -> List[str]: - """Convert a list of numbers to latex formatted terms - The first non-zero term is treated differently. For this term a leading + is suppressed. - Args: - numbers: List of numbers to format - decimals: Number of decimal places to round to (default: 10). - Returns: - List of formatted terms - """ - first_term = True - terms = [] - for number in numbers: - term = num_to_latex_ket(number, first_term, decimals) - if term is not None: - first_term = False - terms.append(term) - return terms - - def _numbers_to_latex_terms(numbers: List[complex], decimals: int = 10) -> List[str]: """Convert a list of numbers to latex formatted terms diff --git a/qiskit_pkg/LICENSE.txt b/qiskit_pkg/LICENSE.txt deleted file mode 120000 index 4ab43736a839..000000000000 --- a/qiskit_pkg/LICENSE.txt +++ /dev/null @@ -1 +0,0 @@ -../LICENSE.txt \ No newline at end of file diff --git a/qiskit_pkg/MANIFEST.in b/qiskit_pkg/MANIFEST.in deleted file mode 100644 index 42eb4101e514..000000000000 --- a/qiskit_pkg/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include LICENSE.txt diff --git a/qiskit_pkg/README.md b/qiskit_pkg/README.md deleted file mode 120000 index 32d46ee883b5..000000000000 --- a/qiskit_pkg/README.md +++ /dev/null @@ -1 +0,0 @@ -../README.md \ No newline at end of file diff --git a/qiskit_pkg/setup.py b/qiskit_pkg/setup.py deleted file mode 100644 index 4846b2d0b9f6..000000000000 --- a/qiskit_pkg/setup.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# This file is the setup.py file for the qiskit package. Because python -# packaging doesn't offer a mechanism to have qiskit supersede qiskit-terra -# and cleanly upgrade from one to the other, there needs to be a separate -# package shim to ensure no matter how people installed qiskit < 0.45.0 the -# upgrade works. - -import os - -from setuptools import setup - -README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "README.md") -with open(README_PATH) as readme_file: - README = readme_file.read() - -requirements = ["qiskit-terra==1.0.0"] - -setup( - name="qiskit", - version="1.0.0", - description="Software for developing quantum computing programs", - long_description=README, - long_description_content_type="text/markdown", - url="https://qiskit.org/", - author="Qiskit Development Team", - author_email="hello@qiskit.org", - license="Apache 2.0", - py_modules=[], - packages=[], - classifiers=[ - "Environment :: Console", - "License :: OSI Approved :: Apache Software License", - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - "Operating System :: Microsoft :: Windows", - "Operating System :: MacOS", - "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Topic :: Scientific/Engineering", - ], - keywords="qiskit sdk quantum", - install_requires=requirements, - project_urls={ - "Bug Tracker": "https://github.com/Qiskit/qiskit/issues", - "Documentation": "https://qiskit.org/documentation/", - "Source Code": "https://github.com/Qiskit/qiskit", - }, - include_package_data=True, - python_requires=">=3.8", - extras_require={ - "qasm3-import": ["qiskit-terra[qasm3-import]"], - "visualization": ["qiskit-terra[visualization]"], - "crosstalk-pass": ["qiskit-terra[crosstalk-pass]"], - "csp-layout-pass": ["qiskit-terra[csp-layout-pass]"], - "all": ["qiskit-terra[all]"], - }, -) diff --git a/releasenotes/config.yaml b/releasenotes/config.yaml index 141d552cd9f0..ad71f5dffae4 100644 --- a/releasenotes/config.yaml +++ b/releasenotes/config.yaml @@ -1,3 +1,6 @@ --- encoding: utf8 default_branch: main +collapse_pre_releases: true +pre_release_tag_re: (?P(?:[ab]|rc|pre)+\d*)$ +unreleased_version_title: Unreleased Notes Preview diff --git a/releasenotes/notes/0.45/removed_deprecated_0.21-6c93f7bbc50ae40e.yaml b/releasenotes/notes/0.45/removed_deprecated_0.21-6c93f7bbc50ae40e.yaml index 5f609ad91b89..6c7b67336b80 100644 --- a/releasenotes/notes/0.45/removed_deprecated_0.21-6c93f7bbc50ae40e.yaml +++ b/releasenotes/notes/0.45/removed_deprecated_0.21-6c93f7bbc50ae40e.yaml @@ -3,42 +3,23 @@ upgrade: - | The function :func:`~qiskit.execute_function.execute` does not accept the arguments `qobj_id` and `qobj_header` any more. - Their use was deprecated in Qiskit 0.37 (with Terra 0.21), released on June 2022. + Their use was deprecated in Qiskit 0.37 (with Terra 0.21), released on June 2022. - | - The transpilation pass ``qiskit.transpiler.passes.ALAPSchedule`` is removed. It use was deprecated - in Qiskit 0.37 (with Terra 0.21), released on June 2022 and replaced by - :class:`~.transpiler.passes.ALAPScheduleAnalysis`, which is an - analysis pass. - - | - The transpilation pass ``qiskit.transpiler.passes.ASAPSchedule`` is removed. It use was deprecated - in Qiskit 0.37 (with Terra 0.21), released on June 2022. It has been superseded by - :class:`~.ASAPScheduleAnalysis` and the new scheduling workflow. - - | - The transpilation pass ``qiskit.transpiler.passes.DynamicalDecoupling`` is removed. It use was deprecated - in Qiskit 0.37 (with Terra 0.21), released on June 2022. - Instead, use :class:`~.transpiler.passes.PadDynamicalDecoupling`, which performs the same - function but requires scheduling and alignment analysis passes to run prior to it. - - | - The transpilation pass ``qiskit.transpiler.passes.AlignMeasures`` is removed. It use was deprecated - in Qiskit 0.37 (with Terra 0.21), released on June 2022. - Instead, use :class:`~.ConstrainedReschedule`, which performs the same function - and also supports aligning to additional timing constraints. - - | - The transpilation pass ``qiskit.transpiler.passes.CXDirection`` is removed. It use was deprecated + The transpilation pass ``qiskit.transpiler.passes.CXDirection`` is removed. It use was deprecated in Qiskit 0.37 (with Terra 0.21), released on June 2022. Instead, use the more generic :class:`~.GateDirection` pass. - | - The transpilation pass ``qiskit.transpiler.passes.CheckCXDirection`` is removed. It use was deprecated + The transpilation pass ``qiskit.transpiler.passes.CheckCXDirection`` is removed. It use was deprecated in Qiskit 0.37 (with Terra 0.21), released on June 2022. Instead, use the more generic :class:`~.CheckGateDirection` pass. - | The methods ``to_dict`` in the classes :class:`.pulse.transforms.AlignmentKind`, `.pulse.transforms.AlignEquispaced`, and :class:`.pulse.transforms.AlignFunc` are removed. - They were deprecated + They were deprecated in Qiskit 0.37 (with Terra 0.21), released on June 2022. - | The argument ``circuits`` in the method :meth:`qiskit.qpy.interface.dump` - is removed as its usage was deprecated + is removed as its usage was deprecated in Qiskit 0.37 (with Terra 0.21), released on June 2022. Instead, use the argument ``programs``, which behaves identically. - + diff --git a/releasenotes/notes/5079_IntegerComparator_num_ancilla_qubits-bd1cff3366c345ae.yaml b/releasenotes/notes/5079_IntegerComparator_num_ancilla_qubits-bd1cff3366c345ae.yaml new file mode 100644 index 000000000000..9364ffd605b3 --- /dev/null +++ b/releasenotes/notes/5079_IntegerComparator_num_ancilla_qubits-bd1cff3366c345ae.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The property ``IntegerComparator.num_ancilla_qubits`` is removed, which was + deprecated in Qiskit 0.23 (released on Oct 2020). Its functionality is fully covered + by :attr:`.IntegerComparator.num_ancilla`. diff --git a/releasenotes/notes/Update_backend_model_up_conversion_logic-75ecc2030a9fe6b1.yaml b/releasenotes/notes/Update_backend_model_up_conversion_logic-75ecc2030a9fe6b1.yaml new file mode 100644 index 000000000000..9080273a97a7 --- /dev/null +++ b/releasenotes/notes/Update_backend_model_up_conversion_logic-75ecc2030a9fe6b1.yaml @@ -0,0 +1,19 @@ +--- +upgrade: + - | + The new logic provides better backend model up-conversion mechanism, and better handling of control flow instructions. +fixes: + - | + Fixes return of improper Schedule by Backend.instruction_schedule_map.get('measure', [0]) + + .. code-block:: python + + #import a fake backend which is a sub-class of BackendV2. + from qiskit.providers.fake_provider import FakePerth + backend = FakePerth() + sched = backend.instruction_schedule_map.get('measure', [0]) + + The issue was that the :code:`sched` contained Schedule for measure operation on + all qubits of the backend instead of having the Schedule for measure operation + on just qubit_0. + diff --git a/releasenotes/notes/__qiskit_version__11305-bcf134513641462b.yaml b/releasenotes/notes/__qiskit_version__11305-bcf134513641462b.yaml new file mode 100644 index 000000000000..c1cae7fde91b --- /dev/null +++ b/releasenotes/notes/__qiskit_version__11305-bcf134513641462b.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + The variable ``qiskit.__qiskit_version__`` is removed as it was deprecated since + Qiskit 0.44 (released on July 2023). + Instead, you should use ``qiskit.__version__``. The other packages listed in the + former ``qiskit.__qiskit_version__`` have their own ``__version__`` module level dunder, + as standard in PEP 8. diff --git a/releasenotes/notes/add-filter-op-nodes-aa024a0f1058e4b7.yaml b/releasenotes/notes/add-filter-op-nodes-aa024a0f1058e4b7.yaml new file mode 100644 index 000000000000..9c5a0a5c43f8 --- /dev/null +++ b/releasenotes/notes/add-filter-op-nodes-aa024a0f1058e4b7.yaml @@ -0,0 +1,16 @@ +--- +features: + - | + Added a new transpiler pass :class:`.FilterOpNodes` which is used to filter + :class:`.DAGOpNode`\s in a :class:`.DAGCircuit`. + - | + Added a new keyword argument, ``label``, to the constructor on the + :class:`.BarrierBeforeFinalMeasurements` transpiler pass. If specified the + inserted barrier will be assigned the specified label. This also prevents + the inserted barrier from being merged with any any other pre-existing + adjacent barriers. +other: + - | + The preset pass managers used by :func:`.transpile` and returned with + :class:`.generate_preset_pass_manager` will no longer insert barriers + before final measurements in the output circuits. diff --git a/releasenotes/notes/add-parameter-pow-ff5f8d10813f5733.yaml b/releasenotes/notes/add-parameter-pow-ff5f8d10813f5733.yaml new file mode 100644 index 000000000000..094266071990 --- /dev/null +++ b/releasenotes/notes/add-parameter-pow-ff5f8d10813f5733.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + :class:`~qiskit.circuit.ParameterExpression` (and thus also + :class:`~qiskit.circuit.Parameter`) now support powering: :code:`x**y` + where :code:`x` and :code:`y` can be any combination of + :class:`~qiskit.circuit.ParameterExpression` and number types. diff --git a/releasenotes/notes/add-py312-support-7077426af34ac5da.yaml b/releasenotes/notes/add-py312-support-7077426af34ac5da.yaml new file mode 100644 index 000000000000..4e2be1e92983 --- /dev/null +++ b/releasenotes/notes/add-py312-support-7077426af34ac5da.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Added support for using Qiskit with Python 3.12. diff --git a/releasenotes/notes/allow-none-layout-for-sparse_pauli_op-212470d5b5b307a0.yaml b/releasenotes/notes/allow-none-layout-for-sparse_pauli_op-212470d5b5b307a0.yaml new file mode 100644 index 000000000000..d40486990275 --- /dev/null +++ b/releasenotes/notes/allow-none-layout-for-sparse_pauli_op-212470d5b5b307a0.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + The function :meth:`~.SparsePauliOp.apply_layout` from :class:`.SparsePauliOp` now allows for the + layout argument to also be None. That is, the method can now also be used for circuits where no transpilation/routing + took place (for example when transpiling for a simulator). diff --git a/releasenotes/notes/bit-interning-35da0aaa76aa7fc5.yaml b/releasenotes/notes/bit-interning-35da0aaa76aa7fc5.yaml new file mode 100644 index 000000000000..bb1c83f76f33 --- /dev/null +++ b/releasenotes/notes/bit-interning-35da0aaa76aa7fc5.yaml @@ -0,0 +1,17 @@ +--- +upgrade: + - | + To support a more compact in-memory representation, the + :class:`.QuantumCircuit` class is now limited to supporting + a maximum of ``2^32 (=4,294,967,296)`` qubits and clbits, + for each of these two bit types (the limit is not combined). + The number of unique sequences of indices used in + :attr:`.CircuitInstruction.qubits` and + :attr:`.CircuitInstruction.clbits` is also limited to ``2^32`` + for instructions added to a single circuit. +other: + - | + The :class:`.QuantumCircuit` class now performs interning for the + ``qubits`` and ``clbits`` of the :class:`.CircuitInstruction` + instances that it stores, resulting in a potentially significant + reduction in memory footprint, especially for large circuits. diff --git a/releasenotes/notes/classical-store-e64ee1286219a862.yaml b/releasenotes/notes/classical-store-e64ee1286219a862.yaml new file mode 100644 index 000000000000..729c70a6989c --- /dev/null +++ b/releasenotes/notes/classical-store-e64ee1286219a862.yaml @@ -0,0 +1,56 @@ +--- +features: + - | + A :class:`.QuantumCircuit` can now contain typed classical variables:: + + from qiskit.circuit import QuantumCircuit, ClassicalRegister, QuantumRegister + from qiskit.circuit.classical import expr, types + + qr = QuantumRegister(2, "q") + cr = ClassicalRegister(2, "c") + qc = QuantumCircuit(qr, cr) + # Add two input variables to the circuit with different types. + a = qc.add_input("a", types.Bool()) + mask = qc.add_input("mask", types.Uint(2)) + + # Test whether the input variable was true at runtime. + with qc.if_test(a) as else_: + qc.x(0) + with else_: + qc.h(0) + + qc.cx(0, 1) + qc.measure(qr, cr) + + # Add a typed variable manually, initialized to the same value as the classical register. + b = qc.add_var("b", expr.lift(cr)) + + qc.reset([0, 1]) + qc.h(0) + qc.cx(0, 1) + qc.measure(qr, cr) + + # Store some calculated value into the `b` variable. + qc.store(b, expr.bit_and(b, cr)) + # Test whether we had equality, up to a mask. + with qc.if_test(expr.equal(expr.bit_and(b, mask), mask)): + qc.x(0) + + These variables can be specified either as *inputs* to the circuit, or as scoped variables. + The circuit object does not yet have support for representing typed classical-variable *outputs*, + but this will be added later when hardware and the result interfaces are in more of a position + to support it. Circuits that represent a block of an inner scope may also capture variables + from outer scopes. + + A variable is a :class:`.Var` node, which can now contain an arbitrary type, and represents a + unique memory location within its live range when added to a circuit. These can be constructed + in a circuit using :meth:`.QuantumCircuit.add_var` and :meth:`~.QuantumCircuit.add_input`, or + at a lower level using :meth:`.Var.new`. + + Variables can be manually stored to, using the :class:`.Store` instruction and its corresponding + circuit method :meth:`.QuantumCircuit.store`. This includes writing to :class:`.Clbit` and + :class:`.ClassicalRegister` instances wrapped in :class:`.Var` nodes. + + Variables can be used wherever classical expressions (see :mod:`qiskit.circuit.classical.expr`) + are valid. Currently this is the target expressions of control-flow operations, though we plan + to expand this to gate parameters in the future, as the type and expression system are expanded. diff --git a/releasenotes/notes/dep-estimator-paulilist-ef2bb2865b66f012.yaml b/releasenotes/notes/dep-estimator-paulilist-ef2bb2865b66f012.yaml new file mode 100644 index 000000000000..4fad2f28e073 --- /dev/null +++ b/releasenotes/notes/dep-estimator-paulilist-ef2bb2865b66f012.yaml @@ -0,0 +1,7 @@ +--- +deprecations: + - | + Deprecates using a :class:`~.PauliList` as an observable that is implicitly + converted to a :class:`~.SparsePauliOp` with coefficients 1 when calling + :meth:`.Estimator.run`. Users should instead explicitly convert the argument + using ``SparsePauliOp(pauli_list)`` first. diff --git a/releasenotes/notes/dep-primitives-attr-6b4ec9fde34c42e8.yaml b/releasenotes/notes/dep-primitives-attr-6b4ec9fde34c42e8.yaml new file mode 100644 index 000000000000..43e98cf86166 --- /dev/null +++ b/releasenotes/notes/dep-primitives-attr-6b4ec9fde34c42e8.yaml @@ -0,0 +1,13 @@ +--- +deprecations: + - | + The :class:`~.BaseEstimator` attributes :attr:`~.BaseEstimator.circuits` + :attr:`~.BaseEstimator.observables`, :attr:`~.BaseEstimator.parameters` + and :class:`~.BaseSampler` attributes :attr:`~.BaseSampler.circuits` + :attr:`~.BaseSampler.parameters` have been deprecated. + - | + Deprecates the :class:`~.BaseEstimator` ``_circuits``, ``_observables`` + and ``_parameters``, and :class:`~.BaseSampler` ``_circuits`` and + ``_parameters`` attributes set during `__init__`. Any subclasses + relying on these methods should now manually initialize them to avoid + deprecation warnings. diff --git a/releasenotes/notes/expr-hashable-var-types-7cf2aaa00b201ae6.yaml b/releasenotes/notes/expr-hashable-var-types-7cf2aaa00b201ae6.yaml new file mode 100644 index 000000000000..70a1cf81d061 --- /dev/null +++ b/releasenotes/notes/expr-hashable-var-types-7cf2aaa00b201ae6.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Classical types (subclasses of :class:`~classical.types.Type`) and variables (:class:`~.expr.Var`) + are now hashable. diff --git a/releasenotes/notes/expr-var-standalone-2c1116583a2be9fd.yaml b/releasenotes/notes/expr-var-standalone-2c1116583a2be9fd.yaml new file mode 100644 index 000000000000..71ec0320032e --- /dev/null +++ b/releasenotes/notes/expr-var-standalone-2c1116583a2be9fd.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + :class:`~.expr.Var` nodes now have a :attr:`.Var.standalone` property to quickly query whether + they are new-style memory-owning variables, or whether they wrap old-style classical memory in + the form of a :class:`.Clbit` or :class:`.ClassicalRegister`. diff --git a/releasenotes/notes/fix-aqc-optimizer-typehint-34b54c6278d23f79.yaml b/releasenotes/notes/fix-aqc-optimizer-typehint-34b54c6278d23f79.yaml new file mode 100644 index 000000000000..bca717c18745 --- /dev/null +++ b/releasenotes/notes/fix-aqc-optimizer-typehint-34b54c6278d23f79.yaml @@ -0,0 +1,21 @@ +--- +fixes: + - | + The use of the (deprecated) ``Optimizer`` class on :class:`~.AQC` did not have a + non-deprecated alternative path, which should have been introduced in + the original ``qiskit-algorithms`` deprecation PR + [#10406](https://github.com/Qiskit/qiskit/pull/10406). + It now accepts a callable that implements the :class:`~.Minimizer` protocol, + as explicitly stated in the deprecation warning. The callable can look like the + following example: + + .. code-block:: python + + from scipy.optimize import minimize + from qiskit.transpiler.synthesis.aqc.aqc import AQC + + optimizer = partial(minimize, args=(), method="L-BFGS-B", options={"maxiter": 200}) + aqc = AQC(optimizer=optimizer) + + + diff --git a/releasenotes/notes/fix-block-collapser-cregs-6c2d2dc6931d7bad.yaml b/releasenotes/notes/fix-block-collapser-cregs-6c2d2dc6931d7bad.yaml new file mode 100644 index 000000000000..57543efb27bd --- /dev/null +++ b/releasenotes/notes/fix-block-collapser-cregs-6c2d2dc6931d7bad.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The :class:`.BlockCollapser` transpiler pass will now correctly handle circuits that contain + more than one condition on the same classical register. diff --git a/releasenotes/notes/fix-blueprint-circuit-_append-b4d6c9c41db860f5.yaml b/releasenotes/notes/fix-blueprint-circuit-_append-b4d6c9c41db860f5.yaml new file mode 100644 index 000000000000..0596bc50d964 --- /dev/null +++ b/releasenotes/notes/fix-blueprint-circuit-_append-b4d6c9c41db860f5.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + :class:`.BlueprintCircuit` subclasses will now behave correctly when the semi-public method + :meth:`.QuantumCircuit._append` is used with the blueprint in an unbuilt state, *i.e.* the + circuit will be built before attempting the append. diff --git a/releasenotes/notes/fix-circuit-barrier-c696eabae1dcc6c2.yaml b/releasenotes/notes/fix-circuit-barrier-c696eabae1dcc6c2.yaml new file mode 100644 index 000000000000..b16792f8fb3f --- /dev/null +++ b/releasenotes/notes/fix-circuit-barrier-c696eabae1dcc6c2.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + :meth:`.QuantumCircuit.barrier` will now generate correct output when given a :class:`set` as + one of its inputs. Previously, it would append an invalid operation onto the circuit, though in + practice this usually would not cause observable problems. + Fixed `#11208 `__ diff --git a/releasenotes/notes/fix-instruction-condition-bits-17694f98628b30ad.yaml b/releasenotes/notes/fix-instruction-condition-bits-17694f98628b30ad.yaml new file mode 100644 index 000000000000..4f044b6a468f --- /dev/null +++ b/releasenotes/notes/fix-instruction-condition-bits-17694f98628b30ad.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The property :attr:`.Instruction.condition_bits` will now correctly handle runtime classical + expressions (:mod:`qiskit.circuit.classical`). diff --git a/releasenotes/notes/fix-optimize-1q-sim-407b88e45e6062b6.yaml b/releasenotes/notes/fix-optimize-1q-sim-407b88e45e6062b6.yaml new file mode 100644 index 000000000000..f531561789e0 --- /dev/null +++ b/releasenotes/notes/fix-optimize-1q-sim-407b88e45e6062b6.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + Fixed an issue with the :class:`.Optimize1qGatesDecomposition` transpiler + pass where it would potentially resynthesize a single ideal (meaning the + error rate is ``0.0``) gate which was present in the :class:`.Target`. This + is now fixed so the pass :class:`.Optimize1qGatesDecomposition` will defer + to the circuit's gate if the error rate (which includes number of gates) + are the same. + Fixed `#10568 `__ diff --git a/releasenotes/notes/fix-param-global-phase-31547267f6124552.yaml b/releasenotes/notes/fix-param-global-phase-31547267f6124552.yaml new file mode 100644 index 000000000000..13048f626dc4 --- /dev/null +++ b/releasenotes/notes/fix-param-global-phase-31547267f6124552.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixed compatibility of :class:`.DynamicalDecoupling` and + :class:`.PadDynamicalDecoupling` with circuits that have a parameterized + global phase. + Fixed `#10569 `__. diff --git a/releasenotes/notes/fix-parameterized-self-inverse-7cb2d68b273640f8.yaml b/releasenotes/notes/fix-parameterized-self-inverse-7cb2d68b273640f8.yaml new file mode 100644 index 000000000000..7ca796cdd2a5 --- /dev/null +++ b/releasenotes/notes/fix-parameterized-self-inverse-7cb2d68b273640f8.yaml @@ -0,0 +1,22 @@ +--- +fixes: + - | + Fixed an issue with the :class:`~.InverseCancellation` pass where it would + incorrectly cancel gates passed in as self inverses with a parameter + value, if a run of gates had a different parameter value. For example:: + + from math import pi + + from qiskit.circuit.library import RZGate + from qiskit.circuit import QuantumCircuit + from qiskit.transpiler.passes import InverseCancellation + + inverse_pass = InverseCancellation([RZGate(0)]) + + qc = QuantumCircuit(1) + qc.rz(0, 0) + qc.rz(pi, 0) + + inverse_pass(qc) + + would previously have incorrectly cancelled the two rz gates. diff --git a/releasenotes/notes/fix-pulse-channel-hash-549a8fb5d8738c4d.yaml b/releasenotes/notes/fix-pulse-channel-hash-549a8fb5d8738c4d.yaml new file mode 100644 index 000000000000..19d022572963 --- /dev/null +++ b/releasenotes/notes/fix-pulse-channel-hash-549a8fb5d8738c4d.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixed the :func:`hash` of Qiskit Pulse ``Channel`` objects (such as :class:`.DriveChannel`) in + cases where the channel was transferred from one Python process to another that used a different + hash seed. diff --git a/releasenotes/notes/fix-qasm2-deepcopy-conditioned-gate-2fa75fee622c1fc0.yaml b/releasenotes/notes/fix-qasm2-deepcopy-conditioned-gate-2fa75fee622c1fc0.yaml new file mode 100644 index 000000000000..5574dbcab368 --- /dev/null +++ b/releasenotes/notes/fix-qasm2-deepcopy-conditioned-gate-2fa75fee622c1fc0.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Conditioned custom gates imported from OpenQASM 2 will now correctly retain their conditions + when pickled and deep-copied. Previously, any conditional custom gate (defined by a ``gate`` + statement in an OpenQASM 2 file) would lose its condition when copied or pickled. diff --git a/releasenotes/notes/fix-qpy-statepreparation-retrieval-1feea5eb74db7f1e.yaml b/releasenotes/notes/fix-qpy-statepreparation-retrieval-1feea5eb74db7f1e.yaml new file mode 100644 index 000000000000..67cdafcb463a --- /dev/null +++ b/releasenotes/notes/fix-qpy-statepreparation-retrieval-1feea5eb74db7f1e.yaml @@ -0,0 +1,5 @@ +fixes: + - | + Fixed QPY deserialization of the :class:`.StatePreparation` and :class:`.Initialize` circuit + instructions with string and integer parameters (as opposed to an explicit statevector, which + was already working). Fixed `#11158 `__. diff --git a/releasenotes/notes/fix-qreg-visualization-after-layout-42d3e643b923d8bc.yaml b/releasenotes/notes/fix-qreg-visualization-after-layout-42d3e643b923d8bc.yaml new file mode 100644 index 000000000000..8e887846e432 --- /dev/null +++ b/releasenotes/notes/fix-qreg-visualization-after-layout-42d3e643b923d8bc.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Fixed a bug in :class:`~.SabreLayout` where it would fail to add the layout + register information to the property set. This affected circuit visualization, as + ``circuit.draw()`` after transpilation with certain optimization levels would show + the full ``Qubit[register]`` label rather than the expected register name + (e.g. ``q0``). diff --git a/releasenotes/notes/fix-schedule-qpy-use-symengine-05ae1dfab73e3ff8.yaml b/releasenotes/notes/fix-schedule-qpy-use-symengine-05ae1dfab73e3ff8.yaml new file mode 100644 index 000000000000..3fe71509ea7d --- /dev/null +++ b/releasenotes/notes/fix-schedule-qpy-use-symengine-05ae1dfab73e3ff8.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixed an issue with :func:`.qpy.dump` which would cause the function to + potentially ignore the value of ``use_symengine`` when serializing a + :class:`.ScheduleBlock` object. diff --git a/releasenotes/notes/fix-unitary-equal-eb828aca3aaa5e73.yaml b/releasenotes/notes/fix-unitary-equal-eb828aca3aaa5e73.yaml new file mode 100644 index 000000000000..8d090ec5c356 --- /dev/null +++ b/releasenotes/notes/fix-unitary-equal-eb828aca3aaa5e73.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Previously two objects of type :class:`~.UnitaryGate` class were considered equal + even when they had different global phases. This could lead to transpiler + passes not maintaining the global phase and, in theory, to incorrect optimizations. + This commit changes the :meth:`~.UnitaryGate.__eq__` method not to ignore the global + phases in the comparison. diff --git a/releasenotes/notes/fix_11143-d32a262538873a9d.yaml b/releasenotes/notes/fix_11143-d32a262538873a9d.yaml new file mode 100644 index 000000000000..ed04a2520e68 --- /dev/null +++ b/releasenotes/notes/fix_11143-d32a262538873a9d.yaml @@ -0,0 +1,4 @@ +upgrade: + - | + The functions ``qiskit.visualization.state_visualization.num_to_latex_ket`` and ``qiskit.visualization.state_visualization.numbers_to_latex_terms`` + have been removed, as they were removed in Qiskit 0.40 (released in Jan 2023). For similar functionality, see sympy's ``nsimplify`` and ``latex`` functions. \ No newline at end of file diff --git a/releasenotes/notes/fix_sim_backend-f3971b1ed4d0c4e6.yaml b/releasenotes/notes/fix_sim_backend-f3971b1ed4d0c4e6.yaml new file mode 100644 index 000000000000..5abfdc2d00cc --- /dev/null +++ b/releasenotes/notes/fix_sim_backend-f3971b1ed4d0c4e6.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fix `backend_utils.py` for simulator backends with BackendV2. + `is_simulator_backend` and `is_local_backend` returns `True` + if backend name that contains `simulator`. diff --git a/releasenotes/notes/move-synthesis-plugin-error-61e3683bf5a0c225.yaml b/releasenotes/notes/move-synthesis-plugin-error-61e3683bf5a0c225.yaml new file mode 100644 index 000000000000..6ceb03b1b64c --- /dev/null +++ b/releasenotes/notes/move-synthesis-plugin-error-61e3683bf5a0c225.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + The :class:`.UnitarySynthesis` transpiler pass will now generate an error on initialization when + a nonexistent sythesis plugin is specified, rather than waiting until runtime to raise. Fixed + `#11355 `__. diff --git a/releasenotes/notes/new-exception-too-wide-3231c1df15952445.yaml b/releasenotes/notes/new-exception-too-wide-3231c1df15952445.yaml new file mode 100644 index 000000000000..d3bfa72c071e --- /dev/null +++ b/releasenotes/notes/new-exception-too-wide-3231c1df15952445.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Added a new exception class :exc:`.CircuitToWideForTarget` which + subclasses :exc:`.TranspilerError`. It's used in places where a + :exc:`.TranspilerError` was previously raised when the error was that + the number of circuit qubits was larger than the target backend's qubits. + The new class enables more differentiating between this error condition and + other :exc:`.TranspilerError`\s. diff --git a/releasenotes/notes/overlap-circuit-barriers-63b9b1be9c1da100.yaml b/releasenotes/notes/overlap-circuit-barriers-63b9b1be9c1da100.yaml new file mode 100644 index 000000000000..dcae6e5585f1 --- /dev/null +++ b/releasenotes/notes/overlap-circuit-barriers-63b9b1be9c1da100.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Fixed a bug which caused :class:`~qiskit.circuit.library.UnitaryOverlap` to error upon initialization if given an input circuit containing a barrier. diff --git a/releasenotes/notes/platform-support-f7f693aaf5dec044.yaml b/releasenotes/notes/platform-support-f7f693aaf5dec044.yaml new file mode 100644 index 000000000000..ae4235d57065 --- /dev/null +++ b/releasenotes/notes/platform-support-f7f693aaf5dec044.yaml @@ -0,0 +1,29 @@ +--- +upgrade: + - | + The ``symengine`` library is now a hard requirement for all platforms. + Previously, ``symengine`` was required only on platforms that had + pre-compiled packages available and ``sympy`` would be used as a fallback + if it wasn't installed. These split requirements were resulting in increased + complexity, as it was necessary to determine which libraries were installed + to debug an issue. Requiring ``symengine`` for all systems greatly decreases + the complexity and optimizes Qiskit for higher performance. However, + users on i686 Linux, 32 bit Windows, and s390x Linux (the platforms without + precompiled packages on PyPI) will need to build symengine from source. + - | + Support for 32 bit platforms, i686 Linux and 32 bit Windows, on + Python < 3.10 has been downgraded from Tier 2 to Tier 3, as documented in + the `platform support page `_. This is a consequence of making + ``symengine`` required for all users, as there is a lack of pre-compiled packages + available for these platforms, so users will need to build symengine from + source. + - | + For macOS users, the minimum version of macOS is now 10.12. Previously, the + precompiled binary wheel packages for macOS x86_64 were published with + support for >=10.9. However, because of changes in the + `support policy `__ + for the Rust programming language the minimum version needed to raised + to macOS 10.12. If you're using Qiskit on macOS 10.9 you can probably + build Qiskit from source while the Qiskit MSRV (minimum supported Rust + version) is < 1.74, but the precompiled binaries published to PyPI will + only be compatible with macOS >= 10.12. diff --git a/releasenotes/notes/qft_lnn_synthesis-c917dc00c3a8cabc.yaml b/releasenotes/notes/qft_lnn_synthesis-c917dc00c3a8cabc.yaml new file mode 100644 index 000000000000..e03e4fd086f9 --- /dev/null +++ b/releasenotes/notes/qft_lnn_synthesis-c917dc00c3a8cabc.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add a new synthesis method :func:`.synth_qft_line` of a QFT circuit + for linear nearest neighbor connectivity, which significantly reduces + the number of SWAPs for large numbers of qubits compared to SABRE. diff --git a/releasenotes/notes/releasenotes/notes/remove-deprecated-parametric-pulses-class-667e4b970e1163b3.yaml b/releasenotes/notes/releasenotes/notes/remove-deprecated-parametric-pulses-class-667e4b970e1163b3.yaml new file mode 100644 index 000000000000..7ba9d188f55a --- /dev/null +++ b/releasenotes/notes/releasenotes/notes/remove-deprecated-parametric-pulses-class-667e4b970e1163b3.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The module ``ParametricPulse`` is removed. + All classes in it were deprecated since Qiskit 0.39 (with qiskit-terra 0.22), released on Oct 2022. + Instead, use :class:`.SymbolicPulse` and check its documentation for details. \ No newline at end of file diff --git a/releasenotes/notes/remove-algorithm-utils-707648b69af439dc.yaml b/releasenotes/notes/remove-algorithm-utils-707648b69af439dc.yaml new file mode 100644 index 000000000000..67c551dad70f --- /dev/null +++ b/releasenotes/notes/remove-algorithm-utils-707648b69af439dc.yaml @@ -0,0 +1,27 @@ +--- +upgrade: + - | + The following tools in :mod:`qiskit.utils` have been removed from the codebase: + + * Utils in ``qiskit.utils.arithmetic`` + * Utils in ``qiskit.utils.circuit_utils`` + * Utils in ``qiskit.utils.entangler_map`` + * Utils in ``qiskit.utils.name_unnamed_args`` + + These functions were used exclusively in the context of ``qiskit.algorithms`` and + ``qiskit.opflow``, and were deprecated in Qiskit 0.46. ``qiskit.algorithms`` and + ``qiskit.opflow`` have been deprecated since Qiskit 0.45 and Qiskit Terra 0.24 + respectively. + + The following utilities have also been removed: + + * Utils in ``qiskit.utils.validation`` + * ``algorithm_globals`` + + These were deprecated in Qiskit 0.45 as a consequence of the migration + of ``qiskit.algorithms`` to a standalone + `package `_, where + these utils have also been migrated. They can be found in the new package + under ``qiskit_algorithms.utils``. + + diff --git a/releasenotes/notes/remove-dag-none-be220777dc246803.yaml b/releasenotes/notes/remove-dag-none-be220777dc246803.yaml new file mode 100644 index 000000000000..44fbd7d134ee --- /dev/null +++ b/releasenotes/notes/remove-dag-none-be220777dc246803.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + It is no longer allowable to pass ``None`` as the ``qargs`` or ``cargs`` parameters in + :meth:`.DAGCircuit.apply_operation_back` and :meth:`~.DAGCircuit.apply_operation_front`. If you + want to explicitly pass an empty argument, use the empty tuple ``()`` instead. diff --git a/releasenotes/notes/remove-deprecated-visualization-pulse-8adf40ff1a69df63.yaml b/releasenotes/notes/remove-deprecated-visualization-pulse-8adf40ff1a69df63.yaml new file mode 100644 index 000000000000..ab3ba5517880 --- /dev/null +++ b/releasenotes/notes/remove-deprecated-visualization-pulse-8adf40ff1a69df63.yaml @@ -0,0 +1,15 @@ +--- +upgrade: + - | + The classes and methods in :mod:`qiskit.visualization.pulse`: + + * :class:`~qiskit.visualization.pulse.SchedStyle` + * :class:`~qiskit.visualization.pulse.PulseStyle` + * :class:`~qiskit.visualization.pulse.matplotlib.EventsOutputChannels` + * :class:`~qiskit.visualization.pulse.matplotlib.WaveformDrawer` + * :class:`~qiskit.visualization.pulse.matplotlib.ScheduleDrawer` + * :func:`~qiskit.visualization.pulse.interpolation.interp1d` + * :func:`~qiskit.visualization.interpolation.pulse.step_wise` + + Have been removed following their deprecation in Qiskit Terra 0.23. + Use the new interface :func:`qiskit.visualization.pulse_drawer` for pulse visualization. diff --git a/releasenotes/notes/remove-discrete-pulse-library-d7aa098a528161df.yaml b/releasenotes/notes/remove-discrete-pulse-library-d7aa098a528161df.yaml new file mode 100644 index 000000000000..fd18ee185c11 --- /dev/null +++ b/releasenotes/notes/remove-discrete-pulse-library-d7aa098a528161df.yaml @@ -0,0 +1,7 @@ +--- +upgrade: + - | + The discrete pulse library (previously located at ``qiskit.pulse.library.discrete``) + was removed, following its deprecation in Qiskit 0.46. + All of the removed pulses have a :class:`~.pulse.SymbolicPulse` counterpart. Consult + the Qiskit 0.46 release notes for more details. diff --git a/releasenotes/notes/remove-legacy-qasm2-parser-53ad3f1817fd68cc.yaml b/releasenotes/notes/remove-legacy-qasm2-parser-53ad3f1817fd68cc.yaml new file mode 100644 index 000000000000..d3a581761060 --- /dev/null +++ b/releasenotes/notes/remove-legacy-qasm2-parser-53ad3f1817fd68cc.yaml @@ -0,0 +1,34 @@ +--- +upgrade: + - | + The legacy OpenQASM 2 parser module previously present in ``qiskit.qasm`` has + been removed. It was marked as deprecated in Qiskit 0.46.0 release. The + OpenQASM 2 parser has been superseded by the :mod:`qiskit.qasm2` module which + provides a faster more correct parser for QASM2. + - | + The ``qiskit.converters.ast_to_dag`` function has been removed. It + previously was used to convert the abstract syntax tree generated by the + legacy OpenQASM 2 parser (in the ``qiskit.qasm`` module which no longer exists) + and convert that directly to a :class:`.DAGCircuit`. This function was + marked as deprecated in the Qiskit 0.46.0 release. As the legacy + OpenQASM 2 parser has been removed this function no longer serves a purpose. + If you were previously using this, you can instead parse your OpenQASM 2 files + into a :class:`.QuantumCircuit` using the + :meth:`.QuantumCircuit.from_qasm_file` or + :meth:`.QuantumCircuit.from_qasm_str` constructor methods and then + converting that :class:`.QuantumCircuit` into a :class:`.DAGCircuit` with + :func:`.circuit_to_dag`. + - | + Removed the ``QuantumCircuit.qasm()`` method to generate a OpenQASM 2 + representation of the :class:`.QuantumCircuit` object. Instead the + :func:`.qasm2.dump` or :func:`.qasm2.dumps` functions should be used. This + function was marked as deprecated in the 0.46.0 release. If you were using + the ``QuantumCircuit.qasm()`` method to generate pygments formatted output + you should instead look at the standalone ``openqasm-pygments`` package + to provide this functionality. + - | + Removed the ``qiskit.tools.jupyter.library.qasm_widget`` function which + was used to visualize qasm strings in a jupyter notebook. This function + was marked as deprecated as part of the Qiskit 0.44.0 release. The function + was originally used for building documentation but hasn't been used in some + time and has been removed from Qiskit. diff --git a/releasenotes/notes/remove-namespace-hooks-995bdb7515a9fe35.yaml b/releasenotes/notes/remove-namespace-hooks-995bdb7515a9fe35.yaml new file mode 100644 index 000000000000..df8601ba031c --- /dev/null +++ b/releasenotes/notes/remove-namespace-hooks-995bdb7515a9fe35.yaml @@ -0,0 +1,14 @@ +--- +upgrade: + - | + Support for extensions of the ``qiskit`` and ``qiskit.providers`` namespaces + by external packages has been removed. Support for doing this was deprecated + in the Qiskit 0.44.0 release. In the past, the Qiskit project was composed + of elements that extended a shared namespace and hook points were added + to enable doing that. However, it was not intended for these interfaces to + ever be used by other packages. Now that the overall Qiskit package is no + longer using that packaging model, leaving the possibility for these + extensions carry more risk than benefits and has therefore been removed. + If you’re maintaining a package that extends the Qiskit namespace (i.e. + your users import from ``qiskit.x`` or ``qiskit.providers.y``) you should + transition to using a standalone Python namespace for your package. diff --git a/releasenotes/notes/remove-opflow-qi-utils-3debd943c65b17da.yaml b/releasenotes/notes/remove-opflow-qi-utils-3debd943c65b17da.yaml new file mode 100644 index 000000000000..8157db5a5e46 --- /dev/null +++ b/releasenotes/notes/remove-opflow-qi-utils-3debd943c65b17da.yaml @@ -0,0 +1,20 @@ +--- +upgrade: + - | + The ``qiskit.opflow`` module has been removed, following its deprecation in + Qiskit 0.44. For more context on the deprecation and + detailed migration instructions, you can read the + `Opflow Migration Guide `__. + + + - | + A series of legacy quantum execution utils have been removed, following their deprecation in Qiskit 0.44. + These include the ``qiskit.utils.QuantumInstance`` class, as well the modules: + + - ``qiskit.utils.backend_utils`` + - ``qiskit.utils.mitigation`` + - ``qiskit.utils.measurement_error_mitigation`` + - ``qiskit.utils.run_circuits`` + + Their functionality has been superseded by the use of primitives. For more context on the deprecation and + detailed migration instructions, you can read the `QuantumInstance Migration Guide `__. diff --git a/releasenotes/notes/remove-qiskit-algorithms-a43541fe24b72208.yaml b/releasenotes/notes/remove-qiskit-algorithms-a43541fe24b72208.yaml new file mode 100644 index 000000000000..51112c0d064a --- /dev/null +++ b/releasenotes/notes/remove-qiskit-algorithms-a43541fe24b72208.yaml @@ -0,0 +1,9 @@ +--- +upgrade: + - | + The ``qiskit.algorithms`` module has been removed, following its deprecation in + Qiskit 0.44. The primitive-based algorithms from this module have been migrated to a standalone library (``qiskit_algorithms``) + and can be found on PyPi or `GitHub `_. + The decision to migrate the algorithms module to a separate package + was made to clarify the purpose Qiskit and make a distinction between the tools + and libraries built on top of it. diff --git a/releasenotes/notes/remove_deprecated_legacy_pulse_builder_commands-61b85da62d82fb8c.yaml b/releasenotes/notes/remove_deprecated_legacy_pulse_builder_commands-61b85da62d82fb8c.yaml new file mode 100644 index 000000000000..5812d352f2ba --- /dev/null +++ b/releasenotes/notes/remove_deprecated_legacy_pulse_builder_commands-61b85da62d82fb8c.yaml @@ -0,0 +1,77 @@ +--- +upgrade: + - | + Removed logic for injecting circuit gate operation into the pulse context. + Removed logic for calling QuantumCircuit into pulse builder context. + + Removed + :func:`~qiskit.pulse.builder.call_gate`, + :func:`~qiskit.pulse.builder.cx`, + :func:`~qiskit.pulse.builder.u1`, + :func:`~qiskit.pulse.builder.u2`, + :func:`~qiskit.pulse.builder.u3`, + :func:`~qiskit.pulse.builder.x`, + :func:`~qiskit.pulse.builder.active_transpiler_settings`, + :func:`~qiskit.pulse.builder.active_circuit_scheduler_settings`, + :func:`~qiskit.pulse.builder.transpiler_settings`, + :func:`~qiskit.pulse.builder.circuit_scheduler_settings` + + :func:`~qiskit.pulse.builder._compile_lazy_circuit_before`, + :func:`~qiskit.pulse.builder._PulseBuilder.transpiler_settings`, + :func:`~qiskit.pulse.builder._PulseBuilder._compile_lazy_circuit`, + :func:`~qiskit.pulse.builder._PulseBuilder._compile_circuit`, + :func:`~qiskit.pulse.builder._PulseBuilder._new_circuit`, + :func:`~qiskit.pulse.builder._PulseBuilder.call_gate` + + Removed arguments :code:`default_transpiler_settings`, :code:`default_circuit_scheduler_settings` + in :func:`~qiskit.pulse.builder.build` + + Removed support of :class:`.QuantumCircuit` type for argument :code:`target` + in :func:`~qiskit.pulse.builder.call` + + Removed support of :class:`.QuantumCircuit` type for argument :code:`subroutine` + in :func:`~qiskit.pulse.builder._PulseBuilder.call_subroutine` + + + + .. code-block:: python + + from qiskit import compiler + from qiskit.providers.fake_provider import FakePerth + backend=FakePerth() + + """Users are encouraged to use gate operation in realm of QuantumCircuits, + not in qiskit-pulse. pulse should exclusively be used to make and call + schedules comprised of pulses like Drag, Gaussian, GaussianSquare etc""" + + #Example: + with pulse.build() as sched: + pulse.play(pulse.Gaussian(150, 0.40, 10) + pulse.play(pulse.GaussianSquare(150, 0.1, 12) + + """This is not encouraged but you can call + QuantumCircuit into a pulse builder context like this:""" + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc = compiler.transpile(qc, backend) + sched = compiler.schedule(qc, backend) + + with pulse.build(backend) as qc_sched: + pulse.call(sched) + + """This example shows how to get pulse gate instructions. + For BackendV2""" + sched = backend.target['x'][(qubit,)].calibration + + # For BackendV1 + sched = backend.defaults().instruction_schedule_map.get('x', (0,)) + + + Modified module doc of :mod:`qiskit.pulse.builder` related with example + code with circuit elements. + + Modified related tests in + :file:`test/python/pulse/test_builder.py`, + :file:`test/python/pulse/test_block.py` + :file:`test/python/pulse/test_builder_v2.py` + :file:`test/python/transpiler/test_calibrationbuilder.py` diff --git a/releasenotes/notes/rr-decomposition-synthesis-70eb88ada9305916.yaml b/releasenotes/notes/rr-decomposition-synthesis-70eb88ada9305916.yaml new file mode 100644 index 000000000000..e4ec6a970a3c --- /dev/null +++ b/releasenotes/notes/rr-decomposition-synthesis-70eb88ada9305916.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Fixed an issue in :func:`.unitary_to_gate_sequence` which caused unitary + decomposition in RR basis to emit two R gates in some cases where the + matrix can be expressed as a single R gate. Previously, in those cases you + would get two R gates with the same phi parameter. Now, they are combined + into one. diff --git a/releasenotes/notes/sabre-no-route-barrier-bc82fecb65d3ab9a.yaml b/releasenotes/notes/sabre-no-route-barrier-bc82fecb65d3ab9a.yaml new file mode 100644 index 000000000000..fe77e9c51ef0 --- /dev/null +++ b/releasenotes/notes/sabre-no-route-barrier-bc82fecb65d3ab9a.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixed an issue in the :class:`.SabreSwap` and :class:`.SabreLayout` + transpiler passes where it would incorrectly treat 2 qubit + :class:`.Barrier` instructions as an instruction that needs to be + routed according the transpiler :class:`.Target`. When this occured the + output was still correct but would potentially include unecessary + :class:`.SwapGate` instructions. diff --git a/releasenotes/notes/singletonize-instructions-78723f68cd0ac03f.yaml b/releasenotes/notes/singletonize-instructions-78723f68cd0ac03f.yaml new file mode 100644 index 000000000000..ced4a873bd6a --- /dev/null +++ b/releasenotes/notes/singletonize-instructions-78723f68cd0ac03f.yaml @@ -0,0 +1,32 @@ +--- +features: + - | + The following standard library instructions are now instances of + :class:`~.SingletonInstruction`: + + * :class:`~.Measure` + * :class:`~.Reset` + + This means that if these classes are instantiated as (e.g.) ``Measure()`` using + all the constructor defaults, they will all share a single global + instance. This results in large reduction in the memory overhead for > 1 + object of these types and significantly faster object construction time. +upgrade: + - | + The following standard library instructions: + + * :class:`~.Measure` + * :class:`~.Reset` + + are immutable, unless the attributes ``label``, ``duration`` and ``unit`` are given as keyword + arguments during class construction. + The attributes :attr:`~.Instruction.label`, :attr:`~.Instruction.duration`, :attr:`~.Instruction.unit`, + and :attr:`~.Instruction.condition` attributes are all not publicly accessible and setting these attributes + directly is not allowed and it will raise an exception. If they are needed for a particular + instance you must ensure you have a mutable instance using :meth:`.Instruction.to_mutable` + and use :meth:`.Instruction.c_if` for :attr:`~.Instruction.condition` + For the singleton variant of these instructions, there is a special attribute + :attr:`~.SingletonInstruction._singleton_lookup_key`, that when called generates a key based on the input + arguments, which can be used for identifying and indexing these instructions within the framework. + + diff --git a/releasenotes/notes/terra-nullius-7ef598626d8118c1.yaml b/releasenotes/notes/terra-nullius-7ef598626d8118c1.yaml new file mode 100644 index 000000000000..562929a17b9f --- /dev/null +++ b/releasenotes/notes/terra-nullius-7ef598626d8118c1.yaml @@ -0,0 +1,30 @@ +--- +critical: + - | + You cannot upgrade in place to Qiskit 1.0. You must begin a new virtual environment. + + From Qiskit 1.0, Qiskit is comprised of exactly one Python package: ``qiskit``. Previously, + as a legacy of the "component elements" of early Qiskit, the ``qiskit`` package was a + dependency-only "metapackage", and the core code of Qiskit was in a package called ``qiskit-terra``. + As Qiskit grew, the other elements split off into their own packages (such as ``qiskit-aer``) + until only the core was left in the metapackage. For Qiskit 1.0, we are removing the metapackage + entirely, and replacing it with the actual Qiskit code. + + This means that you cannot upgrade an existing installation to Qiskit 1.0. Instead, you must + create a new Python virtual environment. Using the built-in ``venv`` module, you can do (Linux + and Mac): + + .. code-block:: bash + + # Create the new environment (only once). + python -m venv ~/qiskit-1.0-venv + # Activate the environment (every session). + source ~/qiskit-1.0-venv/bin/activate + # Install Qiskit (only once). + pip install 'qiskit>=1.0' + + For other platforms, or more unusual shells, refer to `the Python standard-library documentation + on activating virtual environments `__. + + If you are a library author, or have code that depends on Qiskit, you should update any old + dependencies on ``qiskit-terra`` to instead depend on ``qiskit``. diff --git a/releasenotes/notes/update-dag-dependency-drawer-d06d4ae660c1cbc2.yaml b/releasenotes/notes/update-dag-dependency-drawer-d06d4ae660c1cbc2.yaml new file mode 100644 index 000000000000..e26bf092b786 --- /dev/null +++ b/releasenotes/notes/update-dag-dependency-drawer-d06d4ae660c1cbc2.yaml @@ -0,0 +1,22 @@ +--- +features: + - | + The :func:`.dag_drawer` has been updated for the :class:`.DAGDependency`. These + drawings have a new color scheme, and the nodes now indicate the ``Qubits`` and + ``Clbits`` that are used by the node. If the node has a ``condition`` the drawings + will indicate that as well. + + .. code-block:: python + + from qiskit.circuit import QuantumCircuit + from qiskit.converters import circuit_to_dagdependency + + qc = QuantumCircuit(3, 2) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + qc.x(1) + qc.barrier() + qc.measure(0, 0) + dagdep = circuit_to_dagdependency(qc) + dagdep.draw() diff --git a/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml b/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml new file mode 100644 index 000000000000..25d361e51896 --- /dev/null +++ b/releasenotes/notes/vf2postlayout-no-better-solution-eb5ced3c8a60ea23.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + :class:`.VF2PostLayout` now distinguishes between 'no solution' and 'no better solution' when + determining a :class:`.Layout` for a given quantum circuit. 'no better solution' is set when the + initial layout of a quantum circuit is also the optimal one, i.e. incurs the least cost in terms of + error rates. diff --git a/requirements-dev.txt b/requirements-dev.txt index ba64fc8cb880..a23cd6d64991 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -28,15 +28,15 @@ ddt>=1.2.0,!=1.4.0,!=1.4.3 # Documentation tooling. # # This alone is not sufficient to fully build the documentation, because several -# components of Terra use some of its optional dependencies in order to document -# themselves. These are the requirements that are _only_ required for the docs -# build, and are not used by Terra itself. +# components of Qiskit use some of its optional dependencies in order to document +# themselves. These are the requirements that are _only_ required for the docs +# build, and are not used by Qiskit itself. +# +# Be careful when adding new requirements. We want to keep the docs build simple because +# we only build docs in this repo to generate API references that get consumed by +# https://github.com/Qiskit/documentation. For example, coordinate adding dependencies +# like `sphinx-design` to make sure that `Qiskit/documentation` will be able to +# consume it properly. Sphinx>=6.0,<7.2 -qiskit-sphinx-theme~=1.16.0 -sphinx-design>=0.2.0 -sphinx-remove-toctrees -sphinx-reredirects -nbsphinx~=0.9.2 -nbconvert~=7.7.1 # TODO: switch to stable release when 4.1 is released reno @ git+https://github.com/openstack/reno.git@81587f616f17904336cdc431e25c42b46cd75b8f diff --git a/requirements-optional.txt b/requirements-optional.txt index 6afa25271ab9..0e94040e69c1 100644 --- a/requirements-optional.txt +++ b/requirements-optional.txt @@ -1,9 +1,9 @@ -# Optional dependencies of Terra that can (mostly) reliably be installed with +# Optional dependencies of Qiskit that can (mostly) reliably be installed with # `pip`. This file is still called `requirements-optional.txt` just to match # standard pip conventions, even though none of these are required. # # If updating this, you probably want to update `qiskit.utils.optionals` and -# maybe `setup.py` too. +# maybe `pyproject.toml` too. # Test-runner enhancements. fixtures @@ -17,7 +17,6 @@ ipywidgets>=7.3.0 matplotlib>=3.3 pillow>=4.2.1 pydot -pygments>=2.4 pylatexenc>=1.4 seaborn>=0.9.0 @@ -25,14 +24,8 @@ seaborn>=0.9.0 qiskit-aer qiskit-qasm3-import python-constraint>=1.4 -cplex; python_version < '3.11' cvxpy -docplex -jax; platform_system != 'Windows' -jaxlib; platform_system != 'Windows' scikit-learn>=0.20.0 -scikit-quant<=0.7; platform_system != 'Windows' -SQSnobFit z3-solver>=4.7 # Tweedledum is unmaintained and its existing Mac wheels are unreliable. If you # manage to get a working install on a Mac the functionality should still work, diff --git a/requirements-tutorials.txt b/requirements-tutorials.txt deleted file mode 100644 index c87701dc97ad..000000000000 --- a/requirements-tutorials.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Requirements for the tutorials CI run that go beyond the others in `requirements-optional.txt`. -# This may also include some requirements that are only in `requirements-dev.txt`, since those -# aren't runtime dependencies or optionals of Terra. - -networkx>=2.3 -jupyter -Sphinx -nbsphinx -qiskit_sphinx_theme -pyscf diff --git a/requirements.txt b/requirements.txt index d41eee50ce95..6402e9a6549f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ rustworkx>=0.13.0 numpy>=1.17,<2 -ply>=3.10 psutil>=5 scipy>=1.5 sympy>=1.3 @@ -8,7 +7,4 @@ dill>=0.3 python-dateutil>=2.8.0 stevedore>=3.0.0 typing-extensions; python_version<'3.11' -# symengine pinning needed due lowered precision handling complex -# multiplication in version 0.10 wich breaks parameter assignment test -# (can be removed once issue is fix) -symengine>=0.9, <0.10; platform_machine == 'x86_64' or platform_machine == 'aarch64' or platform_machine == 'ppc64le' or platform_machine == 'amd64' or platform_machine == 'arm64' +symengine>=0.9,!=0.10.0 diff --git a/setup.py b/setup.py index 97ca604fd757..91fb20c83cf2 100644 --- a/setup.py +++ b/setup.py @@ -13,90 +13,24 @@ "The Qiskit Terra setup file." import os -import re -from setuptools import setup, find_packages +from setuptools import setup from setuptools_rust import Binding, RustExtension +# Most of this configuration is managed by `pyproject.toml`. This only includes the extra bits to +# configure `setuptools-rust`, because we do a little dynamic trick with the debug setting, and we +# also want an explicit `setup.py` file to exist so we can manually call +# +# python setup.py build_rust --inplace --release +# +# to make optimised Rust components even for editable releases, which would otherwise be quite +# unergonomic to do otherwise. -with open("requirements.txt") as f: - REQUIREMENTS = f.read().splitlines() - -# Read long description from README. -README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "README.md") -with open(README_PATH) as readme_file: - README = re.sub( - ".*", - "", - readme_file.read(), - flags=re.S | re.M, - ) # If RUST_DEBUG is set, force compiling in debug mode. Else, use the default behavior of whether # it's an editable installation. rust_debug = True if os.getenv("RUST_DEBUG") == "1" else None -# If modifying these optional extras, make sure to sync with `requirements-optional.txt` and -# `qiskit.utils.optionals` as well. -qasm3_import_extras = [ - "qiskit-qasm3-import>=0.1.0", -] -visualization_extras = [ - "matplotlib>=3.3", - "ipywidgets>=7.3.0", - "pydot", - "pillow>=4.2.1", - "pylatexenc>=1.4", - "seaborn>=0.9.0", - "pygments>=2.4", -] -z3_requirements = [ - "z3-solver>=4.7", -] -csp_requirements = ["python-constraint>=1.4"] - - setup( - name="qiskit-terra", - version="1.0.0", - description="Software for developing quantum computing programs", - long_description=README, - long_description_content_type="text/markdown", - url="https://github.com/Qiskit/qiskit", - author="Qiskit Development Team", - author_email="hello@qiskit.org", - license="Apache 2.0", - classifiers=[ - "Environment :: Console", - "License :: OSI Approved :: Apache Software License", - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - "Operating System :: Microsoft :: Windows", - "Operating System :: MacOS", - "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Topic :: Scientific/Engineering", - ], - keywords="qiskit sdk quantum", - packages=find_packages(exclude=["test*"]), - install_requires=REQUIREMENTS, - include_package_data=True, - python_requires=">=3.8", - extras_require={ - "qasm3-import": qasm3_import_extras, - "visualization": visualization_extras, - "crosstalk-pass": z3_requirements, - "csp-layout-pass": csp_requirements, - "all": visualization_extras + z3_requirements + csp_requirements + qasm3_import_extras, - }, - project_urls={ - "Bug Tracker": "https://github.com/Qiskit/qiskit-terra/issues", - "Documentation": "https://qiskit.org/documentation/", - "Source Code": "https://github.com/Qiskit/qiskit-terra", - }, rust_extensions=[ RustExtension( "qiskit._accelerate", @@ -112,57 +46,4 @@ ), ], options={"bdist_wheel": {"py_limited_api": "cp38"}}, - zip_safe=False, - entry_points={ - "qiskit.unitary_synthesis": [ - "default = qiskit.transpiler.passes.synthesis.unitary_synthesis:DefaultUnitarySynthesis", - "aqc = qiskit.transpiler.synthesis.aqc.aqc_plugin:AQCSynthesisPlugin", - "sk = qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevSynthesis", - ], - "qiskit.synthesis": [ - "clifford.default = qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisClifford", - "clifford.ag = qiskit.transpiler.passes.synthesis.high_level_synthesis:AGSynthesisClifford", - "clifford.bm = qiskit.transpiler.passes.synthesis.high_level_synthesis:BMSynthesisClifford", - "clifford.greedy = qiskit.transpiler.passes.synthesis.high_level_synthesis:GreedySynthesisClifford", - "clifford.layers = qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerSynthesisClifford", - "clifford.lnn = qiskit.transpiler.passes.synthesis.high_level_synthesis:LayerLnnSynthesisClifford", - "linear_function.default = qiskit.transpiler.passes.synthesis.high_level_synthesis:DefaultSynthesisLinearFunction", - "linear_function.kms = qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisLinearFunction", - "linear_function.pmh = qiskit.transpiler.passes.synthesis.high_level_synthesis:PMHSynthesisLinearFunction", - "permutation.default = qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation", - "permutation.kms = qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation", - "permutation.basic = qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation", - "permutation.acg = qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation", - ], - "qiskit.transpiler.init": [ - "default = qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultInitPassManager", - ], - "qiskit.transpiler.translation": [ - "translator = qiskit.transpiler.preset_passmanagers.builtin_plugins:BasisTranslatorPassManager", - "unroller = qiskit.transpiler.preset_passmanagers.builtin_plugins:UnrollerPassManager", - "synthesis = qiskit.transpiler.preset_passmanagers.builtin_plugins:UnitarySynthesisPassManager", - ], - "qiskit.transpiler.routing": [ - "basic = qiskit.transpiler.preset_passmanagers.builtin_plugins:BasicSwapPassManager", - "stochastic = qiskit.transpiler.preset_passmanagers.builtin_plugins:StochasticSwapPassManager", - "lookahead = qiskit.transpiler.preset_passmanagers.builtin_plugins:LookaheadSwapPassManager", - "sabre = qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreSwapPassManager", - "none = qiskit.transpiler.preset_passmanagers.builtin_plugins:NoneRoutingPassManager", - ], - "qiskit.transpiler.optimization": [ - "default = qiskit.transpiler.preset_passmanagers.builtin_plugins:OptimizationPassManager", - ], - "qiskit.transpiler.layout": [ - "default = qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultLayoutPassManager", - "trivial = qiskit.transpiler.preset_passmanagers.builtin_plugins:TrivialLayoutPassManager", - "dense = qiskit.transpiler.preset_passmanagers.builtin_plugins:DenseLayoutPassManager", - "noise_adaptive = qiskit.transpiler.preset_passmanagers.builtin_plugins:NoiseAdaptiveLayoutPassManager", - "sabre = qiskit.transpiler.preset_passmanagers.builtin_plugins:SabreLayoutPassManager", - ], - "qiskit.transpiler.scheduling": [ - "alap = qiskit.transpiler.preset_passmanagers.builtin_plugins:AlapSchedulingPassManager", - "asap = qiskit.transpiler.preset_passmanagers.builtin_plugins:AsapSchedulingPassManager", - "default = qiskit.transpiler.preset_passmanagers.builtin_plugins:DefaultSchedulingPassManager", - ], - }, ) diff --git a/test/benchmarks/converters.py b/test/benchmarks/converters.py index 041e40dd6de1..d99822fa8016 100644 --- a/test/benchmarks/converters.py +++ b/test/benchmarks/converters.py @@ -14,7 +14,6 @@ # pylint: disable=attribute-defined-outside-init,unsubscriptable-object from qiskit import converters -from qiskit import qasm from .utils import random_circuit @@ -38,7 +37,6 @@ def setup(self, n_qubits, depth): raise NotImplementedError self.qc = random_circuit(n_qubits, depth, measure=True, conditional=True, seed=seed) self.dag = converters.circuit_to_dag(self.qc) - self.qasm = qasm.Qasm(data=self.qc.qasm()).parse() def time_circuit_to_dag(self, *_): converters.circuit_to_dag(self.qc) @@ -48,6 +46,3 @@ def time_circuit_to_instruction(self, *_): def time_dag_to_circuit(self, *_): converters.dag_to_circuit(self.dag) - - def time_ast_to_circuit(self, *_): - converters.ast_to_dag(self.qasm) diff --git a/test/python/algorithms/__init__.py b/test/python/algorithms/__init__.py deleted file mode 100644 index 5303c5b1256c..000000000000 --- a/test/python/algorithms/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Algorithms test module""" - -from .algorithms_test_case import QiskitAlgorithmsTestCase - -__all__ = ["QiskitAlgorithmsTestCase"] diff --git a/test/python/algorithms/algorithms_test_case.py b/test/python/algorithms/algorithms_test_case.py deleted file mode 100644 index 8fc9effd0bf7..000000000000 --- a/test/python/algorithms/algorithms_test_case.py +++ /dev/null @@ -1,21 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Algorithms Test Case""" - -from qiskit.test import QiskitTestCase - - -class QiskitAlgorithmsTestCase(QiskitTestCase): - """Algorithms test Case""" - - pass diff --git a/test/python/algorithms/eigensolvers/__init__.py b/test/python/algorithms/eigensolvers/__init__.py deleted file mode 100644 index e2113e5c114e..000000000000 --- a/test/python/algorithms/eigensolvers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for the eigensolvers.""" diff --git a/test/python/algorithms/eigensolvers/test_numpy_eigensolver.py b/test/python/algorithms/eigensolvers/test_numpy_eigensolver.py deleted file mode 100644 index 13f2f9a67e4b..000000000000 --- a/test/python/algorithms/eigensolvers/test_numpy_eigensolver.py +++ /dev/null @@ -1,216 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test NumPyEigensolver""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from ddt import data, ddt - -from qiskit.algorithms.eigensolvers import NumPyEigensolver -from qiskit.algorithms import AlgorithmError -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info import Operator, SparsePauliOp, Pauli, ScalarOp - -H2_SPARSE_PAULI = SparsePauliOp( - ["II", "ZI", "IZ", "ZZ", "XX"], - coeffs=[ - -1.052373245772859, - 0.39793742484318045, - -0.39793742484318045, - -0.01128010425623538, - 0.18093119978423156, - ], -) - -H2_OP = Operator(H2_SPARSE_PAULI.to_matrix()) - -H2_PAULI = PauliSumOp(H2_SPARSE_PAULI) - - -@ddt -class TestNumPyEigensolver(QiskitAlgorithmsTestCase): - """Test NumPy Eigen solver""" - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_ce(self, op): - """Test basics""" - algo = NumPyEigensolver() - result = algo.compute_eigenvalues(operator=op, aux_operators=[]) - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_ce_k4(self, op): - """Test for k=4 eigenvalues""" - algo = NumPyEigensolver(k=4) - result = algo.compute_eigenvalues(operator=op, aux_operators=[]) - self.assertEqual(len(result.eigenvalues), 4) - self.assertEqual(len(result.eigenstates), 4) - self.assertEqual(result.eigenvalues.dtype, np.float64) - np.testing.assert_array_almost_equal( - result.eigenvalues, [-1.85727503, -1.24458455, -0.88272215, -0.22491125] - ) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_ce_k4_filtered(self, op): - """Test for k=4 eigenvalues with filter""" - - # define filter criterion - # pylint: disable=unused-argument - def criterion(x, v, a_v): - return v >= -1 - - algo = NumPyEigensolver(k=4, filter_criterion=criterion) - result = algo.compute_eigenvalues(operator=op, aux_operators=[]) - self.assertEqual(len(result.eigenvalues), 2) - self.assertEqual(len(result.eigenstates), 2) - self.assertEqual(result.eigenvalues.dtype, np.float64) - np.testing.assert_array_almost_equal(result.eigenvalues, [-0.88272215, -0.22491125]) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_ce_k4_filtered_empty(self, op): - """Test for k=4 eigenvalues with filter always returning False""" - - # define filter criterion - # pylint: disable=unused-argument - def criterion(x, v, a_v): - return False - - algo = NumPyEigensolver(k=4, filter_criterion=criterion) - result = algo.compute_eigenvalues(operator=op, aux_operators=[]) - self.assertEqual(len(result.eigenvalues), 0) - self.assertEqual(len(result.eigenstates), 0) - - @data( - SparsePauliOp(["X"], coeffs=[1.0]), - SparsePauliOp(["Y"], coeffs=[1.0]), - SparsePauliOp(["Z"], coeffs=[1.0]), - ) - def test_ce_k1_1q(self, op): - """Test for 1 qubit operator""" - algo = NumPyEigensolver(k=1) - result = algo.compute_eigenvalues(operator=op) - np.testing.assert_array_almost_equal(result.eigenvalues, [-1]) - - @data( - SparsePauliOp(["X"], coeffs=[1.0]), - SparsePauliOp(["Y"], coeffs=[1.0]), - SparsePauliOp(["Z"], coeffs=[1.0]), - ) - def test_ce_k2_1q(self, op): - """Test for 1 qubit operator""" - algo = NumPyEigensolver(k=2) - result = algo.compute_eigenvalues(operator=op) - np.testing.assert_array_almost_equal(result.eigenvalues, [-1, 1]) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_aux_operators_list(self, op): - """Test list-based aux_operators.""" - aux_op1 = Operator(SparsePauliOp(["II"], coeffs=[2.0]).to_matrix()) - aux_op2 = SparsePauliOp(["II", "ZZ", "YY", "XX"], coeffs=[0.5, 0.5, 0.5, -0.5]) - aux_ops = [aux_op1, aux_op2] - algo = NumPyEigensolver() - result = algo.compute_eigenvalues(operator=op, aux_operators=aux_ops) - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operators_evaluated), 1) - self.assertEqual(len(result.aux_operators_evaluated[0]), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[0][1][0], 0, places=6) - # metadata - self.assertAlmostEqual(result.aux_operators_evaluated[0][0][1].pop("variance"), 0.0) - self.assertAlmostEqual(result.aux_operators_evaluated[0][1][1].pop("variance"), 0.0) - - # Go again with additional None and zero operators - extra_ops = [*aux_ops, None, 0] - result = algo.compute_eigenvalues(operator=op, aux_operators=extra_ops) - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operators_evaluated), 1) - self.assertEqual(len(result.aux_operators_evaluated[0]), 4) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[0][1][0], 0, places=6) - self.assertIsNone(result.aux_operators_evaluated[0][2], None) - self.assertEqual(result.aux_operators_evaluated[0][3][0], 0.0) - # metadata - self.assertAlmostEqual(result.aux_operators_evaluated[0][0][1].pop("variance"), 0.0) - self.assertAlmostEqual(result.aux_operators_evaluated[0][1][1].pop("variance"), 0.0) - self.assertEqual(result.aux_operators_evaluated[0][3][1].pop("variance"), 0.0) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_aux_operators_dict(self, op): - """Test dict-based aux_operators.""" - aux_op1 = Operator(SparsePauliOp(["II"], coeffs=[2.0]).to_matrix()) - aux_op2 = SparsePauliOp(["II", "ZZ", "YY", "XX"], coeffs=[0.5, 0.5, 0.5, -0.5]) - aux_ops = {"aux_op1": aux_op1, "aux_op2": aux_op2} - algo = NumPyEigensolver() - result = algo.compute_eigenvalues(operator=op, aux_operators=aux_ops) - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operators_evaluated), 1) - self.assertEqual(len(result.aux_operators_evaluated[0]), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op2"][0], 0, places=6) - # metadata - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op1"][1].pop("variance"), 0.0) - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op2"][1].pop("variance"), 0.0) - - # Go again with additional None and zero operators - extra_ops = {**aux_ops, "None_operator": None, "zero_operator": 0} - result = algo.compute_eigenvalues(operator=op, aux_operators=extra_ops) - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operators_evaluated), 1) - self.assertEqual(len(result.aux_operators_evaluated[0]), 3) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op2"][0], 0, places=6) - self.assertEqual(result.aux_operators_evaluated[0]["zero_operator"][0], 0.0) - self.assertTrue("None_operator" not in result.aux_operators_evaluated[0].keys()) - # metadata - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op1"][1].pop("variance"), 0.0) - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op2"][1].pop("variance"), 0.0) - self.assertAlmostEqual( - result.aux_operators_evaluated[0]["zero_operator"][1].pop("variance"), 0.0 - ) - - def test_pauli_op(self): - """Test simple pauli operator""" - algo = NumPyEigensolver(k=1) - result = algo.compute_eigenvalues(operator=Pauli("X")) - np.testing.assert_array_almost_equal(result.eigenvalues, [-1]) - - def test_scalar_op(self): - """Test scalar operator""" - algo = NumPyEigensolver(k=1) - with self.assertRaises(AlgorithmError): - algo.compute_eigenvalues(operator=ScalarOp(1)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/eigensolvers/test_vqd.py b/test/python/algorithms/eigensolvers/test_vqd.py deleted file mode 100644 index 47eab6404f2c..000000000000 --- a/test/python/algorithms/eigensolvers/test_vqd.py +++ /dev/null @@ -1,453 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test VQD""" - -import unittest -import warnings -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from ddt import data, ddt - -from qiskit import QuantumCircuit -from qiskit.algorithms.eigensolvers import VQD, VQDResult -from qiskit.algorithms import AlgorithmError -from qiskit.algorithms.optimizers import COBYLA, L_BFGS_B, SLSQP, SPSA -from qiskit.algorithms.state_fidelities import ComputeUncompute -from qiskit.circuit.library import TwoLocal, RealAmplitudes -from qiskit.opflow import PauliSumOp -from qiskit.primitives import Sampler, Estimator -from qiskit.quantum_info import SparsePauliOp -from qiskit.quantum_info.operators import Operator -from qiskit.utils import algorithm_globals - - -H2_SPARSE_PAULI = SparsePauliOp.from_list( - [ - ("II", -1.052373245772859), - ("IZ", 0.39793742484318045), - ("ZI", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] -) -H2_OP = Operator(H2_SPARSE_PAULI.to_matrix()) - -H2_PAULI = PauliSumOp(H2_SPARSE_PAULI) - - -@ddt -class TestVQD(QiskitAlgorithmsTestCase): - """Test VQD""" - - def setUp(self): - super().setUp() - self.seed = 50 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - - self.h2_energy = -1.85727503 - self.h2_energy_excited = [-1.85727503, -1.24458455, -0.88272215, -0.22491125] - - self.ryrz_wavefunction = TwoLocal( - rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1 - ) - self.ry_wavefunction = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz") - - self.estimator = Estimator() - self.estimator_shots = Estimator(options={"shots": 1024, "seed": self.seed}) - self.fidelity = ComputeUncompute(Sampler()) - self.betas = [50, 50] - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_basic_operator(self, op): - """Test the VQD without aux_operators.""" - wavefunction = self.ryrz_wavefunction - vqd = VQD( - estimator=self.estimator, - fidelity=self.fidelity, - ansatz=wavefunction, - optimizer=COBYLA(), - betas=self.betas, - ) - - result = vqd.compute_eigenvalues(operator=op) - - with self.subTest(msg="test eigenvalue"): - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited[:2], decimal=1 - ) - - with self.subTest(msg="test dimension of optimal point"): - self.assertEqual(len(result.optimal_points[-1]), 8) - - with self.subTest(msg="assert cost_function_evals is set"): - self.assertIsNotNone(result.cost_function_evals) - - with self.subTest(msg="assert optimizer_times is set"): - self.assertIsNotNone(result.optimizer_times) - - with self.subTest(msg="assert return ansatz is set"): - job = self.estimator.run( - result.optimal_circuits, - [op] * len(result.optimal_points), - result.optimal_points, - ) - np.testing.assert_array_almost_equal(job.result().values, result.eigenvalues, 6) - - with self.subTest(msg="assert returned values are eigenvalues"): - np.testing.assert_array_almost_equal( - result.optimal_values, self.h2_energy_excited[:2], decimal=3 - ) - - def test_full_spectrum(self): - """Test obtaining all eigenvalues.""" - vqd = VQD(self.estimator, self.fidelity, self.ryrz_wavefunction, optimizer=L_BFGS_B(), k=4) - result = vqd.compute_eigenvalues(H2_PAULI) - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=2 - ) - - @data(H2_PAULI, H2_SPARSE_PAULI) - def test_beta_autoeval(self, op): - """Test beta autoevaluation for different operator types.""" - - with self.assertLogs(level="INFO") as logs: - vqd = VQD( - self.estimator_shots, self.fidelity, self.ryrz_wavefunction, optimizer=L_BFGS_B() - ) - _ = vqd.compute_eigenvalues(op) - - # the first log message shows the value of beta[0] - beta = float(logs.output[0].split()[-1]) - self.assertAlmostEqual(beta, 20.40459399499687, 4) - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_mismatching_num_qubits(self, op): - """Ensuring circuit and operator mismatch is caught""" - wavefunction = QuantumCircuit(1) - optimizer = SLSQP(maxiter=50) - vqd = VQD( - estimator=self.estimator, - fidelity=self.fidelity, - k=1, - ansatz=wavefunction, - optimizer=optimizer, - betas=self.betas, - ) - with self.assertRaises(AlgorithmError): - _ = vqd.compute_eigenvalues(operator=op) - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_missing_varform_params(self, op): - """Test specifying a variational form with no parameters raises an error.""" - circuit = QuantumCircuit(op.num_qubits) - vqd = VQD( - estimator=self.estimator, - fidelity=self.fidelity, - ansatz=circuit, - optimizer=SLSQP(), - k=1, - betas=self.betas, - ) - with self.assertRaises(AlgorithmError): - vqd.compute_eigenvalues(operator=op) - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_callback(self, op): - """Test the callback on VQD.""" - history = {"eval_count": [], "parameters": [], "mean": [], "metadata": [], "step": []} - - def store_intermediate_result(eval_count, parameters, mean, metadata, step): - history["eval_count"].append(eval_count) - history["parameters"].append(parameters) - history["mean"].append(mean) - history["metadata"].append(metadata) - history["step"].append(step) - - optimizer = COBYLA(maxiter=3) - wavefunction = self.ry_wavefunction - - vqd = VQD( - estimator=self.estimator_shots, - fidelity=self.fidelity, - ansatz=wavefunction, - optimizer=optimizer, - callback=store_intermediate_result, - betas=self.betas, - ) - - vqd.compute_eigenvalues(operator=op) - - self.assertTrue(all(isinstance(count, int) for count in history["eval_count"])) - self.assertTrue(all(isinstance(mean, float) for mean in history["mean"])) - self.assertTrue(all(isinstance(metadata, dict) for metadata in history["metadata"])) - self.assertTrue(all(isinstance(count, int) for count in history["step"])) - for params in history["parameters"]: - self.assertTrue(all(isinstance(param, float) for param in params)) - - ref_eval_count = [1, 2, 3, 1, 2, 3] - ref_mean = [-1.07, -1.45, -1.37, 37.43, 48.55, 28.94] - # new ref_mean for statevector simulator. The old unit test was on qasm - # and the ref_mean values were slightly different. - - ref_step = [1, 1, 1, 2, 2, 2] - - np.testing.assert_array_almost_equal(history["eval_count"], ref_eval_count, decimal=0) - np.testing.assert_array_almost_equal(history["mean"], ref_mean, decimal=2) - np.testing.assert_array_almost_equal(history["step"], ref_step, decimal=0) - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_vqd_optimizer(self, op): - """Test running same VQD twice to re-use optimizer, then switch optimizer""" - - vqd = VQD( - estimator=self.estimator, - fidelity=self.fidelity, - ansatz=RealAmplitudes(), - optimizer=SLSQP(), - k=2, - betas=self.betas, - ) - - def run_check(): - result = vqd.compute_eigenvalues(operator=op) - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited[:2], decimal=3 - ) - - run_check() - - with self.subTest("Optimizer re-use"): - run_check() - - with self.subTest("Optimizer replace"): - vqd.optimizer = L_BFGS_B() - run_check() - - with self.subTest("Batched optimizer replace"): - vqd.optimizer = SLSQP(maxiter=60, max_evals_grouped=10) - run_check() - - with self.subTest("SPSA replace"): - # SPSA takes too long to converge, so we will - # only check that it runs with no errors. - vqd.optimizer = SPSA(maxiter=5, learning_rate=0.01, perturbation=0.01) - result = vqd.compute_eigenvalues(operator=op) - self.assertIsInstance(result, VQDResult) - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_optimizer_list(self, op): - """Test sending an optimizer list""" - - optimizers = [SLSQP(), L_BFGS_B()] - initial_point_1 = [ - 1.70256666, - -5.34843975, - -0.39542903, - 5.99477786, - -2.74374986, - -4.85284669, - 0.2442925, - -1.51638917, - ] - initial_point_2 = [ - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - 0.5, - ] - vqd = VQD( - estimator=self.estimator, - fidelity=self.fidelity, - ansatz=RealAmplitudes(), - optimizer=optimizers, - initial_point=[initial_point_1, initial_point_2], - k=2, - betas=self.betas, - ) - - result = vqd.compute_eigenvalues(operator=op) - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited[:2], decimal=3 - ) - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_aux_operators_list(self, op): - """Test list-based aux_operators.""" - wavefunction = self.ry_wavefunction - vqd = VQD( - estimator=self.estimator, - fidelity=self.fidelity, - ansatz=wavefunction, - optimizer=SLSQP(), - k=2, - betas=self.betas, - ) - - # Start with an empty list - result = vqd.compute_eigenvalues(op, aux_operators=[]) - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited[:2], decimal=2 - ) - self.assertIsNone(result.aux_operators_evaluated) - - # Go again with two auxiliary operators - aux_op1 = SparsePauliOp.from_list([("II", 2.0)]) - aux_op2 = SparsePauliOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - result = vqd.compute_eigenvalues(op, aux_operators=aux_ops) - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited[:2], decimal=2 - ) - self.assertEqual(len(result.aux_operators_evaluated), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0][0], 2, places=2) - self.assertAlmostEqual(result.aux_operators_evaluated[0][1][0], 0, places=2) - # metadata - self.assertIsInstance(result.aux_operators_evaluated[0][1][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0][1][1], dict) - - # Go again with additional None and zero operators - extra_ops = [*aux_ops, None, 0] - result = vqd.compute_eigenvalues(op, aux_operators=extra_ops) - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited[:2], decimal=2 - ) - self.assertEqual(len(result.aux_operators_evaluated), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0][0], 2, places=2) - self.assertAlmostEqual(result.aux_operators_evaluated[0][1][0], 0, places=2) - self.assertEqual(result.aux_operators_evaluated[0][2][0], 0.0) - self.assertEqual(result.aux_operators_evaluated[0][3][0], 0.0) - # metadata - self.assertIsInstance(result.aux_operators_evaluated[0][0][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0][1][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0][3][1], dict) - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_aux_operators_dict(self, op): - """Test dictionary compatibility of aux_operators""" - wavefunction = self.ry_wavefunction - vqd = VQD( - estimator=self.estimator, - fidelity=self.fidelity, - ansatz=wavefunction, - optimizer=SLSQP(), - betas=self.betas, - ) - - # Start with an empty dictionary - result = vqd.compute_eigenvalues(op, aux_operators={}) - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited[:2], decimal=2 - ) - self.assertIsNone(result.aux_operators_evaluated) - - # Go again with two auxiliary operators - aux_op1 = SparsePauliOp.from_list([("II", 2.0)]) - aux_op2 = SparsePauliOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = {"aux_op1": aux_op1, "aux_op2": aux_op2} - result = vqd.compute_eigenvalues(op, aux_operators=aux_ops) - self.assertEqual(len(result.eigenvalues), 2) - self.assertEqual(result.eigenvalues.dtype, np.complex128) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503, 2) - self.assertEqual(len(result.aux_operators_evaluated), 2) - self.assertEqual(len(result.aux_operators_evaluated[0]), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op2"][0], 0, places=1) - # metadata - self.assertIsInstance(result.aux_operators_evaluated[0]["aux_op1"][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0]["aux_op2"][1], dict) - - # Go again with additional None and zero operators - extra_ops = {**aux_ops, "None_operator": None, "zero_operator": 0} - result = vqd.compute_eigenvalues(op, aux_operators=extra_ops) - self.assertEqual(len(result.eigenvalues), 2) - self.assertEqual(result.eigenvalues.dtype, np.complex128) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503, places=5) - self.assertEqual(len(result.aux_operators_evaluated), 2) - self.assertEqual(len(result.aux_operators_evaluated[0]), 3) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[0]["aux_op2"][0], 0.0, places=2) - self.assertEqual(result.aux_operators_evaluated[0]["zero_operator"][0], 0.0) - self.assertTrue("None_operator" not in result.aux_operators_evaluated[0].keys()) - # metadata - self.assertIsInstance(result.aux_operators_evaluated[0]["aux_op1"][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0]["aux_op2"][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0]["zero_operator"][1], dict) - - @data(H2_PAULI, H2_OP, H2_SPARSE_PAULI) - def test_aux_operator_std_dev(self, op): - """Test non-zero standard deviations of aux operators.""" - wavefunction = self.ry_wavefunction - vqd = VQD( - estimator=self.estimator, - fidelity=self.fidelity, - ansatz=wavefunction, - initial_point=[ - 1.70256666, - -5.34843975, - -0.39542903, - 5.99477786, - -2.74374986, - -4.85284669, - 0.2442925, - -1.51638917, - ], - optimizer=COBYLA(maxiter=0), - betas=self.betas, - ) - - # Go again with two auxiliary operators - aux_op1 = SparsePauliOp.from_list([("II", 2.0)]) - aux_op2 = SparsePauliOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - result = vqd.compute_eigenvalues(op, aux_operators=aux_ops) - self.assertEqual(len(result.aux_operators_evaluated), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0][0], 2.0, places=1) - self.assertAlmostEqual( - result.aux_operators_evaluated[0][1][0], 0.0019531249999999445, places=1 - ) - # metadata - self.assertIsInstance(result.aux_operators_evaluated[0][0][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0][1][1], dict) - - # Go again with additional None and zero operators - aux_ops = [*aux_ops, None, 0] - result = vqd.compute_eigenvalues(op, aux_operators=aux_ops) - self.assertEqual(len(result.aux_operators_evaluated[0]), 4) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0][0], 2.0, places=1) - self.assertAlmostEqual( - result.aux_operators_evaluated[0][1][0], 0.0019531249999999445, places=1 - ) - self.assertEqual(result.aux_operators_evaluated[0][2][0], 0.0) - self.assertEqual(result.aux_operators_evaluated[0][3][0], 0.0) - # metadata - self.assertIsInstance(result.aux_operators_evaluated[0][0][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0][1][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0][2][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[0][3][1], dict) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/evolvers/__init__.py b/test/python/algorithms/evolvers/__init__.py deleted file mode 100644 index fdb172d367f0..000000000000 --- a/test/python/algorithms/evolvers/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/evolvers/test_evolution_problem.py b/test/python/algorithms/evolvers/test_evolution_problem.py deleted file mode 100644 index dc9d45f548a2..000000000000 --- a/test/python/algorithms/evolvers/test_evolution_problem.py +++ /dev/null @@ -1,136 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test evolver problem class.""" -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import data, ddt, unpack -from numpy.testing import assert_raises - -from qiskit.algorithms.evolvers.evolution_problem import EvolutionProblem -from qiskit.circuit import Parameter -from qiskit.opflow import Y, Z, One, X, Zero - - -@ddt -class TestEvolutionProblem(QiskitAlgorithmsTestCase): - """Test evolver problem class.""" - - def test_init_default(self): - """Tests that all default fields are initialized correctly.""" - hamiltonian = Y - time = 2.5 - initial_state = One - - with self.assertWarns(DeprecationWarning): - evo_problem = EvolutionProblem(hamiltonian, time, initial_state) - - expected_hamiltonian = Y - expected_time = 2.5 - expected_initial_state = One - expected_aux_operators = None - expected_t_param = None - expected_param_value_dict = None - - self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) - self.assertEqual(evo_problem.time, expected_time) - self.assertEqual(evo_problem.initial_state, expected_initial_state) - self.assertEqual(evo_problem.aux_operators, expected_aux_operators) - self.assertEqual(evo_problem.t_param, expected_t_param) - self.assertEqual(evo_problem.param_value_dict, expected_param_value_dict) - - def test_init_all(self): - """Tests that all fields are initialized correctly.""" - t_parameter = Parameter("t") - with self.assertWarns(DeprecationWarning): - hamiltonian = t_parameter * Z + Y - time = 2 - initial_state = One - aux_operators = [X, Y] - param_value_dict = {t_parameter: 3.2} - - with self.assertWarns(DeprecationWarning): - evo_problem = EvolutionProblem( - hamiltonian, - time, - initial_state, - aux_operators, - t_param=t_parameter, - param_value_dict=param_value_dict, - ) - expected_hamiltonian = Y + t_parameter * Z - - expected_time = 2 - expected_initial_state = One - expected_aux_operators = [X, Y] - expected_t_param = t_parameter - expected_param_value_dict = {t_parameter: 3.2} - - with self.assertWarns(DeprecationWarning): - self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) - - self.assertEqual(evo_problem.time, expected_time) - self.assertEqual(evo_problem.initial_state, expected_initial_state) - self.assertEqual(evo_problem.aux_operators, expected_aux_operators) - self.assertEqual(evo_problem.t_param, expected_t_param) - self.assertEqual(evo_problem.param_value_dict, expected_param_value_dict) - - @data([Y, -1, One], [Y, -1.2, One], [Y, 0, One]) - @unpack - def test_init_errors(self, hamiltonian, time, initial_state): - """Tests expected errors are thrown on invalid time argument.""" - with self.assertWarns(DeprecationWarning), assert_raises(ValueError): - _ = EvolutionProblem(hamiltonian, time, initial_state) - - def test_validate_params(self): - """Tests expected errors are thrown on parameters mismatch.""" - param_x = Parameter("x") - param_y = Parameter("y") - with self.subTest(msg="Parameter missing in dict."): - with self.assertWarns(DeprecationWarning): - hamiltonian = param_x * X + param_y * Y - - param_dict = {param_y: 2} - with self.assertWarns(DeprecationWarning): - evolution_problem = EvolutionProblem( - hamiltonian, 2, Zero, param_value_dict=param_dict - ) - with assert_raises(ValueError): - evolution_problem.validate_params() - - with self.subTest(msg="Empty dict."): - with self.assertWarns(DeprecationWarning): - hamiltonian = param_x * X + param_y * Y - - param_dict = {} - with self.assertWarns(DeprecationWarning): - evolution_problem = EvolutionProblem( - hamiltonian, 2, Zero, param_value_dict=param_dict - ) - with assert_raises(ValueError): - evolution_problem.validate_params() - - with self.subTest(msg="Extra parameter in dict."): - with self.assertWarns(DeprecationWarning): - hamiltonian = param_x * X + param_y * Y - - param_dict = {param_y: 2, param_x: 1, Parameter("z"): 1} - with self.assertWarns(DeprecationWarning): - evolution_problem = EvolutionProblem( - hamiltonian, 2, Zero, param_value_dict=param_dict - ) - with assert_raises(ValueError): - evolution_problem.validate_params() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/evolvers/test_evolution_result.py b/test/python/algorithms/evolvers/test_evolution_result.py deleted file mode 100644 index 2fe40d8c66f8..000000000000 --- a/test/python/algorithms/evolvers/test_evolution_result.py +++ /dev/null @@ -1,50 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Class for testing evolution result.""" -import unittest - -from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.algorithms.evolvers.evolution_result import EvolutionResult -from qiskit.opflow import Zero - - -class TestEvolutionResult(QiskitAlgorithmsTestCase): - """Class for testing evolution result and relevant metadata.""" - - def test_init_state(self): - """Tests that a class is initialized correctly with an evolved_state.""" - evolved_state = Zero - with self.assertWarns(DeprecationWarning): - evo_result = EvolutionResult(evolved_state=evolved_state) - - expected_state = Zero - expected_aux_ops_evaluated = None - - self.assertEqual(evo_result.evolved_state, expected_state) - self.assertEqual(evo_result.aux_ops_evaluated, expected_aux_ops_evaluated) - - def test_init_observable(self): - """Tests that a class is initialized correctly with an evolved_observable.""" - evolved_state = Zero - evolved_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] - with self.assertWarns(DeprecationWarning): - evo_result = EvolutionResult(evolved_state, evolved_aux_ops_evaluated) - - expected_state = Zero - expected_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] - - self.assertEqual(evo_result.evolved_state, expected_state) - self.assertEqual(evo_result.aux_ops_evaluated, expected_aux_ops_evaluated) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/evolvers/trotterization/__init__.py b/test/python/algorithms/evolvers/trotterization/__init__.py deleted file mode 100644 index 96c0cf22bec9..000000000000 --- a/test/python/algorithms/evolvers/trotterization/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py b/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py deleted file mode 100644 index 6635d4db5d85..000000000000 --- a/test/python/algorithms/evolvers/trotterization/test_trotter_qrte.py +++ /dev/null @@ -1,259 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test TrotterQRTE.""" - -import unittest -import warnings - -from test.python.opflow import QiskitOpflowTestCase -from ddt import ddt, data, unpack -import numpy as np -from numpy.testing import assert_raises - -from qiskit import BasicAer, QuantumCircuit -from qiskit.algorithms import EvolutionProblem -from qiskit.algorithms.evolvers.trotterization import ( - TrotterQRTE, -) -from qiskit.circuit.library import ZGate -from qiskit.quantum_info import Statevector -from qiskit.utils import algorithm_globals, QuantumInstance -from qiskit.circuit import Parameter -from qiskit.opflow import ( - X, - Z, - Zero, - VectorStateFn, - StateFn, - I, - Y, - SummedOp, - ExpectationFactory, -) -from qiskit.synthesis import SuzukiTrotter, QDrift - - -@ddt -class TestTrotterQRTE(QiskitOpflowTestCase): - """TrotterQRTE tests.""" - - def setUp(self): - super().setUp() - self.seed = 50 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - backend_statevector = BasicAer.get_backend("statevector_simulator") - backend_qasm = BasicAer.get_backend("qasm_simulator") - with self.assertWarns(DeprecationWarning): - self.quantum_instance = QuantumInstance( - backend=backend_statevector, - shots=1, - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - self.quantum_instance_qasm = QuantumInstance( - backend=backend_qasm, - shots=8000, - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - self.backends_dict = { - "qi_sv": self.quantum_instance, - "qi_qasm": self.quantum_instance_qasm, - "b_sv": backend_statevector, - "None": None, - } - - self.backends_names = ["qi_qasm", "b_sv", "None", "qi_sv"] - self.backends_names_not_none = ["qi_sv", "b_sv", "qi_qasm"] - - @data( - ( - None, - VectorStateFn( - Statevector([0.29192658 - 0.45464871j, 0.70807342 - 0.45464871j], dims=(2,)) - ), - ), - ( - SuzukiTrotter(), - VectorStateFn(Statevector([0.29192658 - 0.84147098j, 0.0 - 0.45464871j], dims=(2,))), - ), - ) - @unpack - def test_trotter_qrte_trotter_single_qubit(self, product_formula, expected_state): - """Test for default TrotterQRTE on a single qubit.""" - operator = SummedOp([X, Z]) - initial_state = StateFn([1, 0]) - time = 1 - with self.assertWarns(DeprecationWarning): - evolution_problem = EvolutionProblem(operator, time, initial_state) - trotter_qrte = TrotterQRTE(product_formula=product_formula) - evolution_result_state_circuit = trotter_qrte.evolve(evolution_problem).evolved_state - - np.testing.assert_equal(evolution_result_state_circuit.eval(), expected_state) - - def test_trotter_qrte_trotter_single_qubit_aux_ops(self): - """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" - operator = SummedOp([X, Z]) - # LieTrotter with 1 rep - aux_ops = [X, Y] - - initial_state = Zero - time = 3 - with self.assertWarns(DeprecationWarning): - evolution_problem = EvolutionProblem(operator, time, initial_state, aux_ops) - - expected_evolved_state = VectorStateFn( - Statevector([0.98008514 + 0.13970775j, 0.01991486 + 0.13970775j], dims=(2,)) - ) - expected_aux_ops_evaluated = [(0.078073, 0.0), (0.268286, 0.0)] - expected_aux_ops_evaluated_qasm = [ - (0.05799999999999995, 0.011161518713866855), - (0.2495, 0.010826759383582883), - ] - - for backend_name in self.backends_names_not_none: - with self.subTest(msg=f"Test {backend_name} backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - backend = self.backends_dict[backend_name] - with self.assertWarns(DeprecationWarning): - expectation = ExpectationFactory.build( - operator=operator, - backend=backend, - ) - with self.assertWarns(DeprecationWarning): - trotter_qrte = TrotterQRTE(quantum_instance=backend, expectation=expectation) - evolution_result = trotter_qrte.evolve(evolution_problem) - - np.testing.assert_equal( - evolution_result.evolved_state.eval(), expected_evolved_state - ) - if backend_name == "qi_qasm": - expected_aux_ops_evaluated = expected_aux_ops_evaluated_qasm - np.testing.assert_array_almost_equal( - evolution_result.aux_ops_evaluated, expected_aux_ops_evaluated - ) - - @data( - ( - SummedOp([(X ^ Y), (Y ^ X)]), - VectorStateFn( - Statevector( - [-0.41614684 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.90929743 + 0.0j], dims=(2, 2) - ) - ), - ), - ( - (Z ^ Z) + (Z ^ I) + (I ^ Z), - VectorStateFn( - Statevector( - [-0.9899925 - 0.14112001j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], dims=(2, 2) - ) - ), - ), - ( - Y ^ Y, - VectorStateFn( - Statevector( - [0.54030231 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.84147098j], dims=(2, 2) - ) - ), - ), - ) - @unpack - def test_trotter_qrte_trotter_two_qubits(self, operator, expected_state): - """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" - # LieTrotter with 1 rep - initial_state = StateFn([1, 0, 0, 0]) - with self.assertWarns(DeprecationWarning): - evolution_problem = EvolutionProblem(operator, 1, initial_state) - trotter_qrte = TrotterQRTE() - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) - - def test_trotter_qrte_trotter_two_qubits_with_params(self): - """Test for TrotterQRTE on two qubits with a parametrized Hamiltonian.""" - # LieTrotter with 1 rep - initial_state = StateFn([1, 0, 0, 0]) - w_param = Parameter("w") - u_param = Parameter("u") - params_dict = {w_param: 2.0, u_param: 3.0} - operator = w_param * (Z ^ Z) / 2.0 + (Z ^ I) + u_param * (I ^ Z) / 3.0 - time = 1 - expected_state = VectorStateFn( - Statevector([-0.9899925 - 0.14112001j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], dims=(2, 2)) - ) - with self.assertWarns(DeprecationWarning): - evolution_problem = EvolutionProblem( - operator, time, initial_state, param_value_dict=params_dict - ) - trotter_qrte = TrotterQRTE() - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) - - @data( - ( - Zero, - VectorStateFn( - Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j], dims=(2,)) - ), - ), - ( - QuantumCircuit(1).compose(ZGate(), [0]), - VectorStateFn( - Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j], dims=(2,)) - ), - ), - ) - @unpack - def test_trotter_qrte_qdrift(self, initial_state, expected_state): - """Test for TrotterQRTE with QDrift.""" - operator = SummedOp([X, Z]) - time = 1 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - with self.assertWarns(DeprecationWarning): - evolution_problem = EvolutionProblem(operator, time, initial_state) - trotter_qrte = TrotterQRTE(product_formula=QDrift(seed=0)) - evolution_result = trotter_qrte.evolve(evolution_problem) - np.testing.assert_equal(evolution_result.evolved_state.eval(), expected_state) - - @data((Parameter("t"), {}), (None, {Parameter("x"): 2}), (None, None)) - @unpack - def test_trotter_qrte_trotter_errors(self, t_param, param_value_dict): - """Test TrotterQRTE with raising errors.""" - operator = X * Parameter("t") + Z - initial_state = Zero - time = 1 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - with self.assertWarns(DeprecationWarning): - trotter_qrte = TrotterQRTE() - evolution_problem = EvolutionProblem( - operator, - time, - initial_state, - t_param=t_param, - param_value_dict=param_value_dict, - ) - with assert_raises(ValueError): - _ = trotter_qrte.evolve(evolution_problem) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/gradients/__init__.py b/test/python/algorithms/gradients/__init__.py deleted file mode 100644 index 756b2a26196b..000000000000 --- a/test/python/algorithms/gradients/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for the primitive-based gradients""" diff --git a/test/python/algorithms/gradients/logging_primitives.py b/test/python/algorithms/gradients/logging_primitives.py deleted file mode 100644 index ef2cad9e2cc0..000000000000 --- a/test/python/algorithms/gradients/logging_primitives.py +++ /dev/null @@ -1,42 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test primitives that check what kind of operations are in the circuits they execute.""" - -from qiskit.primitives import Estimator, Sampler - - -class LoggingEstimator(Estimator): - """An estimator checking what operations were in the circuits it executed.""" - - def __init__(self, options=None, operations_callback=None): - super().__init__(options=options) - self.operations_callback = operations_callback - - def _run(self, circuits, observables, parameter_values, **run_options): - if self.operations_callback is not None: - ops = [circuit.count_ops() for circuit in circuits] - self.operations_callback(ops) - return super()._run(circuits, observables, parameter_values, **run_options) - - -class LoggingSampler(Sampler): - """A sampler checking what operations were in the circuits it executed.""" - - def __init__(self, operations_callback): - super().__init__() - self.operations_callback = operations_callback - - def _run(self, circuits, parameter_values, **run_options): - ops = [circuit.count_ops() for circuit in circuits] - self.operations_callback(ops) - return super()._run(circuits, parameter_values, **run_options) diff --git a/test/python/algorithms/gradients/test_estimator_gradient.py b/test/python/algorithms/gradients/test_estimator_gradient.py deleted file mode 100644 index 202727c5835a..000000000000 --- a/test/python/algorithms/gradients/test_estimator_gradient.py +++ /dev/null @@ -1,519 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# ============================================================================= - -"""Test Estimator Gradients""" - -import unittest - -import numpy as np -from ddt import ddt, data, unpack - -from qiskit import QuantumCircuit -from qiskit.algorithms.gradients import ( - FiniteDiffEstimatorGradient, - LinCombEstimatorGradient, - ParamShiftEstimatorGradient, - SPSAEstimatorGradient, - ReverseEstimatorGradient, - DerivativeType, -) -from qiskit.circuit import Parameter -from qiskit.circuit.library import EfficientSU2, RealAmplitudes -from qiskit.circuit.library.standard_gates import RXXGate, RYYGate, RZXGate, RZZGate -from qiskit.primitives import Estimator -from qiskit.quantum_info import Operator, SparsePauliOp, Pauli -from qiskit.quantum_info.random import random_pauli_list -from qiskit.test import QiskitTestCase - -from .logging_primitives import LoggingEstimator - -gradient_factories = [ - lambda estimator: FiniteDiffEstimatorGradient(estimator, epsilon=1e-6, method="central"), - lambda estimator: FiniteDiffEstimatorGradient(estimator, epsilon=1e-6, method="forward"), - lambda estimator: FiniteDiffEstimatorGradient(estimator, epsilon=1e-6, method="backward"), - ParamShiftEstimatorGradient, - LinCombEstimatorGradient, - lambda estimator: ReverseEstimatorGradient(), # does not take an estimator! -] - - -@ddt -class TestEstimatorGradient(QiskitTestCase): - """Test Estimator Gradient""" - - @data(*gradient_factories) - def test_gradient_operators(self, grad): - """Test the estimator gradient for different operators""" - estimator = Estimator() - a = Parameter("a") - qc = QuantumCircuit(1) - qc.h(0) - qc.p(a, 0) - qc.h(0) - gradient = grad(estimator) - op = SparsePauliOp.from_list([("Z", 1)]) - correct_result = -1 / np.sqrt(2) - param = [np.pi / 4] - value = gradient.run([qc], [op], [param]).result().gradients[0] - self.assertAlmostEqual(value[0], correct_result, 3) - op = SparsePauliOp.from_list([("Z", 1)]) - value = gradient.run([qc], [op], [param]).result().gradients[0] - self.assertAlmostEqual(value[0], correct_result, 3) - op = Operator.from_label("Z") - value = gradient.run([qc], [op], [param]).result().gradients[0] - self.assertAlmostEqual(value[0], correct_result, 3) - - @data(*gradient_factories) - def test_single_circuit_observable(self, grad): - """Test the estimator gradient for a single circuit and observable""" - estimator = Estimator() - a = Parameter("a") - qc = QuantumCircuit(1) - qc.h(0) - qc.p(a, 0) - qc.h(0) - gradient = grad(estimator) - op = SparsePauliOp.from_list([("Z", 1)]) - correct_result = -1 / np.sqrt(2) - param = [np.pi / 4] - value = gradient.run(qc, op, [param]).result().gradients[0] - self.assertAlmostEqual(value[0], correct_result, 3) - - @data(*gradient_factories) - def test_gradient_p(self, grad): - """Test the estimator gradient for p""" - estimator = Estimator() - a = Parameter("a") - qc = QuantumCircuit(1) - qc.h(0) - qc.p(a, 0) - qc.h(0) - gradient = grad(estimator) - op = SparsePauliOp.from_list([("Z", 1)]) - param_list = [[np.pi / 4], [0], [np.pi / 2]] - correct_results = [[-1 / np.sqrt(2)], [0], [-1]] - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [op], [param]).result().gradients[0] - for j, value in enumerate(gradients): - self.assertAlmostEqual(value, correct_results[i][j], 3) - - @data(*gradient_factories) - def test_gradient_u(self, grad): - """Test the estimator gradient for u""" - estimator = Estimator() - a = Parameter("a") - b = Parameter("b") - c = Parameter("c") - qc = QuantumCircuit(1) - qc.h(0) - qc.u(a, b, c, 0) - qc.h(0) - gradient = grad(estimator) - op = SparsePauliOp.from_list([("Z", 1)]) - - param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] - correct_results = [[-0.70710678, 0.0, 0.0], [-0.35355339, -0.85355339, -0.85355339]] - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [op], [param]).result().gradients[0] - for j, value in enumerate(gradients): - self.assertAlmostEqual(value, correct_results[i][j], 3) - - @data(*gradient_factories) - def test_gradient_efficient_su2(self, grad): - """Test the estimator gradient for EfficientSU2""" - estimator = Estimator() - qc = EfficientSU2(2, reps=1) - op = SparsePauliOp.from_list([("ZI", 1)]) - gradient = grad(estimator) - param_list = [ - [np.pi / 4 for param in qc.parameters], - [np.pi / 2 for param in qc.parameters], - ] - correct_results = [ - [ - -0.35355339, - -0.70710678, - 0, - 0.35355339, - 0, - -0.70710678, - 0, - 0, - ], - [0, 0, 0, 1, 0, 0, 0, 0], - ] - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [op], [param]).result().gradients[0] - np.testing.assert_allclose(gradients, correct_results[i], atol=1e-3) - - @data(*gradient_factories) - def test_gradient_2qubit_gate(self, grad): - """Test the estimator gradient for 2 qubit gates""" - estimator = Estimator() - for gate in [RXXGate, RYYGate, RZZGate, RZXGate]: - param_list = [[np.pi / 4], [np.pi / 2]] - correct_results = [ - [-0.70710678], - [-1], - ] - op = SparsePauliOp.from_list([("ZI", 1)]) - for i, param in enumerate(param_list): - a = Parameter("a") - qc = QuantumCircuit(2) - gradient = grad(estimator) - - if gate is RZZGate: - qc.h([0, 1]) - qc.append(gate(a), [qc.qubits[0], qc.qubits[1]], []) - qc.h([0, 1]) - else: - qc.append(gate(a), [qc.qubits[0], qc.qubits[1]], []) - gradients = gradient.run([qc], [op], [param]).result().gradients[0] - np.testing.assert_allclose(gradients, correct_results[i], atol=1e-3) - - @data(*gradient_factories) - def test_gradient_parameter_coefficient(self, grad): - """Test the estimator gradient for parameter variables with coefficients""" - estimator = Estimator() - qc = RealAmplitudes(num_qubits=2, reps=1) - qc.rz(qc.parameters[0].exp() + 2 * qc.parameters[1], 0) - qc.rx(3.0 * qc.parameters[0] + qc.parameters[1].sin(), 1) - qc.u(qc.parameters[0], qc.parameters[1], qc.parameters[3], 1) - qc.p(2 * qc.parameters[0] + 1, 0) - qc.rxx(qc.parameters[0] + 2, 0, 1) - gradient = grad(estimator) - param_list = [[np.pi / 4 for _ in qc.parameters], [np.pi / 2 for _ in qc.parameters]] - correct_results = [ - [-0.7266653, -0.4905135, -0.0068606, -0.9228880], - [-3.5972095, 0.10237173, -0.3117748, 0], - ] - op = SparsePauliOp.from_list([("ZI", 1)]) - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [op], [param]).result().gradients[0] - np.testing.assert_allclose(gradients, correct_results[i], atol=1e-3) - - @data(*gradient_factories) - def test_gradient_parameters(self, grad): - """Test the estimator gradient for parameters""" - estimator = Estimator() - a = Parameter("a") - b = Parameter("b") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.rx(b, 0) - gradient = grad(estimator) - param_list = [[np.pi / 4, np.pi / 2]] - correct_results = [ - [-0.70710678], - ] - op = SparsePauliOp.from_list([("Z", 1)]) - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [op], [param], parameters=[[a]]).result().gradients[0] - np.testing.assert_allclose(gradients, correct_results[i], atol=1e-3) - - # parameter order - with self.subTest(msg="The order of gradients"): - c = Parameter("c") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.rz(b, 0) - qc.rx(c, 0) - - param_list = [[np.pi / 4, np.pi / 2, np.pi / 3]] - correct_results = [ - [-0.35355339, 0.61237244, -0.61237244], - [-0.61237244, 0.61237244, -0.35355339], - [-0.35355339, -0.61237244], - [-0.61237244, -0.35355339], - ] - param = [[a, b, c], [c, b, a], [a, c], [c, a]] - op = SparsePauliOp.from_list([("Z", 1)]) - for i, p in enumerate(param): - gradient = grad(estimator) - gradients = ( - gradient.run([qc], [op], param_list, parameters=[p]).result().gradients[0] - ) - np.testing.assert_allclose(gradients, correct_results[i], atol=1e-3) - - @data(*gradient_factories) - def test_gradient_multi_arguments(self, grad): - """Test the estimator gradient for multiple arguments""" - estimator = Estimator() - a = Parameter("a") - b = Parameter("b") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc2 = QuantumCircuit(1) - qc2.rx(b, 0) - gradient = grad(estimator) - param_list = [[np.pi / 4], [np.pi / 2]] - correct_results = [ - [-0.70710678], - [-1], - ] - op = SparsePauliOp.from_list([("Z", 1)]) - gradients = gradient.run([qc, qc2], [op] * 2, param_list).result().gradients - np.testing.assert_allclose(gradients, correct_results, atol=1e-3) - - c = Parameter("c") - qc3 = QuantumCircuit(1) - qc3.rx(c, 0) - qc3.ry(a, 0) - param_list2 = [[np.pi / 4], [np.pi / 4, np.pi / 4], [np.pi / 4, np.pi / 4]] - correct_results2 = [ - [-0.70710678], - [-0.5], - [-0.5, -0.5], - ] - gradients2 = ( - gradient.run([qc, qc3, qc3], [op] * 3, param_list2, parameters=[[a], [c], None]) - .result() - .gradients - ) - np.testing.assert_allclose(gradients2[0], correct_results2[0], atol=1e-3) - np.testing.assert_allclose(gradients2[1], correct_results2[1], atol=1e-3) - np.testing.assert_allclose(gradients2[2], correct_results2[2], atol=1e-3) - - @data(*gradient_factories) - def test_gradient_validation(self, grad): - """Test estimator gradient's validation""" - estimator = Estimator() - a = Parameter("a") - qc = QuantumCircuit(1) - qc.rx(a, 0) - gradient = grad(estimator) - param_list = [[np.pi / 4], [np.pi / 2]] - op = SparsePauliOp.from_list([("Z", 1)]) - with self.assertRaises(ValueError): - gradient.run([qc], [op], param_list) - with self.assertRaises(ValueError): - gradient.run([qc, qc], [op, op], param_list, parameters=[[a]]) - with self.assertRaises(ValueError): - gradient.run([qc, qc], [op], param_list, parameters=[[a]]) - with self.assertRaises(ValueError): - gradient.run([qc], [op], [[np.pi / 4, np.pi / 4]]) - - def test_spsa_gradient(self): - """Test the SPSA estimator gradient""" - estimator = Estimator() - with self.assertRaises(ValueError): - _ = SPSAEstimatorGradient(estimator, epsilon=-0.1) - a = Parameter("a") - b = Parameter("b") - qc = QuantumCircuit(2) - qc.rx(b, 0) - qc.rx(a, 1) - param_list = [[1, 1]] - correct_results = [[-0.84147098, 0.84147098]] - op = SparsePauliOp.from_list([("ZI", 1)]) - gradient = SPSAEstimatorGradient(estimator, epsilon=1e-6, seed=123) - gradients = gradient.run([qc], [op], param_list).result().gradients - np.testing.assert_allclose(gradients, correct_results, atol=1e-3) - - # multi parameters - with self.subTest(msg="Multiple parameters"): - gradient = SPSAEstimatorGradient(estimator, epsilon=1e-6, seed=123) - param_list2 = [[1, 1], [1, 1], [3, 3]] - gradients2 = ( - gradient.run([qc] * 3, [op] * 3, param_list2, parameters=[None, [b], None]) - .result() - .gradients - ) - correct_results2 = [[-0.84147098, 0.84147098], [0.84147098], [-0.14112001, 0.14112001]] - for grad, correct in zip(gradients2, correct_results2): - np.testing.assert_allclose(grad, correct, atol=1e-3) - - # batch size - with self.subTest(msg="Batch size"): - correct_results = [[-0.84147098, 0.1682942]] - gradient = SPSAEstimatorGradient(estimator, epsilon=1e-6, batch_size=5, seed=123) - gradients = gradient.run([qc], [op], param_list).result().gradients - np.testing.assert_allclose(gradients, correct_results, atol=1e-3) - - # parameter order - with self.subTest(msg="The order of gradients"): - gradient = SPSAEstimatorGradient(estimator, epsilon=1e-6, seed=123) - c = Parameter("c") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.rz(b, 0) - qc.rx(c, 0) - op = SparsePauliOp.from_list([("Z", 1)]) - param_list3 = [[np.pi / 4, np.pi / 2, np.pi / 3]] - param = [[a, b, c], [c, b, a], [a, c], [c, a]] - expected = [ - [-0.3535525, 0.3535525, 0.3535525], - [0.3535525, 0.3535525, -0.3535525], - [-0.3535525, 0.3535525], - [0.3535525, -0.3535525], - ] - for i, p in enumerate(param): - gradient = SPSAEstimatorGradient(estimator, epsilon=1e-6, seed=123) - gradients = ( - gradient.run([qc], [op], param_list3, parameters=[p]).result().gradients[0] - ) - np.testing.assert_allclose(gradients, expected[i], atol=1e-3) - - @data(ParamShiftEstimatorGradient, LinCombEstimatorGradient) - def test_gradient_random_parameters(self, grad): - """Test param shift and lin comb w/ random parameters""" - rng = np.random.default_rng(123) - qc = RealAmplitudes(num_qubits=3, reps=1) - params = qc.parameters - qc.rx(3.0 * params[0] + params[1].sin(), 0) - qc.ry(params[0].exp() + 2 * params[1], 1) - qc.rz(params[0] * params[1] - params[2], 2) - qc.p(2 * params[0] + 1, 0) - qc.u(params[0].sin(), params[1] - 2, params[2] * params[3], 1) - qc.sx(2) - qc.rxx(params[0].sin(), 1, 2) - qc.ryy(params[1].cos(), 2, 0) - qc.rzz(params[2] * 2, 0, 1) - qc.crx(params[0].exp(), 1, 2) - qc.cry(params[1].arctan(), 2, 0) - qc.crz(params[2] * -2, 0, 1) - qc.dcx(0, 1) - qc.csdg(0, 1) - qc.ccx(0, 1, 2) - qc.iswap(0, 2) - qc.swap(1, 2) - qc.global_phase = params[0] * params[1] + params[2].cos().exp() - - size = 10 - op = SparsePauliOp(random_pauli_list(num_qubits=qc.num_qubits, size=size, seed=rng)) - op.coeffs = rng.normal(0, 10, size) - - estimator = Estimator() - findiff = FiniteDiffEstimatorGradient(estimator, 1e-6) - gradient = grad(estimator) - - num_tries = 10 - param_values = rng.normal(0, 2, (num_tries, qc.num_parameters)).tolist() - np.testing.assert_allclose( - findiff.run([qc] * num_tries, [op] * num_tries, param_values).result().gradients, - gradient.run([qc] * num_tries, [op] * num_tries, param_values).result().gradients, - rtol=1e-4, - ) - - @data((DerivativeType.IMAG, -1.0), (DerivativeType.COMPLEX, -1.0j)) - @unpack - def test_complex_gradient(self, derivative_type, expected_gradient_value): - """Tests if the ``LinCombEstimatorGradient`` has the correct value.""" - estimator = Estimator() - lcu = LinCombEstimatorGradient(estimator, derivative_type=derivative_type) - reverse = ReverseEstimatorGradient(derivative_type=derivative_type) - - for gradient in [lcu, reverse]: - with self.subTest(gradient=gradient): - c = QuantumCircuit(1) - c.rz(Parameter("p"), 0) - result = gradient.run([c], [Pauli("I")], [[0.0]]).result() - self.assertAlmostEqual(result.gradients[0][0], expected_gradient_value) - - @data( - FiniteDiffEstimatorGradient, - ParamShiftEstimatorGradient, - LinCombEstimatorGradient, - SPSAEstimatorGradient, - ) - def test_options(self, grad): - """Test estimator gradient's run options""" - a = Parameter("a") - qc = QuantumCircuit(1) - qc.rx(a, 0) - op = SparsePauliOp.from_list([("Z", 1)]) - estimator = Estimator(options={"shots": 100}) - with self.subTest("estimator"): - if grad is FiniteDiffEstimatorGradient or grad is SPSAEstimatorGradient: - gradient = grad(estimator, epsilon=1e-6) - else: - gradient = grad(estimator) - options = gradient.options - result = gradient.run([qc], [op], [[1]]).result() - self.assertEqual(result.options.get("shots"), 100) - self.assertEqual(options.get("shots"), 100) - - with self.subTest("gradient init"): - if grad is FiniteDiffEstimatorGradient or grad is SPSAEstimatorGradient: - gradient = grad(estimator, epsilon=1e-6, options={"shots": 200}) - else: - gradient = grad(estimator, options={"shots": 200}) - options = gradient.options - result = gradient.run([qc], [op], [[1]]).result() - self.assertEqual(result.options.get("shots"), 200) - self.assertEqual(options.get("shots"), 200) - - with self.subTest("gradient update"): - if grad is FiniteDiffEstimatorGradient or grad is SPSAEstimatorGradient: - gradient = grad(estimator, epsilon=1e-6, options={"shots": 200}) - else: - gradient = grad(estimator, options={"shots": 200}) - gradient.update_default_options(shots=100) - options = gradient.options - result = gradient.run([qc], [op], [[1]]).result() - self.assertEqual(result.options.get("shots"), 100) - self.assertEqual(options.get("shots"), 100) - - with self.subTest("gradient run"): - if grad is FiniteDiffEstimatorGradient or grad is SPSAEstimatorGradient: - gradient = grad(estimator, epsilon=1e-6, options={"shots": 200}) - else: - gradient = grad(estimator, options={"shots": 200}) - options = gradient.options - result = gradient.run([qc], [op], [[1]], shots=300).result() - self.assertEqual(result.options.get("shots"), 300) - # Only default + estimator options. Not run. - self.assertEqual(options.get("shots"), 200) - - @data( - FiniteDiffEstimatorGradient, - ParamShiftEstimatorGradient, - LinCombEstimatorGradient, - SPSAEstimatorGradient, - ) - def test_operations_preserved(self, gradient_cls): - """Test non-parameterized instructions are preserved and not unrolled.""" - x = Parameter("x") - circuit = QuantumCircuit(2) - circuit.initialize([0.5, 0.5, 0.5, 0.5]) # this should remain as initialize - circuit.crx(x, 0, 1) # this should get unrolled - - values = [np.pi / 2] - expect = -1 / (2 * np.sqrt(2)) - - observable = SparsePauliOp(["XX"]) - - ops = [] - - def operations_callback(op): - ops.append(op) - - estimator = LoggingEstimator(operations_callback=operations_callback) - - if gradient_cls in [SPSAEstimatorGradient, FiniteDiffEstimatorGradient]: - gradient = gradient_cls(estimator, epsilon=0.01) - else: - gradient = gradient_cls(estimator) - - job = gradient.run([circuit], [observable], [values]) - result = job.result() - - with self.subTest(msg="assert initialize is preserved"): - self.assertTrue(all("initialize" in ops_i[0].keys() for ops_i in ops)) - - with self.subTest(msg="assert result is correct"): - self.assertAlmostEqual(result.gradients[0].item(), expect, places=5) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/gradients/test_qfi.py b/test/python/algorithms/gradients/test_qfi.py deleted file mode 100644 index b0d05ac7030f..000000000000 --- a/test/python/algorithms/gradients/test_qfi.py +++ /dev/null @@ -1,151 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# ============================================================================= - -"""Test QFI.""" - -import unittest -from ddt import ddt, data - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.algorithms.gradients import LinCombQGT, ReverseQGT, QFI, DerivativeType -from qiskit.circuit import Parameter -from qiskit.circuit.parametervector import ParameterVector -from qiskit.primitives import Estimator -from qiskit.test import QiskitTestCase - - -@ddt -class TestQFI(QiskitTestCase): - """Test QFI""" - - def setUp(self): - super().setUp() - self.estimator = Estimator() - self.lcu_qgt = LinCombQGT(self.estimator, derivative_type=DerivativeType.REAL) - self.reverse_qgt = ReverseQGT(derivative_type=DerivativeType.REAL) - - def test_qfi(self): - """Test if the quantum fisher information calculation is correct for a simple test case. - QFI = [[1, 0], [0, 1]] - [[0, 0], [0, cos^2(a)]] - """ - # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - param_list = [[np.pi / 4, 0.1], [np.pi, 0.1], [np.pi / 2, 0.1]] - correct_values = [[[1, 0], [0, 0.5]], [[1, 0], [0, 0]], [[1, 0], [0, 1]]] - - qfi = QFI(self.lcu_qgt) - for i, param in enumerate(param_list): - qfis = qfi.run([qc], [param]).result().qfis - np.testing.assert_allclose(qfis[0], correct_values[i], atol=1e-3) - - def test_qfi_phase_fix(self): - """Test the phase-fix argument in the QFI calculation""" - # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - param = [np.pi / 4, 0.1] - # test for different values - correct_values = [[1, 0], [0, 1]] - qgt = LinCombQGT(self.estimator, phase_fix=False) - qfi = QFI(qgt) - qfis = qfi.run([qc], [param]).result().qfis - np.testing.assert_allclose(qfis[0], correct_values, atol=1e-3) - - @data("lcu", "reverse") - def test_qfi_maxcut(self, qgt_kind): - """Test the QFI for a simple MaxCut problem. - - This is interesting because it contains the same parameters in different gates. - """ - # create maxcut circuit for the hamiltonian - # H = (I ^ I ^ Z ^ Z) + (I ^ Z ^ I ^ Z) + (Z ^ I ^ I ^ Z) + (I ^ Z ^ Z ^ I) - - x = ParameterVector("x", 2) - ansatz = QuantumCircuit(4) - - # initial hadamard layer - ansatz.h(ansatz.qubits) - - # e^{iZZ} layers - def expiz(qubit0, qubit1): - ansatz.cx(qubit0, qubit1) - ansatz.rz(2 * x[0], qubit1) - ansatz.cx(qubit0, qubit1) - - expiz(2, 1) - expiz(3, 0) - expiz(2, 0) - expiz(1, 0) - - # mixer layer with RX gates - for i in range(ansatz.num_qubits): - ansatz.rx(2 * x[1], i) - - reference = np.array([[16.0, -5.551], [-5.551, 18.497]]) - param = [0.4, 0.69] - - qgt = self.lcu_qgt if qgt_kind == "lcu" else self.reverse_qgt - qfi = QFI(qgt) - qfi_result = qfi.run([ansatz], [param]).result().qfis - np.testing.assert_array_almost_equal(qfi_result[0], reference, decimal=3) - - def test_options(self): - """Test QFI's options""" - a = Parameter("a") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qgt = LinCombQGT(estimator=self.estimator, options={"shots": 100}) - - with self.subTest("QGT"): - qfi = QFI(qgt=qgt) - options = qfi.options - result = qfi.run([qc], [[1]]).result() - self.assertEqual(result.options.get("shots"), 100) - self.assertEqual(options.get("shots"), 100) - - with self.subTest("QFI init"): - qfi = QFI(qgt=qgt, options={"shots": 200}) - result = qfi.run([qc], [[1]]).result() - options = qfi.options - self.assertEqual(result.options.get("shots"), 200) - self.assertEqual(options.get("shots"), 200) - - with self.subTest("QFI update"): - qfi = QFI(qgt, options={"shots": 200}) - qfi.update_default_options(shots=100) - options = qfi.options - result = qfi.run([qc], [[1]]).result() - self.assertEqual(result.options.get("shots"), 100) - self.assertEqual(options.get("shots"), 100) - - with self.subTest("QFI run"): - qfi = QFI(qgt=qgt, options={"shots": 200}) - result = qfi.run([qc], [[0]], shots=300).result() - options = qfi.options - self.assertEqual(result.options.get("shots"), 300) - self.assertEqual(options.get("shots"), 200) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/gradients/test_qgt.py b/test/python/algorithms/gradients/test_qgt.py deleted file mode 100644 index 74f22d462d7c..000000000000 --- a/test/python/algorithms/gradients/test_qgt.py +++ /dev/null @@ -1,309 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# ============================================================================= - -"""Test QGT.""" - -import unittest -from ddt import ddt, data - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.algorithms.gradients import DerivativeType, LinCombQGT, ReverseQGT -from qiskit.circuit import Parameter -from qiskit.circuit.library import RealAmplitudes -from qiskit.primitives import Estimator -from qiskit.test import QiskitTestCase - -from .logging_primitives import LoggingEstimator - - -@ddt -class TestQGT(QiskitTestCase): - """Test QGT""" - - def setUp(self): - super().setUp() - self.estimator = Estimator() - - @data(LinCombQGT, ReverseQGT) - def test_qgt_derivative_type(self, qgt_type): - """Test QGT derivative_type""" - args = () if qgt_type == ReverseQGT else (self.estimator,) - qgt = qgt_type(*args, derivative_type=DerivativeType.REAL) - - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - param_list = [[np.pi / 4, 0], [np.pi / 2, np.pi / 4]] - correct_values = [ - np.array([[1, 0.707106781j], [-0.707106781j, 0.5]]) / 4, - np.array([[1, 1j], [-1j, 1]]) / 4, - ] - - # test real derivative - with self.subTest("Test with DerivativeType.REAL"): - qgt.derivative_type = DerivativeType.REAL - for i, param in enumerate(param_list): - qgt_result = qgt.run([qc], [param]).result().qgts - np.testing.assert_allclose(qgt_result[0], correct_values[i].real, atol=1e-3) - - # test imaginary derivative - with self.subTest("Test with DerivativeType.IMAG"): - qgt.derivative_type = DerivativeType.IMAG - for i, param in enumerate(param_list): - qgt_result = qgt.run([qc], [param]).result().qgts - np.testing.assert_allclose(qgt_result[0], correct_values[i].imag, atol=1e-3) - - # test real + imaginary derivative - with self.subTest("Test with DerivativeType.COMPLEX"): - qgt.derivative_type = DerivativeType.COMPLEX - for i, param in enumerate(param_list): - qgt_result = qgt.run([qc], [param]).result().qgts - np.testing.assert_allclose(qgt_result[0], correct_values[i], atol=1e-3) - - @data(LinCombQGT, ReverseQGT) - def test_qgt_phase_fix(self, qgt_type): - """Test the phase-fix argument in a QGT calculation""" - args = () if qgt_type == ReverseQGT else (self.estimator,) - qgt = qgt_type(*args, phase_fix=False) - - # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - param_list = [[np.pi / 4, 0], [np.pi / 2, np.pi / 4]] - correct_values = [ - np.array([[1, 0.707106781j], [-0.707106781j, 1]]) / 4, - np.array([[1, 1j], [-1j, 1]]) / 4, - ] - - # test real derivative - with self.subTest("Test phase fix with DerivativeType.REAL"): - qgt.derivative_type = DerivativeType.REAL - for i, param in enumerate(param_list): - qgt_result = qgt.run([qc], [param]).result().qgts - np.testing.assert_allclose(qgt_result[0], correct_values[i].real, atol=1e-3) - - # test imaginary derivative - with self.subTest("Test phase fix with DerivativeType.IMAG"): - qgt.derivative_type = DerivativeType.IMAG - for i, param in enumerate(param_list): - qgt_result = qgt.run([qc], [param]).result().qgts - np.testing.assert_allclose(qgt_result[0], correct_values[i].imag, atol=1e-3) - - # test real + imaginary derivative - with self.subTest("Test phase fix with DerivativeType.COMPLEX"): - qgt.derivative_type = DerivativeType.COMPLEX - for i, param in enumerate(param_list): - qgt_result = qgt.run([qc], [param]).result().qgts - np.testing.assert_allclose(qgt_result[0], correct_values[i], atol=1e-3) - - @data(LinCombQGT, ReverseQGT) - def test_qgt_coefficients(self, qgt_type): - """Test the derivative option of QGT""" - args = () if qgt_type == ReverseQGT else (self.estimator,) - qgt = qgt_type(*args, derivative_type=DerivativeType.REAL) - - qc = RealAmplitudes(num_qubits=2, reps=1) - qc.rz(qc.parameters[0].exp() + 2 * qc.parameters[1], 0) - qc.rx(3.0 * qc.parameters[2] + qc.parameters[3].sin(), 1) - - # test imaginary derivative - param_list = [ - [np.pi / 4 for param in qc.parameters], - [np.pi / 2 for param in qc.parameters], - ] - correct_values = ( - np.array( - [ - [ - [5.707309, 4.2924833, 1.5295868, 0.1938604], - [4.2924833, 4.9142136, 0.75, 0.8838835], - [1.5295868, 0.75, 3.4430195, 0.0758252], - [0.1938604, 0.8838835, 0.0758252, 1.1357233], - ], - [ - [1.0, 0.0, 1.0, 0.0], - [0.0, 1.0, 0.0, 0.0], - [1.0, 0.0, 10.0, -0.0], - [0.0, 0.0, -0.0, 1.0], - ], - ] - ) - / 4 - ) - for i, param in enumerate(param_list): - qgt_result = qgt.run([qc], [param]).result().qgts - np.testing.assert_allclose(qgt_result[0], correct_values[i], atol=1e-3) - - @data(LinCombQGT, ReverseQGT) - def test_qgt_parameters(self, qgt_type): - """Test the QGT with specified parameters""" - args = () if qgt_type == ReverseQGT else (self.estimator,) - qgt = qgt_type(*args, derivative_type=DerivativeType.REAL) - - a = Parameter("a") - b = Parameter("b") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.ry(b, 0) - param_values = [np.pi / 4, np.pi / 4] - qgt_result = qgt.run([qc], [param_values], [[a]]).result().qgts - np.testing.assert_allclose(qgt_result[0], [[1 / 4]], atol=1e-3) - - with self.subTest("Test with different parameter orders"): - c = Parameter("c") - qc2 = QuantumCircuit(1) - qc2.rx(a, 0) - qc2.rz(b, 0) - qc2.rx(c, 0) - param_values = [np.pi / 4, np.pi / 4, np.pi / 4] - params = [[a, b, c], [c, b, a], [a, c], [b, a]] - expected = [ - np.array( - [ - [0.25, 0.0, 0.1767767], - [0.0, 0.125, -0.08838835], - [0.1767767, -0.08838835, 0.1875], - ] - ), - np.array( - [ - [0.1875, -0.08838835, 0.1767767], - [-0.08838835, 0.125, 0.0], - [0.1767767, 0.0, 0.25], - ] - ), - np.array([[0.25, 0.1767767], [0.1767767, 0.1875]]), - np.array([[0.125, 0.0], [0.0, 0.25]]), - ] - for i, param in enumerate(params): - qgt_result = qgt.run([qc2], [param_values], [param]).result().qgts - np.testing.assert_allclose(qgt_result[0], expected[i], atol=1e-3) - - @data(LinCombQGT, ReverseQGT) - def test_qgt_multi_arguments(self, qgt_type): - """Test the QGT for multiple arguments""" - args = () if qgt_type == ReverseQGT else (self.estimator,) - qgt = qgt_type(*args, derivative_type=DerivativeType.REAL) - - a = Parameter("a") - b = Parameter("b") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.ry(b, 0) - qc2 = QuantumCircuit(1) - qc2.rx(a, 0) - qc2.ry(b, 0) - - param_list = [[np.pi / 4], [np.pi / 2]] - correct_values = [[[1 / 4]], [[1 / 4, 0], [0, 0]]] - param_list = [[np.pi / 4, np.pi / 4], [np.pi / 2, np.pi / 2]] - qgt_results = qgt.run([qc, qc2], param_list, [[a], None]).result().qgts - for i, _ in enumerate(param_list): - np.testing.assert_allclose(qgt_results[i], correct_values[i], atol=1e-3) - - @data(LinCombQGT, ReverseQGT) - def test_qgt_validation(self, qgt_type): - """Test estimator QGT's validation""" - args = () if qgt_type == ReverseQGT else (self.estimator,) - qgt = qgt_type(*args) - - a = Parameter("a") - qc = QuantumCircuit(1) - qc.rx(a, 0) - parameter_values = [[np.pi / 4]] - with self.subTest("assert number of circuits does not match"): - with self.assertRaises(ValueError): - qgt.run([qc, qc], parameter_values) - with self.subTest("assert number of parameter values does not match"): - with self.assertRaises(ValueError): - qgt.run([qc], [[np.pi / 4], [np.pi / 2]]) - with self.subTest("assert number of parameters does not match"): - with self.assertRaises(ValueError): - qgt.run([qc], parameter_values, parameters=[[a], [a]]) - - def test_options(self): - """Test QGT's options""" - a = Parameter("a") - qc = QuantumCircuit(1) - qc.rx(a, 0) - estimator = Estimator(options={"shots": 100}) - - with self.subTest("estimator"): - qgt = LinCombQGT(estimator) - options = qgt.options - result = qgt.run([qc], [[1]]).result() - self.assertEqual(result.options.get("shots"), 100) - self.assertEqual(options.get("shots"), 100) - - with self.subTest("QGT init"): - qgt = LinCombQGT(estimator, options={"shots": 200}) - result = qgt.run([qc], [[1]]).result() - options = qgt.options - self.assertEqual(result.options.get("shots"), 200) - self.assertEqual(options.get("shots"), 200) - - with self.subTest("QGT update"): - qgt = LinCombQGT(estimator, options={"shots": 200}) - qgt.update_default_options(shots=100) - options = qgt.options - result = qgt.run([qc], [[1]]).result() - self.assertEqual(result.options.get("shots"), 100) - self.assertEqual(options.get("shots"), 100) - - with self.subTest("QGT run"): - qgt = LinCombQGT(estimator, options={"shots": 200}) - result = qgt.run([qc], [[0]], shots=300).result() - options = qgt.options - self.assertEqual(result.options.get("shots"), 300) - self.assertEqual(options.get("shots"), 200) - - def test_operations_preserved(self): - """Test non-parameterized instructions are preserved and not unrolled.""" - x, y = Parameter("x"), Parameter("y") - circuit = QuantumCircuit(2) - circuit.initialize([0.5, 0.5, 0.5, 0.5]) # this should remain as initialize - circuit.crx(x, 0, 1) # this should get unrolled - circuit.ry(y, 0) - - values = [np.pi / 2, np.pi] - expect = np.diag([0.25, 0.5]) / 4 - - ops = [] - - def operations_callback(op): - ops.append(op) - - estimator = LoggingEstimator(operations_callback=operations_callback) - qgt = LinCombQGT(estimator, derivative_type=DerivativeType.REAL) - - job = qgt.run([circuit], [values]) - result = job.result() - - with self.subTest(msg="assert initialize is preserved"): - self.assertTrue(all("initialize" in ops_i[0].keys() for ops_i in ops)) - - with self.subTest(msg="assert result is correct"): - np.testing.assert_allclose(result.qgts[0], expect, atol=1e-5) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/gradients/test_sampler_gradient.py b/test/python/algorithms/gradients/test_sampler_gradient.py deleted file mode 100644 index 2faf44df6466..000000000000 --- a/test/python/algorithms/gradients/test_sampler_gradient.py +++ /dev/null @@ -1,690 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# ============================================================================= - -"""Test Sampler Gradients""" - -import unittest -from typing import List - -import numpy as np -from ddt import ddt, data - -from qiskit import QuantumCircuit -from qiskit.algorithms.gradients import ( - FiniteDiffSamplerGradient, - LinCombSamplerGradient, - ParamShiftSamplerGradient, - SPSASamplerGradient, -) -from qiskit.circuit import Parameter -from qiskit.circuit.library import EfficientSU2, RealAmplitudes -from qiskit.circuit.library.standard_gates import RXXGate -from qiskit.primitives import Sampler -from qiskit.result import QuasiDistribution -from qiskit.test import QiskitTestCase - -from .logging_primitives import LoggingSampler - -gradient_factories = [ - lambda sampler: FiniteDiffSamplerGradient(sampler, epsilon=1e-6, method="central"), - lambda sampler: FiniteDiffSamplerGradient(sampler, epsilon=1e-6, method="forward"), - lambda sampler: FiniteDiffSamplerGradient(sampler, epsilon=1e-6, method="backward"), - ParamShiftSamplerGradient, - LinCombSamplerGradient, -] - - -@ddt -class TestSamplerGradient(QiskitTestCase): - """Test Sampler Gradient""" - - @data(*gradient_factories) - def test_single_circuit(self, grad): - """Test the sampler gradient for a single circuit""" - sampler = Sampler() - a = Parameter("a") - qc = QuantumCircuit(1) - qc.h(0) - qc.p(a, 0) - qc.h(0) - qc.measure_all() - gradient = grad(sampler) - param_list = [[np.pi / 4], [0], [np.pi / 2]] - expected = [ - [{0: -0.5 / np.sqrt(2), 1: 0.5 / np.sqrt(2)}], - [{0: 0, 1: 0}], - [{0: -0.499999, 1: 0.499999}], - ] - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [param]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=1) - array2 = _quasi2array(expected[i], num_qubits=1) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(*gradient_factories) - def test_gradient_p(self, grad): - """Test the sampler gradient for p""" - sampler = Sampler() - a = Parameter("a") - qc = QuantumCircuit(1) - qc.h(0) - qc.p(a, 0) - qc.h(0) - qc.measure_all() - gradient = grad(sampler) - param_list = [[np.pi / 4], [0], [np.pi / 2]] - expected = [ - [{0: -0.5 / np.sqrt(2), 1: 0.5 / np.sqrt(2)}], - [{0: 0, 1: 0}], - [{0: -0.499999, 1: 0.499999}], - ] - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [param]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=1) - array2 = _quasi2array(expected[i], num_qubits=1) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(*gradient_factories) - def test_gradient_u(self, grad): - """Test the sampler gradient for u""" - sampler = Sampler() - a = Parameter("a") - b = Parameter("b") - c = Parameter("c") - qc = QuantumCircuit(1) - qc.h(0) - qc.u(a, b, c, 0) - qc.h(0) - qc.measure_all() - gradient = grad(sampler) - param_list = [[np.pi / 4, 0, 0], [np.pi / 4, np.pi / 4, np.pi / 4]] - expected = [ - [{0: -0.5 / np.sqrt(2), 1: 0.5 / np.sqrt(2)}, {0: 0, 1: 0}, {0: 0, 1: 0}], - [{0: -0.176777, 1: 0.176777}, {0: -0.426777, 1: 0.426777}, {0: -0.426777, 1: 0.426777}], - ] - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [param]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=1) - array2 = _quasi2array(expected[i], num_qubits=1) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(*gradient_factories) - def test_gradient_efficient_su2(self, grad): - """Test the sampler gradient for EfficientSU2""" - sampler = Sampler() - qc = EfficientSU2(2, reps=1) - qc.measure_all() - gradient = grad(sampler) - param_list = [ - [np.pi / 4 for param in qc.parameters], - [np.pi / 2 for param in qc.parameters], - ] - expected = [ - [ - { - 0: -0.11963834764831836, - 1: -0.05713834764831845, - 2: -0.21875000000000003, - 3: 0.39552669529663675, - }, - { - 0: -0.32230339059327373, - 1: -0.031250000000000014, - 2: 0.2339150429449554, - 3: 0.11963834764831843, - }, - { - 0: 0.012944173824159189, - 1: -0.01294417382415923, - 2: 0.07544417382415919, - 3: -0.07544417382415919, - }, - { - 0: 0.2080266952966367, - 1: -0.03125000000000002, - 2: -0.11963834764831842, - 3: -0.057138347648318405, - }, - { - 0: -0.11963834764831838, - 1: 0.11963834764831838, - 2: -0.21875000000000003, - 3: 0.21875, - }, - { - 0: -0.2781092167691146, - 1: -0.0754441738241592, - 2: 0.27810921676911443, - 3: 0.07544417382415924, - }, - {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0}, - {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0}, - ], - [ - { - 0: -4.163336342344337e-17, - 1: 2.7755575615628914e-17, - 2: -4.163336342344337e-17, - 3: 0.0, - }, - {0: 0.0, 1: -1.3877787807814457e-17, 2: 4.163336342344337e-17, 3: 0.0}, - { - 0: -0.24999999999999994, - 1: 0.24999999999999994, - 2: 0.24999999999999994, - 3: -0.24999999999999994, - }, - { - 0: 0.24999999999999994, - 1: 0.24999999999999994, - 2: -0.24999999999999994, - 3: -0.24999999999999994, - }, - { - 0: -4.163336342344337e-17, - 1: 4.163336342344337e-17, - 2: -4.163336342344337e-17, - 3: 5.551115123125783e-17, - }, - { - 0: -0.24999999999999994, - 1: 0.24999999999999994, - 2: 0.24999999999999994, - 3: -0.24999999999999994, - }, - {0: 0.0, 1: 2.7755575615628914e-17, 2: 0.0, 3: 2.7755575615628914e-17}, - {0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0}, - ], - ] - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [param]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=2) - array2 = _quasi2array(expected[i], num_qubits=2) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(*gradient_factories) - def test_gradient_2qubit_gate(self, grad): - """Test the sampler gradient for 2 qubit gates""" - sampler = Sampler() - for gate in [RXXGate]: - param_list = [[np.pi / 4], [np.pi / 2]] - correct_results = [ - [{0: -0.5 / np.sqrt(2), 1: 0, 2: 0, 3: 0.5 / np.sqrt(2)}], - [{0: -0.5, 1: 0, 2: 0, 3: 0.5}], - ] - for i, param in enumerate(param_list): - a = Parameter("a") - qc = QuantumCircuit(2) - qc.append(gate(a), [qc.qubits[0], qc.qubits[1]], []) - qc.measure_all() - gradient = grad(sampler) - gradients = gradient.run([qc], [param]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=2) - array2 = _quasi2array(correct_results[i], num_qubits=2) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(*gradient_factories) - def test_gradient_parameter_coefficient(self, grad): - """Test the sampler gradient for parameter variables with coefficients""" - sampler = Sampler() - qc = RealAmplitudes(num_qubits=2, reps=1) - qc.rz(qc.parameters[0].exp() + 2 * qc.parameters[1], 0) - qc.rx(3.0 * qc.parameters[0] + qc.parameters[1].sin(), 1) - qc.u(qc.parameters[0], qc.parameters[1], qc.parameters[3], 1) - qc.p(2 * qc.parameters[0] + 1, 0) - qc.rxx(qc.parameters[0] + 2, 0, 1) - qc.measure_all() - gradient = grad(sampler) - param_list = [[np.pi / 4 for _ in qc.parameters], [np.pi / 2 for _ in qc.parameters]] - correct_results = [ - [ - { - 0: 0.30014831912265927, - 1: -0.6634809704357856, - 2: 0.343589357193753, - 3: 0.019743294119373426, - }, - { - 0: 0.16470607453981906, - 1: -0.40996282450610577, - 2: 0.08791803062881773, - 3: 0.15733871933746948, - }, - { - 0: 0.27036068339663866, - 1: -0.273790986018701, - 2: 0.12752010079553433, - 3: -0.12408979817347202, - }, - { - 0: -0.2098616294167757, - 1: -0.2515823946449894, - 2: 0.21929102305386305, - 3: 0.24215300100790207, - }, - ], - [ - { - 0: -1.844810060881004, - 1: 0.04620532700836027, - 2: 1.6367366426074323, - 3: 0.16186809126521057, - }, - { - 0: 0.07296073407769421, - 1: -0.021774869186331716, - 2: 0.02177486918633173, - 3: -0.07296073407769456, - }, - { - 0: -0.07794369186049102, - 1: -0.07794369186049122, - 2: 0.07794369186049117, - 3: 0.07794369186049112, - }, - { - 0: 0.0, - 1: 0.0, - 2: 0.0, - 3: 0.0, - }, - ], - ] - - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [param]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=2) - array2 = _quasi2array(correct_results[i], num_qubits=2) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(*gradient_factories) - def test_gradient_parameters(self, grad): - """Test the sampler gradient for parameters""" - sampler = Sampler() - a = Parameter("a") - b = Parameter("b") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.rz(b, 0) - qc.measure_all() - gradient = grad(sampler) - param_list = [[np.pi / 4, np.pi / 2]] - expected = [ - [{0: -0.5 / np.sqrt(2), 1: 0.5 / np.sqrt(2)}], - ] - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [param], parameters=[[a]]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=1) - array2 = _quasi2array(expected[i], num_qubits=1) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - # parameter order - with self.subTest(msg="The order of gradients"): - c = Parameter("c") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.rz(b, 0) - qc.rx(c, 0) - qc.measure_all() - param_values = [[np.pi / 4, np.pi / 2, np.pi / 3]] - params = [[a, b, c], [c, b, a], [a, c], [c, a]] - expected = [ - [ - {0: -0.17677666583387008, 1: 0.17677666583378482}, - {0: 0.3061861668168149, 1: -0.3061861668167012}, - {0: -0.3061861668168149, 1: 0.30618616681678645}, - ], - [ - {0: -0.3061861668168149, 1: 0.30618616681678645}, - {0: 0.3061861668168149, 1: -0.3061861668167012}, - {0: -0.17677666583387008, 1: 0.17677666583378482}, - ], - [ - {0: -0.17677666583387008, 1: 0.17677666583378482}, - {0: -0.3061861668168149, 1: 0.30618616681678645}, - ], - [ - {0: -0.3061861668168149, 1: 0.30618616681678645}, - {0: -0.17677666583387008, 1: 0.17677666583378482}, - ], - ] - for i, p in enumerate(params): - gradients = gradient.run([qc], param_values, parameters=[p]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=1) - array2 = _quasi2array(expected[i], num_qubits=1) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(*gradient_factories) - def test_gradient_multi_arguments(self, grad): - """Test the sampler gradient for multiple arguments""" - sampler = Sampler() - a = Parameter("a") - b = Parameter("b") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.measure_all() - qc2 = QuantumCircuit(1) - qc2.rx(b, 0) - qc2.measure_all() - gradient = grad(sampler) - param_list = [[np.pi / 4], [np.pi / 2]] - correct_results = [ - [{0: -0.5 / np.sqrt(2), 1: 0.5 / np.sqrt(2)}], - [{0: -0.499999, 1: 0.499999}], - ] - gradients = gradient.run([qc, qc2], param_list).result().gradients - for i, q_dists in enumerate(gradients): - array1 = _quasi2array(q_dists, num_qubits=1) - array2 = _quasi2array(correct_results[i], num_qubits=1) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - # parameters - with self.subTest(msg="Different parameters"): - c = Parameter("c") - qc3 = QuantumCircuit(1) - qc3.rx(c, 0) - qc3.ry(a, 0) - qc3.measure_all() - param_list2 = [[np.pi / 4], [np.pi / 4, np.pi / 4], [np.pi / 4, np.pi / 4]] - gradients = ( - gradient.run([qc, qc3, qc3], param_list2, parameters=[[a], [c], None]) - .result() - .gradients - ) - correct_results = [ - [{0: -0.5 / np.sqrt(2), 1: 0.5 / np.sqrt(2)}], - [{0: -0.25, 1: 0.25}], - [{0: -0.25, 1: 0.25}, {0: -0.25, 1: 0.25}], - ] - for i, q_dists in enumerate(gradients): - array1 = _quasi2array(q_dists, num_qubits=1) - array2 = _quasi2array(correct_results[i], num_qubits=1) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(*gradient_factories) - def test_gradient_validation(self, grad): - """Test sampler gradient's validation""" - sampler = Sampler() - a = Parameter("a") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.measure_all() - gradient = grad(sampler) - param_list = [[np.pi / 4], [np.pi / 2]] - with self.assertRaises(ValueError): - gradient.run([qc], param_list) - with self.assertRaises(ValueError): - gradient.run([qc, qc], param_list, parameters=[[a]]) - with self.assertRaises(ValueError): - gradient.run([qc], [[np.pi / 4, np.pi / 4]]) - - def test_spsa_gradient(self): - """Test the SPSA sampler gradient""" - sampler = Sampler() - with self.assertRaises(ValueError): - _ = SPSASamplerGradient(sampler, epsilon=-0.1) - - a = Parameter("a") - b = Parameter("b") - c = Parameter("c") - qc = QuantumCircuit(2) - qc.rx(b, 0) - qc.rx(a, 1) - qc.measure_all() - param_list = [[1, 2]] - correct_results = [ - [ - {0: 0.2273244, 1: -0.6480598, 2: 0.2273244, 3: 0.1934111}, - {0: -0.2273244, 1: 0.6480598, 2: -0.2273244, 3: -0.1934111}, - ], - ] - gradient = SPSASamplerGradient(sampler, epsilon=1e-6, seed=123) - for i, param in enumerate(param_list): - gradients = gradient.run([qc], [param]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=2) - array2 = _quasi2array(correct_results[i], num_qubits=2) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - # multi parameters - with self.subTest(msg="Multiple parameters"): - param_list2 = [[1, 2], [1, 2], [3, 4]] - correct_results2 = [ - [ - {0: 0.2273244, 1: -0.6480598, 2: 0.2273244, 3: 0.1934111}, - {0: -0.2273244, 1: 0.6480598, 2: -0.2273244, 3: -0.1934111}, - ], - [ - {0: -0.2273244, 1: 0.6480598, 2: -0.2273244, 3: -0.1934111}, - ], - [ - {0: -0.0141129, 1: -0.0564471, 2: -0.3642884, 3: 0.4348484}, - {0: 0.0141129, 1: 0.0564471, 2: 0.3642884, 3: -0.4348484}, - ], - ] - gradient = SPSASamplerGradient(sampler, epsilon=1e-6, seed=123) - gradients = ( - gradient.run([qc] * 3, param_list2, parameters=[None, [b], None]).result().gradients - ) - for i, result in enumerate(gradients): - array1 = _quasi2array(result, num_qubits=2) - array2 = _quasi2array(correct_results2[i], num_qubits=2) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - # batch size - with self.subTest(msg="Batch size"): - param_list = [[1, 1]] - gradient = SPSASamplerGradient(sampler, epsilon=1e-6, batch_size=4, seed=123) - gradients = gradient.run([qc], param_list).result().gradients - correct_results3 = [ - [ - { - 0: -0.1620149622932887, - 1: -0.25872053011771756, - 2: 0.3723827084675668, - 3: 0.04835278392088804, - }, - { - 0: -0.1620149622932887, - 1: 0.3723827084675668, - 2: -0.25872053011771756, - 3: 0.04835278392088804, - }, - ] - ] - for i, q_dists in enumerate(gradients): - array1 = _quasi2array(q_dists, num_qubits=2) - array2 = _quasi2array(correct_results3[i], num_qubits=2) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - # parameter order - with self.subTest(msg="The order of gradients"): - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.rz(b, 0) - qc.rx(c, 0) - qc.measure_all() - param_list = [[np.pi / 4, np.pi / 2, np.pi / 3]] - param = [[a, b, c], [c, b, a], [a, c], [c, a]] - correct_results = [ - [ - {0: -0.17677624757590138, 1: 0.17677624757590138}, - {0: 0.17677624757590138, 1: -0.17677624757590138}, - {0: 0.17677624757590138, 1: -0.17677624757590138}, - ], - [ - {0: 0.17677624757590138, 1: -0.17677624757590138}, - {0: 0.17677624757590138, 1: -0.17677624757590138}, - {0: -0.17677624757590138, 1: 0.17677624757590138}, - ], - [ - {0: -0.17677624757590138, 1: 0.17677624757590138}, - {0: 0.17677624757590138, 1: -0.17677624757590138}, - ], - [ - {0: 0.17677624757590138, 1: -0.17677624757590138}, - {0: -0.17677624757590138, 1: 0.17677624757590138}, - ], - ] - for i, p in enumerate(param): - gradient = SPSASamplerGradient(sampler, epsilon=1e-6, seed=123) - gradients = gradient.run([qc], param_list, parameters=[p]).result().gradients[0] - array1 = _quasi2array(gradients, num_qubits=1) - array2 = _quasi2array(correct_results[i], num_qubits=1) - np.testing.assert_allclose(array1, array2, atol=1e-3) - - @data(ParamShiftSamplerGradient, LinCombSamplerGradient) - def test_gradient_random_parameters(self, grad): - """Test param shift and lin comb w/ random parameters""" - rng = np.random.default_rng(123) - qc = RealAmplitudes(num_qubits=3, reps=1) - params = qc.parameters - qc.rx(3.0 * params[0] + params[1].sin(), 0) - qc.ry(params[0].exp() + 2 * params[1], 1) - qc.rz(params[0] * params[1] - params[2], 2) - qc.p(2 * params[0] + 1, 0) - qc.u(params[0].sin(), params[1] - 2, params[2] * params[3], 1) - qc.sx(2) - qc.rxx(params[0].sin(), 1, 2) - qc.ryy(params[1].cos(), 2, 0) - qc.rzz(params[2] * 2, 0, 1) - qc.crx(params[0].exp(), 1, 2) - qc.cry(params[1].arctan(), 2, 0) - qc.crz(params[2] * -2, 0, 1) - qc.dcx(0, 1) - qc.csdg(0, 1) - qc.ccx(0, 1, 2) - qc.iswap(0, 2) - qc.swap(1, 2) - qc.global_phase = params[0] * params[1] + params[2].cos().exp() - qc.measure_all() - - sampler = Sampler() - findiff = FiniteDiffSamplerGradient(sampler, 1e-6) - gradient = grad(sampler) - - num_qubits = qc.num_qubits - num_tries = 10 - param_values = rng.normal(0, 2, (num_tries, qc.num_parameters)).tolist() - result1 = findiff.run([qc] * num_tries, param_values).result().gradients - result2 = gradient.run([qc] * num_tries, param_values).result().gradients - self.assertEqual(len(result1), len(result2)) - for res1, res2 in zip(result1, result2): - array1 = _quasi2array(res1, num_qubits) - array2 = _quasi2array(res2, num_qubits) - np.testing.assert_allclose(array1, array2, rtol=1e-4) - - @data( - FiniteDiffSamplerGradient, - ParamShiftSamplerGradient, - LinCombSamplerGradient, - SPSASamplerGradient, - ) - def test_options(self, grad): - """Test sampler gradient's run options""" - a = Parameter("a") - qc = QuantumCircuit(1) - qc.rx(a, 0) - qc.measure_all() - sampler = Sampler(options={"shots": 100}) - with self.subTest("sampler"): - if grad is FiniteDiffSamplerGradient or grad is SPSASamplerGradient: - gradient = grad(sampler, epsilon=1e-6) - else: - gradient = grad(sampler) - options = gradient.options - result = gradient.run([qc], [[1]]).result() - self.assertEqual(result.options.get("shots"), 100) - self.assertEqual(options.get("shots"), 100) - - with self.subTest("gradient init"): - if grad is FiniteDiffSamplerGradient or grad is SPSASamplerGradient: - gradient = grad(sampler, epsilon=1e-6, options={"shots": 200}) - else: - gradient = grad(sampler, options={"shots": 200}) - options = gradient.options - result = gradient.run([qc], [[1]]).result() - self.assertEqual(result.options.get("shots"), 200) - self.assertEqual(options.get("shots"), 200) - - with self.subTest("gradient update"): - if grad is FiniteDiffSamplerGradient or grad is SPSASamplerGradient: - gradient = grad(sampler, epsilon=1e-6, options={"shots": 200}) - else: - gradient = grad(sampler, options={"shots": 200}) - gradient.update_default_options(shots=100) - options = gradient.options - result = gradient.run([qc], [[1]]).result() - self.assertEqual(result.options.get("shots"), 100) - self.assertEqual(options.get("shots"), 100) - - with self.subTest("gradient run"): - if grad is FiniteDiffSamplerGradient or grad is SPSASamplerGradient: - gradient = grad(sampler, epsilon=1e-6, options={"shots": 200}) - else: - gradient = grad(sampler, options={"shots": 200}) - options = gradient.options - result = gradient.run([qc], [[1]], shots=300).result() - self.assertEqual(result.options.get("shots"), 300) - # Only default + sampler options. Not run. - self.assertEqual(options.get("shots"), 200) - - @data( - FiniteDiffSamplerGradient, - ParamShiftSamplerGradient, - LinCombSamplerGradient, - SPSASamplerGradient, - ) - def test_operations_preserved(self, gradient_cls): - """Test non-parameterized instructions are preserved and not unrolled.""" - x = Parameter("x") - circuit = QuantumCircuit(2) - circuit.initialize(np.array([1, 1, 0, 0]) / np.sqrt(2)) # this should remain as initialize - circuit.crx(x, 0, 1) # this should get unrolled - circuit.measure_all() - - values = [np.pi / 2] - expect = [{0: 0, 1: -0.25, 2: 0, 3: 0.25}] - - ops = [] - - def operations_callback(op): - ops.append(op) - - sampler = LoggingSampler(operations_callback=operations_callback) - - if gradient_cls in [SPSASamplerGradient, FiniteDiffSamplerGradient]: - gradient = gradient_cls(sampler, epsilon=0.01) - else: - gradient = gradient_cls(sampler) - - job = gradient.run([circuit], [values]) - result = job.result() - - with self.subTest(msg="assert initialize is preserved"): - self.assertTrue(all("initialize" in ops_i[0].keys() for ops_i in ops)) - - with self.subTest(msg="assert result is correct"): - array1 = _quasi2array(result.gradients[0], num_qubits=2) - array2 = _quasi2array(expect, num_qubits=2) - np.testing.assert_allclose(array1, array2, atol=1e-5) - - -def _quasi2array(quasis: List[QuasiDistribution], num_qubits: int) -> np.ndarray: - ret = np.zeros((len(quasis), 2**num_qubits)) - for i, quasi in enumerate(quasis): - ret[i, list(quasi.keys())] = list(quasi.values()) - return ret - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/minimum_eigensolvers/__init__.py b/test/python/algorithms/minimum_eigensolvers/__init__.py deleted file mode 100644 index fdb172d367f0..000000000000 --- a/test/python/algorithms/minimum_eigensolvers/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/minimum_eigensolvers/test_adapt_vqe.py b/test/python/algorithms/minimum_eigensolvers/test_adapt_vqe.py deleted file mode 100644 index 9509abf422f3..000000000000 --- a/test/python/algorithms/minimum_eigensolvers/test_adapt_vqe.py +++ /dev/null @@ -1,245 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test of the AdaptVQE minimum eigensolver""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -from ddt import ddt, data, unpack -import numpy as np - -from qiskit.algorithms.minimum_eigensolvers import VQE -from qiskit.algorithms.minimum_eigensolvers.adapt_vqe import AdaptVQE, TerminationCriterion -from qiskit.algorithms.optimizers import SLSQP -from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.circuit.library import EvolvedOperatorAnsatz -from qiskit.opflow import PauliSumOp -from qiskit.primitives import Estimator -from qiskit.quantum_info import SparsePauliOp -from qiskit.utils import algorithm_globals - - -@ddt -class TestAdaptVQE(QiskitAlgorithmsTestCase): - """Test of the AdaptVQE minimum eigensolver""" - - def setUp(self): - super().setUp() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 42 - - with self.assertWarns(DeprecationWarning): - self.h2_op = PauliSumOp.from_list( - [ - ("IIII", -0.8105479805373266), - ("ZZII", -0.2257534922240251), - ("IIZI", +0.12091263261776641), - ("ZIZI", +0.12091263261776641), - ("IZZI", +0.17218393261915543), - ("IIIZ", +0.17218393261915546), - ("IZIZ", +0.1661454325638243), - ("ZZIZ", +0.1661454325638243), - ("IIZZ", -0.2257534922240251), - ("IZZZ", +0.16892753870087926), - ("ZZZZ", +0.17464343068300464), - ("IXIX", +0.04523279994605788), - ("ZXIX", +0.04523279994605788), - ("IXZX", -0.04523279994605788), - ("ZXZX", -0.04523279994605788), - ] - ) - self.excitation_pool = [ - PauliSumOp( - SparsePauliOp(["IIIY", "IIZY"], coeffs=[0.5 + 0.0j, -0.5 + 0.0j]), coeff=1.0 - ), - PauliSumOp( - SparsePauliOp(["ZYII", "IYZI"], coeffs=[-0.5 + 0.0j, 0.5 + 0.0j]), coeff=1.0 - ), - PauliSumOp( - SparsePauliOp( - ["ZXZY", "IXIY", "IYIX", "ZYZX", "IYZX", "ZYIX", "ZXIY", "IXZY"], - coeffs=[ - -0.125 + 0.0j, - 0.125 + 0.0j, - -0.125 + 0.0j, - 0.125 + 0.0j, - 0.125 + 0.0j, - -0.125 + 0.0j, - 0.125 + 0.0j, - -0.125 + 0.0j, - ], - ), - coeff=1.0, - ), - ] - self.initial_state = QuantumCircuit(QuantumRegister(4)) - self.initial_state.x(0) - self.initial_state.x(1) - self.ansatz = EvolvedOperatorAnsatz( - self.excitation_pool, initial_state=self.initial_state - ) - self.optimizer = SLSQP() - - def test_default(self): - """Default execution""" - calc = AdaptVQE(VQE(Estimator(), self.ansatz, self.optimizer)) - - with self.assertWarns(DeprecationWarning): - res = calc.compute_minimum_eigenvalue(operator=self.h2_op) - - expected_eigenvalue = -1.85727503 - - self.assertAlmostEqual(res.eigenvalue, expected_eigenvalue, places=6) - np.testing.assert_allclose(res.eigenvalue_history, [expected_eigenvalue], rtol=1e-6) - - def test_with_quantum_info(self): - """Test behavior with quantum_info-based operators.""" - ansatz = EvolvedOperatorAnsatz( - [op.primitive for op in self.excitation_pool], - initial_state=self.initial_state, - ) - - calc = AdaptVQE(VQE(Estimator(), ansatz, self.optimizer)) - res = calc.compute_minimum_eigenvalue(operator=self.h2_op.primitive) - - expected_eigenvalue = -1.85727503 - - self.assertAlmostEqual(res.eigenvalue, expected_eigenvalue, places=6) - np.testing.assert_allclose(res.eigenvalue_history, [expected_eigenvalue], rtol=1e-6) - - def test_converged(self): - """Test to check termination criteria""" - calc = AdaptVQE( - VQE(Estimator(), self.ansatz, self.optimizer), - gradient_threshold=1e-3, - ) - with self.assertWarns(DeprecationWarning): - res = calc.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertEqual(res.termination_criterion, TerminationCriterion.CONVERGED) - - def test_maximum(self): - """Test to check termination criteria""" - calc = AdaptVQE( - VQE(Estimator(), self.ansatz, self.optimizer), - max_iterations=1, - ) - with self.assertWarns(DeprecationWarning): - res = calc.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertEqual(res.termination_criterion, TerminationCriterion.MAXIMUM) - - def test_eigenvalue_threshold(self): - """Test for the eigenvalue_threshold attribute.""" - operator = SparsePauliOp.from_list( - [ - ("XX", 1.0), - ("ZX", -0.5), - ("XZ", -0.5), - ] - ) - ansatz = EvolvedOperatorAnsatz( - [ - SparsePauliOp.from_list([("YZ", 0.4)]), - SparsePauliOp.from_list([("ZY", 0.5)]), - ], - initial_state=QuantumCircuit(2), - ) - - calc = AdaptVQE( - VQE(Estimator(), ansatz, self.optimizer), - eigenvalue_threshold=1, - ) - res = calc.compute_minimum_eigenvalue(operator) - - self.assertEqual(res.termination_criterion, TerminationCriterion.CONVERGED) - - def test_threshold_attribute(self): - """Test the (pending deprecated) threshold attribute""" - with self.assertWarns(PendingDeprecationWarning): - calc = AdaptVQE( - VQE(Estimator(), self.ansatz, self.optimizer), - threshold=1e-3, - ) - with self.assertWarns(DeprecationWarning): - res = calc.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertEqual(res.termination_criterion, TerminationCriterion.CONVERGED) - - @data( - ([1, 1], True), - ([1, 11], False), - ([11, 1], False), - ([1, 12], False), - ([12, 2], False), - ([1, 1, 1], True), - ([1, 2, 1], False), - ([1, 2, 2], True), - ([1, 2, 21], False), - ([1, 12, 2], False), - ([11, 1, 2], False), - ([1, 2, 1, 1], True), - ([1, 2, 1, 2], True), - ([1, 2, 1, 21], False), - ([11, 2, 1, 2], False), - ([1, 11, 1, 111], False), - ([11, 1, 111, 1], False), - ([1, 2, 3, 1, 2, 3], True), - ([1, 2, 3, 4, 1, 2, 3], False), - ([11, 2, 3, 1, 2, 3], False), - ([1, 2, 3, 1, 2, 31], False), - ([1, 2, 3, 4, 1, 2, 3, 4], True), - ([11, 2, 3, 4, 1, 2, 3, 4], False), - ([1, 2, 3, 4, 1, 2, 3, 41], False), - ([1, 2, 3, 4, 5, 1, 2, 3, 4], False), - ) - @unpack - def test_cyclicity(self, seq, is_cycle): - """Test AdaptVQE index cycle detection""" - self.assertEqual(is_cycle, AdaptVQE._check_cyclicity(seq)) - - def test_vqe_solver(self): - """Test to check if the VQE solver remains the same or not""" - solver = VQE(Estimator(), self.ansatz, self.optimizer) - calc = AdaptVQE(solver) - with self.assertWarns(DeprecationWarning): - _ = calc.compute_minimum_eigenvalue(operator=self.h2_op) - self.assertEqual(solver.ansatz, calc.solver.ansatz) - - def test_gradient_calculation(self): - """Test to check if the gradient calculation""" - solver = VQE(Estimator(), QuantumCircuit(1), self.optimizer) - calc = AdaptVQE(solver) - calc._excitation_pool = [SparsePauliOp("X")] - res = calc._compute_gradients(operator=SparsePauliOp("Y"), theta=[]) - # compare with manually computed reference value - self.assertAlmostEqual(res[0][0], 2.0) - - def test_supports_aux_operators(self): - """Test that auxiliary operators are supported""" - calc = AdaptVQE(VQE(Estimator(), self.ansatz, self.optimizer)) - with self.assertWarns(DeprecationWarning): - res = calc.compute_minimum_eigenvalue(operator=self.h2_op, aux_operators=[self.h2_op]) - - expected_eigenvalue = -1.85727503 - - self.assertAlmostEqual(res.eigenvalue, expected_eigenvalue, places=6) - self.assertAlmostEqual(res.aux_operators_evaluated[0][0], expected_eigenvalue, places=6) - np.testing.assert_allclose(res.eigenvalue_history, [expected_eigenvalue], rtol=1e-6) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/minimum_eigensolvers/test_numpy_minimum_eigensolver.py b/test/python/algorithms/minimum_eigensolvers/test_numpy_minimum_eigensolver.py deleted file mode 100644 index f5724f77c152..000000000000 --- a/test/python/algorithms/minimum_eigensolvers/test_numpy_minimum_eigensolver.py +++ /dev/null @@ -1,240 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test NumPy minimum eigensolver""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from ddt import ddt, data - -from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info import Operator, SparsePauliOp - -H2_SPARSE_PAULI = SparsePauliOp( - ["II", "ZI", "IZ", "ZZ", "XX"], - coeffs=[ - -1.052373245772859, - 0.39793742484318045, - -0.39793742484318045, - -0.01128010425623538, - 0.18093119978423156, - ], -) - -H2_OP = Operator(H2_SPARSE_PAULI.to_matrix()) - -H2_PAULI = PauliSumOp(H2_SPARSE_PAULI) - - -@ddt -class TestNumPyMinimumEigensolver(QiskitAlgorithmsTestCase): - """Test NumPy minimum eigensolver""" - - def setUp(self): - super().setUp() - aux_op1 = Operator(SparsePauliOp(["II"], coeffs=[2.0]).to_matrix()) - aux_op2 = SparsePauliOp(["II", "ZZ", "YY", "XX"], coeffs=[0.5, 0.5, 0.5, -0.5]) - self.aux_ops_list = [aux_op1, aux_op2] - self.aux_ops_dict = {"aux_op1": aux_op1, "aux_op2": aux_op2} - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_cme(self, op): - """Basic test""" - algo = NumPyMinimumEigensolver() - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=self.aux_ops_list) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 2) - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_cme_reuse(self, op): - """Test reuse""" - algo = NumPyMinimumEigensolver() - - with self.subTest("Test with no operator or aux_operators, give via compute method"): - result = algo.compute_minimum_eigenvalue(operator=op) - self.assertEqual(result.eigenvalue.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalue, -1.85727503) - self.assertIsNone(result.aux_operators_evaluated) - - with self.subTest("Test with added aux_operators"): - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=self.aux_ops_list) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 2) - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0) - - with self.subTest("Test with aux_operators removed"): - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=[]) - self.assertEqual(result.eigenvalue.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalue, -1.85727503) - self.assertIsNone(result.aux_operators_evaluated) - - with self.subTest("Test with aux_operators set again"): - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=self.aux_ops_list) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 2) - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0) - - with self.subTest("Test after setting first aux_operators as main operator"): - result = algo.compute_minimum_eigenvalue( - operator=self.aux_ops_list[0], aux_operators=[] - ) - self.assertAlmostEqual(result.eigenvalue, 2 + 0j) - self.assertIsNone(result.aux_operators_evaluated) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_cme_filter(self, op): - """Basic test""" - - # define filter criterion - # pylint: disable=unused-argument - def criterion(x, v, a_v): - return v >= -0.5 - - algo = NumPyMinimumEigensolver(filter_criterion=criterion) - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=self.aux_ops_list) - self.assertAlmostEqual(result.eigenvalue, -0.22491125 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 2) - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_cme_filter_empty(self, op): - """Test with filter always returning False""" - - # define filter criterion - # pylint: disable=unused-argument - def criterion(x, v, a_v): - return False - - algo = NumPyMinimumEigensolver(filter_criterion=criterion) - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=self.aux_ops_list) - self.assertEqual(result.eigenvalue, None) - self.assertEqual(result.eigenstate, None) - self.assertEqual(result.aux_operators_evaluated, None) - - @data("X", "Y", "Z") - def test_cme_1q(self, op): - """Test for 1 qubit operator""" - algo = NumPyMinimumEigensolver() - operator = SparsePauliOp([op], coeffs=1.0) - result = algo.compute_minimum_eigenvalue(operator=operator) - self.assertAlmostEqual(result.eigenvalue, -1) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_cme_aux_ops_dict(self, op): - """Test dictionary compatibility of aux_operators""" - # Start with an empty dictionary - algo = NumPyMinimumEigensolver() - - with self.subTest("Test with an empty dictionary."): - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators={}) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertIsNone(result.aux_operators_evaluated) - - with self.subTest("Test with two auxiliary operators."): - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=self.aux_ops_dict) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 2) - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][0], 2) - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][0], 0) - - with self.subTest("Test with additional zero and None operators."): - extra_ops = {"None_op": None, "zero_op": 0, **self.aux_ops_dict} - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=extra_ops) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 3) - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][0], 2) - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][0], 0) - self.assertEqual(result.aux_operators_evaluated["zero_op"], (0.0, {"variance": 0})) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_aux_operators_list(self, op): - """Test list-based aux_operators.""" - algo = NumPyMinimumEigensolver() - - with self.subTest("Test with two auxiliary operators."): - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=self.aux_ops_list) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operators_evaluated[0][1].pop("variance"), 0.0) - self.assertAlmostEqual(result.aux_operators_evaluated[1][1].pop("variance"), 0.0) - - with self.subTest("Test with additional zero and None operators."): - extra_ops = [*self.aux_ops_list, None, 0] - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=extra_ops) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 4) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0, places=6) - self.assertIsNone(result.aux_operators_evaluated[2], None) - self.assertEqual(result.aux_operators_evaluated[3][0], 0.0) - # standard deviations - self.assertAlmostEqual(result.aux_operators_evaluated[0][1].pop("variance"), 0.0) - self.assertAlmostEqual(result.aux_operators_evaluated[1][1].pop("variance"), 0.0) - self.assertEqual(result.aux_operators_evaluated[3][1].pop("variance"), 0.0) - - @data(H2_SPARSE_PAULI, H2_PAULI, H2_OP) - def test_aux_operators_dict(self, op): - """Test dict-based aux_operators.""" - algo = NumPyMinimumEigensolver() - - with self.subTest("Test with two auxiliary operators."): - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=self.aux_ops_dict) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][0], 0, places=6) - # standard deviations - self.assertAlmostEqual( - result.aux_operators_evaluated["aux_op1"][1].pop("variance"), 0.0 - ) - self.assertAlmostEqual( - result.aux_operators_evaluated["aux_op2"][1].pop("variance"), 0.0 - ) - - with self.subTest("Test with additional zero and None operators."): - extra_ops = {**self.aux_ops_dict, "None_operator": None, "zero_operator": 0} - result = algo.compute_minimum_eigenvalue(operator=op, aux_operators=extra_ops) - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operators_evaluated), 3) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][0], 0, places=6) - self.assertEqual(result.aux_operators_evaluated["zero_operator"][0], 0.0) - self.assertTrue("None_operator" not in result.aux_operators_evaluated.keys()) - # standard deviations - self.assertAlmostEqual( - result.aux_operators_evaluated["aux_op1"][1].pop("variance"), 0.0 - ) - self.assertAlmostEqual( - result.aux_operators_evaluated["aux_op2"][1].pop("variance"), 0.0 - ) - self.assertAlmostEqual( - result.aux_operators_evaluated["zero_operator"][1].pop("variance"), 0.0 - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/minimum_eigensolvers/test_qaoa.py b/test/python/algorithms/minimum_eigensolvers/test_qaoa.py deleted file mode 100644 index c269f2aa0769..000000000000 --- a/test/python/algorithms/minimum_eigensolvers/test_qaoa.py +++ /dev/null @@ -1,304 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test the QAOA algorithm.""" - -import unittest -import warnings - -from functools import partial -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -import rustworkx as rx -from ddt import ddt, idata, unpack -from scipy.optimize import minimize as scipy_minimize - -from qiskit import QuantumCircuit -from qiskit.algorithms.minimum_eigensolvers import QAOA -from qiskit.algorithms.optimizers import COBYLA, NELDER_MEAD -from qiskit.circuit import Parameter -from qiskit.primitives import Sampler -from qiskit.quantum_info import Pauli, SparsePauliOp -from qiskit.result import QuasiDistribution -from qiskit.utils import algorithm_globals - - -W1 = np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]) -P1 = 1 -M1 = SparsePauliOp.from_list( - [ - ("IIIX", 1), - ("IIXI", 1), - ("IXII", 1), - ("XIII", 1), - ] -) -S1 = {"0101", "1010"} - - -W2 = np.array( - [ - [0.0, 8.0, -9.0, 0.0], - [8.0, 0.0, 7.0, 9.0], - [-9.0, 7.0, 0.0, -8.0], - [0.0, 9.0, -8.0, 0.0], - ] -) -P2 = 1 -M2 = None -S2 = {"1011", "0100"} - -CUSTOM_SUPERPOSITION = [1 / np.sqrt(15)] * 15 + [0] - - -@ddt -class TestQAOA(QiskitAlgorithmsTestCase): - """Test QAOA with MaxCut.""" - - def setUp(self): - super().setUp() - self.seed = 10598 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - self.sampler = Sampler() - - @idata( - [ - [W1, P1, M1, S1], - [W2, P2, M2, S2], - ] - ) - @unpack - def test_qaoa(self, w, reps, mixer, solutions): - """QAOA test""" - self.log.debug("Testing %s-step QAOA with MaxCut on graph\n%s", reps, w) - - qubit_op, _ = self._get_operator(w) - - qaoa = QAOA(self.sampler, COBYLA(), reps=reps, mixer=mixer) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, solutions) - - @idata( - [ - [W1, P1, S1], - [W2, P2, S2], - ] - ) - @unpack - def test_qaoa_qc_mixer(self, w, prob, solutions): - """QAOA test with a mixer as a parameterized circuit""" - self.log.debug( - "Testing %s-step QAOA with MaxCut on graph with a mixer as a parameterized circuit\n%s", - prob, - w, - ) - - optimizer = COBYLA() - qubit_op, _ = self._get_operator(w) - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - theta = Parameter("θ") - mixer.rx(theta, range(num_qubits)) - - qaoa = QAOA(self.sampler, optimizer, reps=prob, mixer=mixer) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, solutions) - - def test_qaoa_qc_mixer_many_parameters(self): - """QAOA test with a mixer as a parameterized circuit with the num of parameters > 1.""" - optimizer = COBYLA() - qubit_op, _ = self._get_operator(W1) - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - for i in range(num_qubits): - theta = Parameter("θ" + str(i)) - mixer.rx(theta, range(num_qubits)) - - qaoa = QAOA(self.sampler, optimizer, reps=2, mixer=mixer) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - self.log.debug(x) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, S1) - - def test_qaoa_qc_mixer_no_parameters(self): - """QAOA test with a mixer as a parameterized circuit with zero parameters.""" - qubit_op, _ = self._get_operator(W1) - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - # just arbitrary circuit - mixer.rx(np.pi / 2, range(num_qubits)) - - qaoa = QAOA(self.sampler, COBYLA(), reps=1, mixer=mixer) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - # we just assert that we get a result, it is not meaningful. - self.assertIsNotNone(result.eigenstate) - - def test_change_operator_size(self): - """QAOA change operator size test""" - qubit_op, _ = self._get_operator( - np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]) - ) - qaoa = QAOA(self.sampler, COBYLA(), reps=1) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - with self.subTest(msg="QAOA 4x4"): - self.assertIn(graph_solution, {"0101", "1010"}) - - qubit_op, _ = self._get_operator( - np.array( - [ - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - ] - ) - ) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - with self.subTest(msg="QAOA 6x6"): - self.assertIn(graph_solution, {"010101", "101010"}) - - @idata([[W2, S2, None], [W2, S2, [0.0, 0.0]], [W2, S2, [1.0, 0.8]]]) - @unpack - def test_qaoa_initial_point(self, w, solutions, init_pt): - """Check first parameter value used is initial point as expected""" - qubit_op, _ = self._get_operator(w) - - first_pt = [] - - def cb_callback(eval_count, parameters, mean, metadata): - nonlocal first_pt - if eval_count == 1: - first_pt = list(parameters) - - qaoa = QAOA( - self.sampler, - COBYLA(), - initial_point=init_pt, - callback=cb_callback, - ) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - - with self.subTest("Initial Point"): - # If None the preferred random initial point of QAOA variational form - if init_pt is None: - self.assertLess(result.eigenvalue, -0.97) - else: - self.assertListEqual(init_pt, first_pt) - - with self.subTest("Solution"): - self.assertIn(graph_solution, solutions) - - def test_qaoa_random_initial_point(self): - """QAOA random initial point""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - w = rx.adjacency_matrix( - rx.undirected_gnp_random_graph(5, 0.5, seed=algorithm_globals.random_seed) - ) - qubit_op, _ = self._get_operator(w) - qaoa = QAOA(self.sampler, NELDER_MEAD(disp=True), reps=2) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - self.assertLess(result.eigenvalue, -0.97) - - def test_optimizer_scipy_callable(self): - """Test passing a SciPy optimizer directly as callable.""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - w = rx.adjacency_matrix( - rx.undirected_gnp_random_graph(5, 0.5, seed=algorithm_globals.random_seed) - ) - qubit_op, _ = self._get_operator(w) - qaoa = QAOA( - self.sampler, - partial(scipy_minimize, method="Nelder-Mead", options={"maxiter": 2}), - ) - result = qaoa.compute_minimum_eigenvalue(qubit_op) - self.assertEqual(result.cost_function_evals, 5) - - def _get_operator(self, weight_matrix): - """Generate Hamiltonian for the max-cut problem of a graph. - - Args: - weight_matrix (numpy.ndarray) : adjacency matrix. - - Returns: - PauliSumOp: operator for the Hamiltonian - float: a constant shift for the obj function. - - """ - num_nodes = weight_matrix.shape[0] - pauli_list = [] - shift = 0 - for i in range(num_nodes): - for j in range(i): - if weight_matrix[i, j] != 0: - x_p = np.zeros(num_nodes, dtype=bool) - z_p = np.zeros(num_nodes, dtype=bool) - z_p[i] = True - z_p[j] = True - pauli_list.append([0.5 * weight_matrix[i, j], Pauli((z_p, x_p))]) - shift -= 0.5 * weight_matrix[i, j] - lst = [(pauli[1].to_label(), pauli[0]) for pauli in pauli_list] - return SparsePauliOp.from_list(lst), shift - - def _get_graph_solution(self, x: np.ndarray) -> str: - """Get graph solution from binary string. - - Args: - x : binary string as numpy array. - - Returns: - a graph solution as string. - """ - - return "".join([str(int(i)) for i in 1 - x]) - - def _sample_most_likely(self, state_vector: QuasiDistribution) -> np.ndarray: - """Compute the most likely binary string from state vector. - Args: - state_vector: Quasi-distribution. - - Returns: - Binary string as numpy.ndarray of ints. - """ - values = list(state_vector.values()) - n = int(np.log2(len(values))) - k = np.argmax(np.abs(values)) - x = np.zeros(n) - for i in range(n): - x[i] = k % 2 - k >>= 1 - return x - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/minimum_eigensolvers/test_qaoa_opflow.py b/test/python/algorithms/minimum_eigensolvers/test_qaoa_opflow.py deleted file mode 100644 index 18527c85046e..000000000000 --- a/test/python/algorithms/minimum_eigensolvers/test_qaoa_opflow.py +++ /dev/null @@ -1,312 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test the QAOA algorithm with opflow.""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -from functools import partial -import numpy as np - -from scipy.optimize import minimize as scipy_minimize -from ddt import ddt, idata, unpack - -import rustworkx as rx - -from qiskit import QuantumCircuit -from qiskit.algorithms.minimum_eigensolvers import QAOA -from qiskit.algorithms.optimizers import COBYLA, NELDER_MEAD -from qiskit.circuit import Parameter -from qiskit.opflow import PauliSumOp -from qiskit.quantum_info import Pauli -from qiskit.result import QuasiDistribution -from qiskit.primitives import Sampler -from qiskit.utils import algorithm_globals - -I = PauliSumOp.from_list([("I", 1)]) -X = PauliSumOp.from_list([("X", 1)]) - -W1 = np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]) -P1 = 1 -M1 = (I ^ I ^ I ^ X) + (I ^ I ^ X ^ I) + (I ^ X ^ I ^ I) + (X ^ I ^ I ^ I) -S1 = {"0101", "1010"} - - -W2 = np.array( - [ - [0.0, 8.0, -9.0, 0.0], - [8.0, 0.0, 7.0, 9.0], - [-9.0, 7.0, 0.0, -8.0], - [0.0, 9.0, -8.0, 0.0], - ] -) -P2 = 1 -M2 = None -S2 = {"1011", "0100"} - -CUSTOM_SUPERPOSITION = [1 / np.sqrt(15)] * 15 + [0] - - -@ddt -class TestQAOA(QiskitAlgorithmsTestCase): - """Test QAOA with MaxCut.""" - - def setUp(self): - super().setUp() - self.seed = 10598 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - self.sampler = Sampler() - - @idata( - [ - [W1, P1, M1, S1], - [W2, P2, M2, S2], - ] - ) - @unpack - def test_qaoa(self, w, reps, mixer, solutions): - """QAOA test""" - self.log.debug("Testing %s-step QAOA with MaxCut on graph\n%s", reps, w) - - qubit_op, _ = self._get_operator(w) - - qaoa = QAOA(self.sampler, COBYLA(), reps=reps, mixer=mixer) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, solutions) - - @idata( - [ - [W1, P1, S1], - [W2, P2, S2], - ] - ) - @unpack - def test_qaoa_qc_mixer(self, w, prob, solutions): - """QAOA test with a mixer as a parameterized circuit""" - self.log.debug( - "Testing %s-step QAOA with MaxCut on graph with a mixer as a parameterized circuit\n%s", - prob, - w, - ) - - optimizer = COBYLA() - qubit_op, _ = self._get_operator(w) - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - theta = Parameter("θ") - mixer.rx(theta, range(num_qubits)) - - qaoa = QAOA(self.sampler, optimizer, reps=prob, mixer=mixer) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, solutions) - - def test_qaoa_qc_mixer_many_parameters(self): - """QAOA test with a mixer as a parameterized circuit with the num of parameters > 1.""" - optimizer = COBYLA() - qubit_op, _ = self._get_operator(W1) - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - for i in range(num_qubits): - theta = Parameter("θ" + str(i)) - mixer.rx(theta, range(num_qubits)) - - qaoa = QAOA(self.sampler, optimizer, reps=2, mixer=mixer) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - self.log.debug(x) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, S1) - - def test_qaoa_qc_mixer_no_parameters(self): - """QAOA test with a mixer as a parameterized circuit with zero parameters.""" - qubit_op, _ = self._get_operator(W1) - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - # just arbitrary circuit - mixer.rx(np.pi / 2, range(num_qubits)) - - qaoa = QAOA(self.sampler, COBYLA(), reps=1, mixer=mixer) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - # we just assert that we get a result, it is not meaningful. - self.assertIsNotNone(result.eigenstate) - - def test_change_operator_size(self): - """QAOA change operator size test""" - qubit_op, _ = self._get_operator( - np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]) - ) - qaoa = QAOA(self.sampler, COBYLA(), reps=1) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - with self.subTest(msg="QAOA 4x4"): - self.assertIn(graph_solution, {"0101", "1010"}) - - qubit_op, _ = self._get_operator( - np.array( - [ - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - ] - ) - ) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - with self.subTest(msg="QAOA 6x6"): - self.assertIn(graph_solution, {"010101", "101010"}) - - @idata([[W2, S2, None], [W2, S2, [0.0, 0.0]], [W2, S2, [1.0, 0.8]]]) - @unpack - def test_qaoa_initial_point(self, w, solutions, init_pt): - """Check first parameter value used is initial point as expected""" - qubit_op, _ = self._get_operator(w) - - first_pt = [] - - def cb_callback(eval_count, parameters, mean, metadata): - nonlocal first_pt - if eval_count == 1: - first_pt = list(parameters) - - qaoa = QAOA( - self.sampler, - COBYLA(), - initial_point=init_pt, - callback=cb_callback, - ) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - - with self.subTest("Initial Point"): - # If None the preferred random initial point of QAOA variational form - if init_pt is None: - self.assertLess(result.eigenvalue, -0.97) - else: - self.assertListEqual(init_pt, first_pt) - - with self.subTest("Solution"): - self.assertIn(graph_solution, solutions) - - def test_qaoa_random_initial_point(self): - """QAOA random initial point""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - w = rx.adjacency_matrix( - rx.undirected_gnp_random_graph(5, 0.5, seed=algorithm_globals.random_seed) - ) - qubit_op, _ = self._get_operator(w) - qaoa = QAOA(self.sampler, NELDER_MEAD(disp=True), reps=2) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - self.assertLess(result.eigenvalue, -0.97) - - def test_optimizer_scipy_callable(self): - """Test passing a SciPy optimizer directly as callable.""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - w = rx.adjacency_matrix( - rx.undirected_gnp_random_graph(5, 0.5, seed=algorithm_globals.random_seed) - ) - qubit_op, _ = self._get_operator(w) - qaoa = QAOA( - self.sampler, - partial(scipy_minimize, method="Nelder-Mead", options={"maxiter": 2}), - ) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(qubit_op) - self.assertEqual(result.cost_function_evals, 5) - - def _get_operator(self, weight_matrix): - """Generate Hamiltonian for the max-cut problem of a graph. - - Args: - weight_matrix (numpy.ndarray) : adjacency matrix. - - Returns: - PauliSumOp: operator for the Hamiltonian - float: a constant shift for the obj function. - - """ - num_nodes = weight_matrix.shape[0] - pauli_list = [] - shift = 0 - for i in range(num_nodes): - for j in range(i): - if weight_matrix[i, j] != 0: - x_p = np.zeros(num_nodes, dtype=bool) - z_p = np.zeros(num_nodes, dtype=bool) - z_p[i] = True - z_p[j] = True - pauli_list.append([0.5 * weight_matrix[i, j], Pauli((z_p, x_p))]) - shift -= 0.5 * weight_matrix[i, j] - opflow_list = [(pauli[1].to_label(), pauli[0]) for pauli in pauli_list] - with self.assertWarns(DeprecationWarning): - return PauliSumOp.from_list(opflow_list), shift - - def _get_graph_solution(self, x: np.ndarray) -> str: - """Get graph solution from binary string. - - Args: - x : binary string as numpy array. - - Returns: - a graph solution as string. - """ - - return "".join([str(int(i)) for i in 1 - x]) - - def _sample_most_likely(self, state_vector: QuasiDistribution) -> np.ndarray: - """Compute the most likely binary string from state vector. - Args: - state_vector: Quasi-distribution. - - Returns: - Binary string as numpy.ndarray of ints. - """ - values = list(state_vector.values()) - n = int(np.log2(len(values))) - k = np.argmax(np.abs(values)) - x = np.zeros(n) - for i in range(n): - x[i] = k % 2 - k >>= 1 - return x - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/minimum_eigensolvers/test_sampling_vqe.py b/test/python/algorithms/minimum_eigensolvers/test_sampling_vqe.py deleted file mode 100644 index 4a7eb1929ef9..000000000000 --- a/test/python/algorithms/minimum_eigensolvers/test_sampling_vqe.py +++ /dev/null @@ -1,287 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test the Sampler VQE.""" - - -import unittest -import warnings -from functools import partial -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from ddt import data, ddt -from scipy.optimize import minimize as scipy_minimize - -from qiskit.algorithms import AlgorithmError -from qiskit.algorithms.minimum_eigensolvers import SamplingVQE -from qiskit.algorithms.optimizers import L_BFGS_B, QNSPSA, SLSQP, OptimizerResult -from qiskit.algorithms.state_fidelities import ComputeUncompute -from qiskit.circuit import ParameterVector, QuantumCircuit -from qiskit.circuit.library import RealAmplitudes, TwoLocal -from qiskit.opflow import PauliSumOp -from qiskit.primitives import Sampler -from qiskit.quantum_info import Operator, Pauli, SparsePauliOp -from qiskit.utils import algorithm_globals - - -# pylint: disable=invalid-name -def _mock_optimizer(fun, x0, jac=None, bounds=None, inputs=None): - """A mock of a callable that can be used as minimizer in the VQE. - - If ``inputs`` is given as a dictionary, stores the inputs in that dictionary. - """ - result = OptimizerResult() - result.x = np.zeros_like(x0) - result.fun = fun(result.x) - result.nit = 0 - - if inputs is not None: - inputs.update({"fun": fun, "x0": x0, "jac": jac, "bounds": bounds}) - return result - - -PAULI_OP = PauliSumOp(SparsePauliOp(["ZZ", "IZ", "II"], coeffs=[1, -0.5, 0.12])) -OP = Operator(PAULI_OP.to_matrix()) - - -@ddt -class TestSamplerVQE(QiskitAlgorithmsTestCase): - """Test VQE""" - - def setUp(self): - super().setUp() - self.optimal_value = -1.38 - self.optimal_bitstring = "10" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 42 - - @data(PAULI_OP, OP) - def test_exact_sampler(self, op): - """Test the VQE on BasicAer's statevector simulator.""" - thetas = ParameterVector("th", 4) - ansatz = QuantumCircuit(2) - ansatz.rx(thetas[0], 0) - ansatz.rx(thetas[1], 1) - ansatz.cz(0, 1) - ansatz.ry(thetas[2], 0) - ansatz.ry(thetas[3], 1) - - optimizer = L_BFGS_B() - - # start in maximal superposition - initial_point = np.zeros(ansatz.num_parameters) - initial_point[-ansatz.num_qubits :] = np.pi / 2 - - vqe = SamplingVQE(Sampler(), ansatz, optimizer, initial_point=initial_point) - result = vqe.compute_minimum_eigenvalue(operator=op) - - with self.subTest(msg="test eigenvalue"): - self.assertAlmostEqual(result.eigenvalue, self.optimal_value, places=5) - - with self.subTest(msg="test optimal_value"): - self.assertAlmostEqual(result.optimal_value, self.optimal_value, places=5) - - with self.subTest(msg="test dimension of optimal point"): - self.assertEqual(len(result.optimal_point), ansatz.num_parameters) - - with self.subTest(msg="assert cost_function_evals is set"): - self.assertIsNotNone(result.cost_function_evals) - - with self.subTest(msg="assert optimizer_time is set"): - self.assertIsNotNone(result.optimizer_time) - - with self.subTest(msg="check best measurement"): - self.assertEqual(result.best_measurement["bitstring"], self.optimal_bitstring) - self.assertEqual(result.best_measurement["value"], self.optimal_value) - - @data(PAULI_OP, OP) - def test_invalid_initial_point(self, op): - """Test the proper error is raised when the initial point has the wrong size.""" - ansatz = RealAmplitudes(2, reps=1) - initial_point = np.array([1]) - - vqe = SamplingVQE(Sampler(), ansatz, SLSQP(), initial_point=initial_point) - - with self.assertRaises(ValueError): - _ = vqe.compute_minimum_eigenvalue(operator=op) - - @data(PAULI_OP, OP) - def test_ansatz_resize(self, op): - """Test the ansatz is properly resized if it's a blueprint circuit.""" - ansatz = RealAmplitudes(1, reps=1) - vqe = SamplingVQE(Sampler(), ansatz, SLSQP()) - result = vqe.compute_minimum_eigenvalue(operator=op) - self.assertAlmostEqual(result.eigenvalue, self.optimal_value, places=5) - - @data(PAULI_OP, OP) - def test_invalid_ansatz_size(self, op): - """Test an error is raised if the ansatz has the wrong number of qubits.""" - ansatz = QuantumCircuit(1) - ansatz.compose(RealAmplitudes(1, reps=2)) - vqe = SamplingVQE(Sampler(), ansatz, SLSQP()) - - with self.assertRaises(AlgorithmError): - _ = vqe.compute_minimum_eigenvalue(operator=op) - - @data(PAULI_OP, OP) - def test_missing_varform_params(self, op): - """Test specifying a variational form with no parameters raises an error.""" - circuit = QuantumCircuit(op.num_qubits) - vqe = SamplingVQE(Sampler(), circuit, SLSQP()) - with self.assertRaises(AlgorithmError): - vqe.compute_minimum_eigenvalue(operator=op) - - @data(PAULI_OP, OP) - def test_batch_evaluate_slsqp(self, op): - """Test batching with SLSQP (as representative of SciPyOptimizer).""" - optimizer = SLSQP(max_evals_grouped=10) - vqe = SamplingVQE(Sampler(), RealAmplitudes(), optimizer) - result = vqe.compute_minimum_eigenvalue(operator=op) - self.assertAlmostEqual(result.eigenvalue, self.optimal_value, places=5) - - def test_batch_evaluate_with_qnspsa(self): - """Test batch evaluating with QNSPSA works.""" - ansatz = TwoLocal(2, rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - - wrapped_sampler = Sampler() - inner_sampler = Sampler() - - callcount = {"count": 0} - - def wrapped_run(*args, **kwargs): - kwargs["callcount"]["count"] += 1 - return inner_sampler.run(*args, **kwargs) - - wrapped_sampler.run = partial(wrapped_run, callcount=callcount) - - fidelity = ComputeUncompute(wrapped_sampler) - - def fidelity_callable(left, right): - batchsize = np.asarray(left).shape[0] - job = fidelity.run(batchsize * [ansatz], batchsize * [ansatz], left, right) - return job.result().fidelities - - qnspsa = QNSPSA(fidelity_callable, maxiter=5) - qnspsa.set_max_evals_grouped(100) - - vqe = SamplingVQE(wrapped_sampler, ansatz, qnspsa) - _ = vqe.compute_minimum_eigenvalue(Pauli("ZZ")) - - # 1 calibration + 1 stddev estimation + 1 initial blocking - # + 5 (1 loss + 1 fidelity + 1 blocking) + 1 return loss + 1 VQE eval - expected = 1 + 1 + 1 + 5 * 3 + 1 + 1 - - self.assertEqual(callcount["count"], expected) - - def test_optimizer_scipy_callable(self): - """Test passing a SciPy optimizer directly as callable.""" - vqe = SamplingVQE( - Sampler(), - RealAmplitudes(), - partial(scipy_minimize, method="COBYLA", options={"maxiter": 2}), - ) - result = vqe.compute_minimum_eigenvalue(Pauli("Z")) - self.assertEqual(result.cost_function_evals, 2) - - def test_optimizer_callable(self): - """Test passing a optimizer directly as callable.""" - ansatz = RealAmplitudes(1, reps=1) - vqe = SamplingVQE(Sampler(), ansatz, _mock_optimizer) - result = vqe.compute_minimum_eigenvalue(Pauli("Z")) - self.assertTrue(np.all(result.optimal_point == np.zeros(ansatz.num_parameters))) - - @data(PAULI_OP, OP) - def test_auxops(self, op): - """Test passing auxiliary operators.""" - ansatz = RealAmplitudes(2, reps=1) - vqe = SamplingVQE(Sampler(), ansatz, SLSQP()) - - as_list = [Pauli("ZZ"), Pauli("II")] - with self.subTest(auxops=as_list): - result = vqe.compute_minimum_eigenvalue(op, aux_operators=as_list) - self.assertIsInstance(result.aux_operators_evaluated, list) - self.assertEqual(len(result.aux_operators_evaluated), 2) - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], -1 + 0j, places=5) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 1 + 0j, places=5) - - as_dict = {"magnetization": SparsePauliOp(["ZI", "IZ"])} - with self.subTest(auxops=as_dict): - result = vqe.compute_minimum_eigenvalue(op, aux_operators=as_dict) - self.assertIsInstance(result.aux_operators_evaluated, dict) - self.assertEqual(len(result.aux_operators_evaluated.keys()), 1) - self.assertAlmostEqual(result.aux_operators_evaluated["magnetization"][0], 0j, places=5) - - def test_nondiag_observable_raises(self): - """Test passing a non-diagonal observable raises an error.""" - vqe = SamplingVQE(Sampler(), RealAmplitudes(), SLSQP()) - - with self.assertRaises(ValueError): - _ = vqe.compute_minimum_eigenvalue(Pauli("X")) - - @data(PAULI_OP, OP) - def test_callback(self, op): - """Test the callback on VQE.""" - history = { - "eval_count": [], - "parameters": [], - "mean": [], - "metadata": [], - } - - def store_intermediate_result(eval_count, parameters, mean, metadata): - history["eval_count"].append(eval_count) - history["parameters"].append(parameters) - history["mean"].append(mean) - history["metadata"].append(metadata) - - sampling_vqe = SamplingVQE( - Sampler(), - RealAmplitudes(2, reps=1), - SLSQP(), - callback=store_intermediate_result, - ) - sampling_vqe.compute_minimum_eigenvalue(operator=op) - - self.assertTrue(all(isinstance(count, int) for count in history["eval_count"])) - self.assertTrue(all(isinstance(mean, complex) for mean in history["mean"])) - self.assertTrue(all(isinstance(metadata, dict) for metadata in history["metadata"])) - for params in history["parameters"]: - self.assertTrue(all(isinstance(param, float) for param in params)) - - def test_aggregation(self): - """Test the aggregation works.""" - - # test a custom aggregration that just uses the best measurement - def best_measurement(measurements): - res = min(measurements, key=lambda meas: meas[1])[1] - return res - - # test CVaR with alpha of 0.4 (i.e. 40% of the best measurements) - alpha = 0.4 - - ansatz = RealAmplitudes(1, reps=0) - ansatz.h(0) - - for aggregation in [alpha, best_measurement]: - with self.subTest(aggregation=aggregation): - vqe = SamplingVQE(Sampler(), ansatz, _mock_optimizer, aggregation=best_measurement) - result = vqe.compute_minimum_eigenvalue(Pauli("Z")) - - # evaluation at x0=0 samples -1 and 1 with 50% probability, and our aggregation - # takes the smallest value - self.assertAlmostEqual(result.optimal_value, -1) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/minimum_eigensolvers/test_vqe.py b/test/python/algorithms/minimum_eigensolvers/test_vqe.py deleted file mode 100644 index 210c622ca817..000000000000 --- a/test/python/algorithms/minimum_eigensolvers/test_vqe.py +++ /dev/null @@ -1,488 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test the variational quantum eigensolver algorithm.""" - -import unittest -import warnings -from test.python.algorithms import QiskitAlgorithmsTestCase - -from functools import partial -import numpy as np -from scipy.optimize import minimize as scipy_minimize -from ddt import data, ddt - -from qiskit import QuantumCircuit -from qiskit.algorithms import AlgorithmError -from qiskit.algorithms.gradients import ParamShiftEstimatorGradient -from qiskit.algorithms.minimum_eigensolvers import VQE -from qiskit.algorithms.optimizers import ( - CG, - COBYLA, - GradientDescent, - L_BFGS_B, - OptimizerResult, - P_BFGS, - QNSPSA, - SLSQP, - SPSA, - TNC, -) -from qiskit.algorithms.state_fidelities import ComputeUncompute -from qiskit.circuit.library import RealAmplitudes, TwoLocal -from qiskit.opflow import PauliSumOp, TwoQubitReduction -from qiskit.quantum_info import SparsePauliOp, Operator, Pauli -from qiskit.primitives import Estimator, Sampler -from qiskit.utils import algorithm_globals - -# pylint: disable=invalid-name -def _mock_optimizer(fun, x0, jac=None, bounds=None, inputs=None) -> OptimizerResult: - """A mock of a callable that can be used as minimizer in the VQE.""" - result = OptimizerResult() - result.x = np.zeros_like(x0) - result.fun = fun(result.x) - result.nit = 0 - - if inputs is not None: - inputs.update({"fun": fun, "x0": x0, "jac": jac, "bounds": bounds}) - return result - - -@ddt -class TestVQE(QiskitAlgorithmsTestCase): - """Test VQE""" - - def setUp(self): - super().setUp() - self.seed = 50 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - self.h2_op = SparsePauliOp( - ["II", "IZ", "ZI", "ZZ", "XX"], - coeffs=[ - -1.052373245772859, - 0.39793742484318045, - -0.39793742484318045, - -0.01128010425623538, - 0.18093119978423156, - ], - ) - self.h2_energy = -1.85727503 - - self.ryrz_wavefunction = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - self.ry_wavefunction = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz") - - @data(L_BFGS_B(), COBYLA()) - def test_basic_aer_statevector(self, estimator): - """Test VQE using reference Estimator.""" - vqe = VQE(Estimator(), self.ryrz_wavefunction, estimator) - - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - with self.subTest(msg="test eigenvalue"): - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - with self.subTest(msg="test optimal_value"): - self.assertAlmostEqual(result.optimal_value, self.h2_energy) - - with self.subTest(msg="test dimension of optimal point"): - self.assertEqual(len(result.optimal_point), 16) - - with self.subTest(msg="assert cost_function_evals is set"): - self.assertIsNotNone(result.cost_function_evals) - - with self.subTest(msg="assert optimizer_time is set"): - self.assertIsNotNone(result.optimizer_time) - - with self.subTest(msg="assert optimizer_result is set"): - self.assertIsNotNone(result.optimizer_result) - - with self.subTest(msg="assert optimizer_result."): - self.assertAlmostEqual(result.optimizer_result.fun, self.h2_energy, places=5) - - with self.subTest(msg="assert return ansatz is set"): - estimator = Estimator() - job = estimator.run(result.optimal_circuit, self.h2_op, result.optimal_point) - np.testing.assert_array_almost_equal(job.result().values, result.eigenvalue, 6) - - def test_invalid_initial_point(self): - """Test the proper error is raised when the initial point has the wrong size.""" - ansatz = self.ryrz_wavefunction - initial_point = np.array([1]) - - vqe = VQE( - Estimator(), - ansatz, - SLSQP(), - initial_point=initial_point, - ) - - with self.assertRaises(ValueError): - _ = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - def test_ansatz_resize(self): - """Test the ansatz is properly resized if it's a blueprint circuit.""" - ansatz = RealAmplitudes(1, reps=1) - vqe = VQE(Estimator(), ansatz, SLSQP()) - result = vqe.compute_minimum_eigenvalue(self.h2_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - def test_invalid_ansatz_size(self): - """Test an error is raised if the ansatz has the wrong number of qubits.""" - ansatz = QuantumCircuit(1) - ansatz.compose(RealAmplitudes(1, reps=2)) - vqe = VQE(Estimator(), ansatz, SLSQP()) - - with self.assertRaises(AlgorithmError): - _ = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - def test_missing_ansatz_params(self): - """Test specifying an ansatz with no parameters raises an error.""" - ansatz = QuantumCircuit(self.h2_op.num_qubits) - vqe = VQE(Estimator(), ansatz, SLSQP()) - with self.assertRaises(AlgorithmError): - vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - def test_max_evals_grouped(self): - """Test with SLSQP with max_evals_grouped.""" - optimizer = SLSQP(maxiter=50, max_evals_grouped=5) - vqe = VQE( - Estimator(), - self.ryrz_wavefunction, - optimizer, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - @data( - CG(), - L_BFGS_B(), - P_BFGS(), - SLSQP(), - TNC(), - ) - def test_with_gradient(self, optimizer): - """Test VQE using gradient primitive.""" - estimator = Estimator() - vqe = VQE( - estimator, - self.ry_wavefunction, - optimizer, - gradient=ParamShiftEstimatorGradient(estimator), - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - def test_gradient_passed(self): - """Test the gradient is properly passed into the optimizer.""" - inputs = {} - estimator = Estimator() - vqe = VQE( - estimator, - RealAmplitudes(), - partial(_mock_optimizer, inputs=inputs), - gradient=ParamShiftEstimatorGradient(estimator), - ) - _ = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertIsNotNone(inputs["jac"]) - - def test_gradient_run(self): - """Test using the gradient to calculate the minimum.""" - estimator = Estimator() - vqe = VQE( - estimator, - RealAmplitudes(), - GradientDescent(maxiter=200, learning_rate=0.1), - gradient=ParamShiftEstimatorGradient(estimator), - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - def test_with_two_qubit_reduction(self): - """Test the VQE using TwoQubitReduction.""" - with self.assertWarns(DeprecationWarning): - qubit_op = PauliSumOp.from_list( - [ - ("IIII", -0.8105479805373266), - ("IIIZ", 0.17218393261915552), - ("IIZZ", -0.22575349222402472), - ("IZZI", 0.1721839326191556), - ("ZZII", -0.22575349222402466), - ("IIZI", 0.1209126326177663), - ("IZZZ", 0.16892753870087912), - ("IXZX", -0.045232799946057854), - ("ZXIX", 0.045232799946057854), - ("IXIX", 0.045232799946057854), - ("ZXZX", -0.045232799946057854), - ("ZZIZ", 0.16614543256382414), - ("IZIZ", 0.16614543256382414), - ("ZZZZ", 0.17464343068300453), - ("ZIZI", 0.1209126326177663), - ] - ) - tapered_qubit_op = TwoQubitReduction(num_particles=2).convert(qubit_op) - vqe = VQE( - Estimator(), - self.ry_wavefunction, - SPSA(maxiter=300, last_avg=5), - ) - result = vqe.compute_minimum_eigenvalue(tapered_qubit_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=2) - - def test_callback(self): - """Test the callback on VQE.""" - history = {"eval_count": [], "parameters": [], "mean": [], "metadata": []} - - def store_intermediate_result(eval_count, parameters, mean, metadata): - history["eval_count"].append(eval_count) - history["parameters"].append(parameters) - history["mean"].append(mean) - history["metadata"].append(metadata) - - optimizer = COBYLA(maxiter=3) - wavefunction = self.ry_wavefunction - - estimator = Estimator() - - vqe = VQE( - estimator, - wavefunction, - optimizer, - callback=store_intermediate_result, - ) - vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertTrue(all(isinstance(count, int) for count in history["eval_count"])) - self.assertTrue(all(isinstance(mean, float) for mean in history["mean"])) - self.assertTrue(all(isinstance(metadata, dict) for metadata in history["metadata"])) - for params in history["parameters"]: - self.assertTrue(all(isinstance(param, float) for param in params)) - - def test_reuse(self): - """Test re-using a VQE algorithm instance.""" - ansatz = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - vqe = VQE(Estimator(), ansatz, SLSQP(maxiter=300)) - with self.subTest(msg="assert VQE works once all info is available"): - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - operator = Operator(np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]])) - - with self.subTest(msg="assert vqe works on re-use."): - result = vqe.compute_minimum_eigenvalue(operator=operator) - self.assertAlmostEqual(result.eigenvalue.real, -1.0, places=5) - - def test_vqe_optimizer_reuse(self): - """Test running same VQE twice to re-use optimizer, then switch optimizer""" - vqe = VQE( - Estimator(), - self.ryrz_wavefunction, - SLSQP(), - ) - - def run_check(): - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - run_check() - - with self.subTest("Optimizer re-use."): - run_check() - - with self.subTest("Optimizer replace."): - vqe.optimizer = L_BFGS_B() - run_check() - - def test_default_batch_evaluation_on_spsa(self): - """Test the default batching works.""" - ansatz = TwoLocal(2, rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - - wrapped_estimator = Estimator() - inner_estimator = Estimator() - - callcount = {"estimator": 0} - - def wrapped_estimator_run(*args, **kwargs): - kwargs["callcount"]["estimator"] += 1 - return inner_estimator.run(*args, **kwargs) - - wrapped_estimator.run = partial(wrapped_estimator_run, callcount=callcount) - - spsa = SPSA(maxiter=5) - - vqe = VQE(wrapped_estimator, ansatz, spsa) - _ = vqe.compute_minimum_eigenvalue(Pauli("ZZ")) - - # 1 calibration + 5 loss + 1 return loss - expected_estimator_runs = 1 + 5 + 1 - - with self.subTest(msg="check callcount"): - self.assertEqual(callcount["estimator"], expected_estimator_runs) - - with self.subTest(msg="check reset to original max evals grouped"): - self.assertIsNone(spsa._max_evals_grouped) - - def test_batch_evaluate_with_qnspsa(self): - """Test batch evaluating with QNSPSA works.""" - ansatz = TwoLocal(2, rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - - wrapped_sampler = Sampler() - inner_sampler = Sampler() - - wrapped_estimator = Estimator() - inner_estimator = Estimator() - - callcount = {"sampler": 0, "estimator": 0} - - def wrapped_estimator_run(*args, **kwargs): - kwargs["callcount"]["estimator"] += 1 - return inner_estimator.run(*args, **kwargs) - - def wrapped_sampler_run(*args, **kwargs): - kwargs["callcount"]["sampler"] += 1 - return inner_sampler.run(*args, **kwargs) - - wrapped_estimator.run = partial(wrapped_estimator_run, callcount=callcount) - wrapped_sampler.run = partial(wrapped_sampler_run, callcount=callcount) - - fidelity = ComputeUncompute(wrapped_sampler) - - def fidelity_callable(left, right): - batchsize = np.asarray(left).shape[0] - job = fidelity.run(batchsize * [ansatz], batchsize * [ansatz], left, right) - return job.result().fidelities - - qnspsa = QNSPSA(fidelity_callable, maxiter=5) - qnspsa.set_max_evals_grouped(100) - - vqe = VQE( - wrapped_estimator, - ansatz, - qnspsa, - ) - _ = vqe.compute_minimum_eigenvalue(Pauli("ZZ")) - - # 5 (fidelity) - expected_sampler_runs = 5 - # 1 calibration + 1 stddev estimation + 1 initial blocking - # + 5 (1 loss + 1 blocking) + 1 return loss - expected_estimator_runs = 1 + 1 + 1 + 5 * 2 + 1 - - self.assertEqual(callcount["sampler"], expected_sampler_runs) - self.assertEqual(callcount["estimator"], expected_estimator_runs) - - def test_optimizer_scipy_callable(self): - """Test passing a SciPy optimizer directly as callable.""" - vqe = VQE( - Estimator(), - self.ryrz_wavefunction, - partial(scipy_minimize, method="L-BFGS-B", options={"maxiter": 10}), - ) - result = vqe.compute_minimum_eigenvalue(self.h2_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=2) - - def test_optimizer_callable(self): - """Test passing a optimizer directly as callable.""" - ansatz = RealAmplitudes(1, reps=1) - vqe = VQE(Estimator(), ansatz, _mock_optimizer) - result = vqe.compute_minimum_eigenvalue(SparsePauliOp("Z")) - self.assertTrue(np.all(result.optimal_point == np.zeros(ansatz.num_parameters))) - - def test_aux_operators_list(self): - """Test list-based aux_operators.""" - vqe = VQE(Estimator(), self.ry_wavefunction, SLSQP(maxiter=300)) - - with self.subTest("Test with an empty list."): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=[]) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertIsInstance(result.aux_operators_evaluated, list) - self.assertEqual(len(result.aux_operators_evaluated), 0) - - with self.subTest("Test with two auxiliary operators."): - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list( - [("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)] - ) - aux_ops = [aux_op1, aux_op2] - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=aux_ops) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - self.assertEqual(len(result.aux_operators_evaluated), 2) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2.0, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0.0, places=6) - # metadata - self.assertIsInstance(result.aux_operators_evaluated[0][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[1][1], dict) - - with self.subTest("Test with additional zero operator."): - extra_ops = [*aux_ops, 0] - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=extra_ops) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - self.assertEqual(len(result.aux_operators_evaluated), 3) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2.0, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0.0, places=6) - self.assertAlmostEqual(result.aux_operators_evaluated[2][0], 0.0) - # metadata - self.assertIsInstance(result.aux_operators_evaluated[0][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[1][1], dict) - self.assertIsInstance(result.aux_operators_evaluated[2][1], dict) - - def test_aux_operators_dict(self): - """Test dictionary compatibility of aux_operators""" - vqe = VQE(Estimator(), self.ry_wavefunction, SLSQP(maxiter=300)) - - with self.subTest("Test with an empty dictionary."): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators={}) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertIsInstance(result.aux_operators_evaluated, dict) - self.assertEqual(len(result.aux_operators_evaluated), 0) - - with self.subTest("Test with two auxiliary operators."): - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list( - [("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)] - ) - aux_ops = {"aux_op1": aux_op1, "aux_op2": aux_op2} - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=aux_ops) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertEqual(len(result.aux_operators_evaluated), 2) - - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][0], 2.0, places=5) - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][0], 0.0, places=5) - # metadata - self.assertIsInstance(result.aux_operators_evaluated["aux_op1"][1], dict) - self.assertIsInstance(result.aux_operators_evaluated["aux_op2"][1], dict) - - with self.subTest("Test with additional zero operator."): - extra_ops = {**aux_ops, "zero_operator": 0} - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=extra_ops) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertEqual(len(result.aux_operators_evaluated), 3) - # expectation values - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][0], 2.0, places=5) - self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][0], 0.0, places=5) - self.assertAlmostEqual(result.aux_operators_evaluated["zero_operator"][0], 0.0) - # metadata - self.assertIsInstance(result.aux_operators_evaluated["aux_op1"][1], dict) - self.assertIsInstance(result.aux_operators_evaluated["aux_op2"][1], dict) - self.assertIsInstance(result.aux_operators_evaluated["zero_operator"][1], dict) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/optimizers/__init__.py b/test/python/algorithms/optimizers/__init__.py deleted file mode 100644 index c6268f028739..000000000000 --- a/test/python/algorithms/optimizers/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Qiskit's algorithm optimizer tests.""" diff --git a/test/python/algorithms/optimizers/test_gradient_descent.py b/test/python/algorithms/optimizers/test_gradient_descent.py deleted file mode 100644 index 8d2396650176..000000000000 --- a/test/python/algorithms/optimizers/test_gradient_descent.py +++ /dev/null @@ -1,196 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for the Gradient Descent optimizer.""" - -from test.python.algorithms import QiskitAlgorithmsTestCase -import numpy as np -from qiskit.algorithms.optimizers import GradientDescent, GradientDescentState -from qiskit.algorithms.optimizers.steppable_optimizer import TellData, AskData -from qiskit.circuit.library import PauliTwoDesign -from qiskit.opflow import I, Z, StateFn - - -class TestGradientDescent(QiskitAlgorithmsTestCase): - """Tests for the gradient descent optimizer.""" - - def setUp(self): - super().setUp() - np.random.seed(12) - self.initial_point = np.array([1, 1, 1, 1, 0]) - - def objective(self, x): - """Objective Function for the tests""" - return (np.linalg.norm(x) - 1) ** 2 - - def grad(self, x): - """Gradient of the objective function""" - return 2 * (np.linalg.norm(x) - 1) * x / np.linalg.norm(x) - - def test_pauli_two_design(self): - """Test standard gradient descent on the Pauli two-design example.""" - circuit = PauliTwoDesign(3, reps=3, seed=2) - parameters = list(circuit.parameters) - with self.assertWarns(DeprecationWarning): - obs = Z ^ Z ^ I - expr = ~StateFn(obs) @ StateFn(circuit) - - initial_point = np.array( - [ - 0.1822308, - -0.27254251, - 0.83684425, - 0.86153976, - -0.7111668, - 0.82766631, - 0.97867993, - 0.46136964, - 2.27079901, - 0.13382699, - 0.29589915, - 0.64883193, - ] - ) - - def objective_pauli(x): - return expr.bind_parameters(dict(zip(parameters, x))).eval().real - - optimizer = GradientDescent(maxiter=100, learning_rate=0.1, perturbation=0.1) - - with self.assertWarns(DeprecationWarning): - result = optimizer.minimize(objective_pauli, x0=initial_point) - self.assertLess(result.fun, -0.95) # final loss - self.assertEqual(result.nfev, 1300) # function evaluations - - def test_callback(self): - """Test the callback.""" - - history = [] - - def callback(*args): - history.append(args) - - optimizer = GradientDescent(maxiter=1, callback=callback) - - _ = optimizer.minimize(self.objective, np.array([1, -1])) - - self.assertEqual(len(history), 1) - self.assertIsInstance(history[0][0], int) # nfevs - self.assertIsInstance(history[0][1], np.ndarray) # parameters - self.assertIsInstance(history[0][2], float) # function value - self.assertIsInstance(history[0][3], float) # norm of the gradient - - def test_minimize(self): - """Test setting the learning rate as iterator and minimizing the funciton.""" - - def learning_rate(): - power = 0.6 - constant_coeff = 0.1 - - def powerlaw(): - n = 0 - while True: - yield constant_coeff * (n**power) - n += 1 - - return powerlaw() - - optimizer = GradientDescent(maxiter=20, learning_rate=learning_rate) - result = optimizer.minimize(self.objective, self.initial_point, self.grad) - - self.assertLess(result.fun, 1e-5) - - def test_no_start(self): - """Tests that making a step without having started the optimizer raises an error.""" - optimizer = GradientDescent() - with self.assertRaises(AttributeError): - optimizer.step() - - def test_start(self): - """Tests if the start method initializes the state properly.""" - optimizer = GradientDescent() - self.assertIsNone(optimizer.state) - self.assertIsNone(optimizer.perturbation) - optimizer.start(x0=self.initial_point, fun=self.objective) - - test_state = GradientDescentState( - x=self.initial_point, - fun=self.objective, - jac=None, - nfev=0, - njev=0, - nit=0, - learning_rate=1, - stepsize=None, - ) - - self.assertEqual(test_state, optimizer.state) - - def test_ask(self): - """Test the ask method.""" - optimizer = GradientDescent() - optimizer.start(fun=self.objective, x0=self.initial_point) - - ask_data = optimizer.ask() - np.testing.assert_equal(ask_data.x_jac, self.initial_point) - self.assertIsNone(ask_data.x_fun) - - def test_evaluate(self): - """Test the evaluate method.""" - optimizer = GradientDescent(perturbation=1e-10) - optimizer.start(fun=self.objective, x0=self.initial_point) - ask_data = AskData(x_jac=self.initial_point) - tell_data = optimizer.evaluate(ask_data=ask_data) - np.testing.assert_almost_equal(tell_data.eval_jac, self.grad(self.initial_point), decimal=2) - - def test_tell(self): - """Test the tell method.""" - optimizer = GradientDescent(learning_rate=1.0) - optimizer.start(fun=self.objective, x0=self.initial_point) - ask_data = AskData(x_jac=self.initial_point) - tell_data = TellData(eval_jac=self.initial_point) - optimizer.tell(ask_data=ask_data, tell_data=tell_data) - np.testing.assert_equal(optimizer.state.x, np.zeros(optimizer.state.x.shape)) - - def test_continue_condition(self): - """Test if the continue condition is working properly.""" - optimizer = GradientDescent(tol=1) - optimizer.start(fun=self.objective, x0=self.initial_point) - self.assertTrue(optimizer.continue_condition()) - optimizer.state.stepsize = 0.1 - self.assertFalse(optimizer.continue_condition()) - optimizer.state.stepsize = 10 - optimizer.state.nit = 1000 - self.assertFalse(optimizer.continue_condition()) - - def test_step(self): - """Tests if performing one step yields the desired result.""" - optimizer = GradientDescent(learning_rate=1.0) - optimizer.start(fun=self.objective, jac=self.grad, x0=self.initial_point) - optimizer.step() - np.testing.assert_almost_equal( - optimizer.state.x, self.initial_point - self.grad(self.initial_point), 6 - ) - - def test_wrong_dimension_gradient(self): - """Tests if an error is raised when a gradient of the wrong dimension is passed.""" - - optimizer = GradientDescent(learning_rate=1.0) - optimizer.start(fun=self.objective, x0=self.initial_point) - ask_data = AskData(x_jac=self.initial_point) - tell_data = TellData(eval_jac=np.array([1.0, 5])) - with self.assertRaises(ValueError): - optimizer.tell(ask_data=ask_data, tell_data=tell_data) - - tell_data = TellData(eval_jac=np.array(1)) - with self.assertRaises(ValueError): - optimizer.tell(ask_data=ask_data, tell_data=tell_data) diff --git a/test/python/algorithms/optimizers/test_optimizer_aqgd.py b/test/python/algorithms/optimizers/test_optimizer_aqgd.py deleted file mode 100644 index 9bef92b48cd4..000000000000 --- a/test/python/algorithms/optimizers/test_optimizer_aqgd.py +++ /dev/null @@ -1,122 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test of AQGD optimizer""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.circuit.library import RealAmplitudes -from qiskit.utils import QuantumInstance, algorithm_globals, optionals -from qiskit.opflow import PauliSumOp -from qiskit.algorithms.optimizers import AQGD -from qiskit.algorithms import VQE, AlgorithmError -from qiskit.opflow.gradients import Gradient -from qiskit.test import slow_test - - -class TestOptimizerAQGD(QiskitAlgorithmsTestCase): - """Test AQGD optimizer using RY for analytic gradient with VQE""" - - def setUp(self): - super().setUp() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 50 - with self.assertWarns(DeprecationWarning): - self.qubit_op = PauliSumOp.from_list( - [ - ("II", -1.052373245772859), - ("IZ", 0.39793742484318045), - ("ZI", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] - ) - - @slow_test - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required to run this test") - def test_simple(self): - """test AQGD optimizer with the parameters as single values.""" - from qiskit_aer import Aer - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - Aer.get_backend("aer_simulator_statevector"), - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - aqgd = AQGD(momentum=0.0) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=RealAmplitudes(), - optimizer=aqgd, - gradient=Gradient("lin_comb"), - quantum_instance=q_instance, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.qubit_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.857, places=3) - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required to run this test") - def test_list(self): - """test AQGD optimizer with the parameters as lists.""" - from qiskit_aer import Aer - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - Aer.get_backend("aer_simulator_statevector"), - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - aqgd = AQGD(maxiter=[1000, 1000, 1000], eta=[1.0, 0.5, 0.3], momentum=[0.0, 0.5, 0.75]) - - with self.assertWarns(DeprecationWarning): - vqe = VQE(ansatz=RealAmplitudes(), optimizer=aqgd, quantum_instance=q_instance) - result = vqe.compute_minimum_eigenvalue(operator=self.qubit_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.857, places=3) - - def test_raises_exception(self): - """tests that AQGD raises an exception when incorrect values are passed.""" - self.assertRaises(AlgorithmError, AQGD, maxiter=[1000], eta=[1.0, 0.5], momentum=[0.0, 0.5]) - - @slow_test - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required to run this test") - def test_int_values(self): - """test AQGD with int values passed as eta and momentum.""" - from qiskit_aer import Aer - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - Aer.get_backend("aer_simulator_statevector"), - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - aqgd = AQGD(maxiter=1000, eta=1, momentum=0) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=RealAmplitudes(), - optimizer=aqgd, - gradient=Gradient("lin_comb"), - quantum_instance=q_instance, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.qubit_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.857, places=3) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/optimizers/test_optimizer_nft.py b/test/python/algorithms/optimizers/test_optimizer_nft.py deleted file mode 100644 index e904c3bffd91..000000000000 --- a/test/python/algorithms/optimizers/test_optimizer_nft.py +++ /dev/null @@ -1,64 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test of NFT optimizer""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit import BasicAer -from qiskit.circuit.library import RealAmplitudes -from qiskit.utils import QuantumInstance, algorithm_globals -from qiskit.opflow import PauliSumOp -from qiskit.algorithms.optimizers import NFT -from qiskit.algorithms import VQE - - -class TestOptimizerNFT(QiskitAlgorithmsTestCase): - """Test NFT optimizer using RY with VQE""" - - def setUp(self): - super().setUp() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 50 - with self.assertWarns(DeprecationWarning): - self.qubit_op = PauliSumOp.from_list( - [ - ("II", -1.052373245772859), - ("IZ", 0.39793742484318045), - ("ZI", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] - ) - - def test_nft(self): - """Test NFT optimizer by using it""" - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=RealAmplitudes(), - optimizer=NFT(), - quantum_instance=QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ), - ) - result = vqe.compute_minimum_eigenvalue(operator=self.qubit_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.857275, places=6) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/optimizers/test_optimizers.py b/test/python/algorithms/optimizers/test_optimizers.py deleted file mode 100644 index f0e759ce4e4e..000000000000 --- a/test/python/algorithms/optimizers/test_optimizers.py +++ /dev/null @@ -1,404 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Optimizers""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -from typing import Optional, List, Tuple -from ddt import ddt, data, unpack -import numpy as np -from scipy.optimize import rosen, rosen_der - -from qiskit.algorithms.optimizers import ( - ADAM, - AQGD, - BOBYQA, - IMFIL, - CG, - CRS, - COBYLA, - DIRECT_L, - DIRECT_L_RAND, - GSLS, - GradientDescent, - L_BFGS_B, - NELDER_MEAD, - Optimizer, - P_BFGS, - POWELL, - SLSQP, - SPSA, - QNSPSA, - TNC, - SciPyOptimizer, -) -from qiskit.circuit.library import RealAmplitudes -from qiskit.exceptions import MissingOptionalLibraryError -from qiskit.utils import algorithm_globals, optionals - - -@ddt -class TestOptimizers(QiskitAlgorithmsTestCase): - """Test Optimizers""" - - def setUp(self): - super().setUp() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 52 - - def run_optimizer( - self, - optimizer: Optimizer, - max_nfev: int, - grad: bool = False, - bounds: Optional[List[Tuple[float, float]]] = None, - ): - """Test the optimizer. - - Args: - optimizer: The optimizer instance to test. - max_nfev: The maximal allowed number of function evaluations. - grad: Whether to pass the gradient function as input. - bounds: Optimizer bounds. - """ - x_0 = [1.3, 0.7, 0.8, 1.9, 1.2] - jac = rosen_der if grad else None - - res = optimizer.minimize(rosen, x_0, jac, bounds) - x_opt = res.x - nfev = res.nfev - - np.testing.assert_array_almost_equal(x_opt, [1.0] * len(x_0), decimal=2) - self.assertLessEqual(nfev, max_nfev) - - def test_adam(self): - """adam test""" - optimizer = ADAM(maxiter=10000, tol=1e-06) - self.run_optimizer(optimizer, max_nfev=10000) - - def test_cg(self): - """cg test""" - optimizer = CG(maxiter=1000, tol=1e-06) - self.run_optimizer(optimizer, max_nfev=10000) - - def test_gradient_descent(self): - """cg test""" - optimizer = GradientDescent(maxiter=100000, tol=1e-06, learning_rate=1e-3) - self.run_optimizer(optimizer, grad=True, max_nfev=100000) - - def test_cobyla(self): - """cobyla test""" - optimizer = COBYLA(maxiter=100000, tol=1e-06) - self.run_optimizer(optimizer, max_nfev=100000) - - def test_l_bfgs_b(self): - """l_bfgs_b test""" - optimizer = L_BFGS_B(maxfun=1000) - self.run_optimizer(optimizer, max_nfev=10000) - - def test_p_bfgs(self): - """parallel l_bfgs_b test""" - optimizer = P_BFGS(maxfun=1000, max_processes=4) - self.run_optimizer(optimizer, max_nfev=10000) - - def test_nelder_mead(self): - """nelder mead test""" - optimizer = NELDER_MEAD(maxfev=10000, tol=1e-06) - self.run_optimizer(optimizer, max_nfev=10000) - - def test_powell(self): - """powell test""" - optimizer = POWELL(maxfev=10000, tol=1e-06) - self.run_optimizer(optimizer, max_nfev=10000) - - def test_slsqp(self): - """slsqp test""" - optimizer = SLSQP(maxiter=1000, tol=1e-06) - self.run_optimizer(optimizer, max_nfev=10000) - - @unittest.skip("Skipping SPSA as it does not do well on non-convex rozen") - def test_spsa(self): - """spsa test""" - optimizer = SPSA(maxiter=10000) - self.run_optimizer(optimizer, max_nfev=100000) - - def test_tnc(self): - """tnc test""" - optimizer = TNC(maxiter=1000, tol=1e-06) - self.run_optimizer(optimizer, max_nfev=10000) - - def test_gsls(self): - """gsls test""" - optimizer = GSLS( - sample_size_factor=40, - sampling_radius=1.0e-12, - maxiter=10000, - max_eval=10000, - min_step_size=1.0e-12, - ) - x_0 = [1.3, 0.7, 0.8, 1.9, 1.2] - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 1 - res = optimizer.minimize(rosen, x_0) - x_value = res.fun - n_evals = res.nfev - - # Ensure value is near-optimal - self.assertLessEqual(x_value, 0.01) - self.assertLessEqual(n_evals, 10000) - - def test_scipy_optimizer(self): - """scipy_optimizer test""" - optimizer = SciPyOptimizer("BFGS", options={"maxiter": 1000}) - self.run_optimizer(optimizer, max_nfev=10000) - - def test_scipy_optimizer_callback(self): - """scipy_optimizer callback test""" - values = [] - - def callback(x): - values.append(x) - - optimizer = SciPyOptimizer("BFGS", options={"maxiter": 1000}, callback=callback) - self.run_optimizer(optimizer, max_nfev=10000) - self.assertTrue(values) # Check the list is nonempty. - - # ESCH and ISRES do not do well with rosen - @data( - (CRS, True), - (DIRECT_L, True), - (DIRECT_L_RAND, True), - (CRS, False), - (DIRECT_L, False), - (DIRECT_L_RAND, False), - ) - @unpack - def test_nlopt(self, optimizer_cls, use_bound): - """NLopt test""" - bounds = [(-6, 6)] * 5 if use_bound else None - try: - optimizer = optimizer_cls() - optimizer.set_options(**{"max_evals": 50000}) - self.run_optimizer(optimizer, max_nfev=50000, bounds=bounds) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - - -@ddt -class TestOptimizerSerialization(QiskitAlgorithmsTestCase): - """Tests concerning the serialization of optimizers.""" - - @data( - ("BFGS", {"maxiter": 100, "eps": np.array([0.1])}), - ("CG", {"maxiter": 200, "gtol": 1e-8}), - ("COBYLA", {"maxiter": 10}), - ("L_BFGS_B", {"maxiter": 30}), - ("NELDER_MEAD", {"maxiter": 0}), - ("NFT", {"maxiter": 100}), - ("P_BFGS", {"maxiter": 5}), - ("POWELL", {"maxiter": 1}), - ("SLSQP", {"maxiter": 400}), - ("TNC", {"maxiter": 20}), - ("dogleg", {"maxiter": 100}), - ("trust-constr", {"maxiter": 10}), - ("trust-ncg", {"maxiter": 100}), - ("trust-exact", {"maxiter": 120}), - ("trust-krylov", {"maxiter": 150}), - ) - @unpack - def test_scipy(self, method, options): - """Test the SciPyOptimizer is serializable.""" - - optimizer = SciPyOptimizer(method, options=options) - serialized = optimizer.settings - from_dict = SciPyOptimizer(**serialized) - - self.assertEqual(from_dict._method, method.lower()) - self.assertEqual(from_dict._options, options) - - def test_independent_reconstruction(self): - """Test the SciPyOptimizers don't reset all settings upon creating a new instance. - - COBYLA is used as representative example here.""" - - kwargs = {"coffee": "without sugar"} - options = {"tea": "with milk"} - optimizer = COBYLA(maxiter=1, options=options, **kwargs) - serialized = optimizer.settings - from_dict = COBYLA(**serialized) - - with self.subTest(msg="test attributes"): - self.assertEqual(from_dict.settings["maxiter"], 1) - - with self.subTest(msg="test options"): - # options should only contain values that are *not* already in the initializer - # (e.g. should not contain maxiter) - self.assertEqual(from_dict.settings["options"], {"tea": "with milk"}) - - with self.subTest(msg="test kwargs"): - self.assertEqual(from_dict.settings["coffee"], "without sugar") - - with self.subTest(msg="option ids differ"): - self.assertNotEqual(id(serialized["options"]), id(from_dict.settings["options"])) - - def test_adam(self): - """Test ADAM is serializable.""" - - adam = ADAM(maxiter=100, amsgrad=True) - settings = adam.settings - - self.assertEqual(settings["maxiter"], 100) - self.assertTrue(settings["amsgrad"]) - - def test_aqgd(self): - """Test AQGD is serializable.""" - - opt = AQGD(maxiter=[200, 100], eta=[0.2, 0.1], momentum=[0.25, 0.1]) - settings = opt.settings - - self.assertListEqual(settings["maxiter"], [200, 100]) - self.assertListEqual(settings["eta"], [0.2, 0.1]) - self.assertListEqual(settings["momentum"], [0.25, 0.1]) - - @unittest.skipIf(not optionals.HAS_SKQUANT, "Install scikit-quant to run this test.") - def test_bobyqa(self): - """Test BOBYQA is serializable.""" - - opt = BOBYQA(maxiter=200) - settings = opt.settings - - self.assertEqual(settings["maxiter"], 200) - - @unittest.skipIf(not optionals.HAS_SKQUANT, "Install scikit-quant to run this test.") - def test_imfil(self): - """Test IMFIL is serializable.""" - - opt = IMFIL(maxiter=200) - settings = opt.settings - - self.assertEqual(settings["maxiter"], 200) - - def test_gradient_descent(self): - """Test GradientDescent is serializable.""" - - opt = GradientDescent(maxiter=10, learning_rate=0.01) - settings = opt.settings - - self.assertEqual(settings["maxiter"], 10) - self.assertEqual(settings["learning_rate"], 0.01) - - def test_gsls(self): - """Test GSLS is serializable.""" - - opt = GSLS(maxiter=100, sampling_radius=1e-3) - settings = opt.settings - - self.assertEqual(settings["maxiter"], 100) - self.assertEqual(settings["sampling_radius"], 1e-3) - - def test_spsa(self): - """Test SPSA optimizer is serializable.""" - options = { - "maxiter": 100, - "blocking": True, - "allowed_increase": 0.1, - "second_order": True, - "learning_rate": 0.02, - "perturbation": 0.05, - "regularization": 0.1, - "resamplings": 2, - "perturbation_dims": 5, - "trust_region": False, - "initial_hessian": None, - "lse_solver": None, - "hessian_delay": 0, - "callback": None, - "termination_checker": None, - } - spsa = SPSA(**options) - - self.assertDictEqual(spsa.settings, options) - - def test_spsa_custom_iterators(self): - """Test serialization works with custom iterators for learning rate and perturbation.""" - rate = 0.99 - - def powerlaw(): - n = 0 - while True: - yield rate**n - n += 1 - - def steps(): - n = 1 - divide_after = 20 - epsilon = 0.5 - while True: - yield epsilon - n += 1 - if n % divide_after == 0: - epsilon /= 2 - - learning_rate = powerlaw() - expected_learning_rate = np.array([next(learning_rate) for _ in range(200)]) - - perturbation = steps() - expected_perturbation = np.array([next(perturbation) for _ in range(200)]) - - spsa = SPSA(maxiter=200, learning_rate=powerlaw, perturbation=steps) - settings = spsa.settings - - self.assertTrue(np.allclose(settings["learning_rate"], expected_learning_rate)) - self.assertTrue(np.allclose(settings["perturbation"], expected_perturbation)) - - def test_qnspsa(self): - """Test QN-SPSA optimizer is serializable.""" - ansatz = RealAmplitudes(1) - fidelity = QNSPSA.get_fidelity(ansatz) - options = { - "fidelity": fidelity, - "maxiter": 100, - "blocking": True, - "allowed_increase": 0.1, - "learning_rate": 0.02, - "perturbation": 0.05, - "regularization": 0.1, - "resamplings": 2, - "perturbation_dims": 5, - "lse_solver": None, - "initial_hessian": None, - "callback": None, - "termination_checker": None, - "hessian_delay": 0, - } - spsa = QNSPSA(**options) - - settings = spsa.settings - expected = options.copy() - - with self.subTest(msg="check constructed dictionary"): - self.assertDictEqual(settings, expected) - - reconstructed = QNSPSA(**settings) # pylint: disable=unexpected-keyword-arg - with self.subTest(msg="test reconstructed optimizer"): - self.assertDictEqual(reconstructed.settings, expected) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/optimizers/test_optimizers_scikitquant.py b/test/python/algorithms/optimizers/test_optimizers_scikitquant.py deleted file mode 100644 index f3d3f645d895..000000000000 --- a/test/python/algorithms/optimizers/test_optimizers_scikitquant.py +++ /dev/null @@ -1,118 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test of scikit-quant optimizers.""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -from ddt import ddt, data, unpack - -import numpy -from qiskit import BasicAer -from qiskit.circuit.library import RealAmplitudes -from qiskit.utils import QuantumInstance, algorithm_globals -from qiskit.exceptions import MissingOptionalLibraryError -from qiskit.opflow import PauliSumOp -from qiskit.algorithms import VQE -from qiskit.algorithms.optimizers import BOBYQA, SNOBFIT, IMFIL - - -@ddt -class TestOptimizers(QiskitAlgorithmsTestCase): - """Test scikit-quant optimizers.""" - - def setUp(self): - """Set the problem.""" - super().setUp() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 50 - with self.assertWarns(DeprecationWarning): - self.qubit_op = PauliSumOp.from_list( - [ - ("II", -1.052373245772859), - ("IZ", 0.39793742484318045), - ("ZI", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] - ) - - def _optimize(self, optimizer): - """launch vqe""" - with self.assertWarns(DeprecationWarning): - qe = QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - with self.assertWarns(DeprecationWarning): - vqe = VQE(ansatz=RealAmplitudes(), optimizer=optimizer, quantum_instance=qe) - result = vqe.compute_minimum_eigenvalue(operator=self.qubit_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.857, places=1) - - def test_bobyqa(self): - """BOBYQA optimizer test.""" - try: - optimizer = BOBYQA(maxiter=150) - self._optimize(optimizer) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - - @unittest.skipIf( - # NB: numpy.__version__ may contain letters, e.g. "1.26.0b1" - tuple(map(int, numpy.__version__.split(".")[:2])) >= (1, 24), - "scikit's SnobFit currently incompatible with NumPy 1.24.0.", - ) - def test_snobfit(self): - """SNOBFIT optimizer test.""" - try: - optimizer = SNOBFIT(maxiter=100, maxfail=100, maxmp=20) - self._optimize(optimizer) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - - @unittest.skipIf( - # NB: numpy.__version__ may contain letters, e.g. "1.26.0b1" - tuple(map(int, numpy.__version__.split(".")[:2])) >= (1, 24), - "scikit's SnobFit currently incompatible with NumPy 1.24.0.", - ) - @data((None,), ([(-1, 1), (None, None)],)) - @unpack - def test_snobfit_missing_bounds(self, bounds): - """SNOBFIT optimizer test with missing bounds.""" - try: - optimizer = SNOBFIT() - with self.assertRaises(ValueError): - optimizer.minimize( - fun=lambda _: 1, # using dummy function (never called) - x0=[0.1, 0.1], # dummy initial point - bounds=bounds, - ) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - - def test_imfil(self): - """IMFIL test.""" - try: - optimizer = IMFIL(maxiter=100) - self._optimize(optimizer) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/optimizers/test_spsa.py b/test/python/algorithms/optimizers/test_spsa.py deleted file mode 100644 index dafa8c44b435..000000000000 --- a/test/python/algorithms/optimizers/test_spsa.py +++ /dev/null @@ -1,293 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for the SPSA optimizer.""" - -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import ddt, data - -import numpy as np - -from qiskit.algorithms.optimizers import SPSA, QNSPSA -from qiskit.circuit.library import PauliTwoDesign -from qiskit.primitives import Estimator, Sampler -from qiskit.providers.basicaer import StatevectorSimulatorPy -from qiskit.opflow import I, Z, StateFn, MatrixExpectation -from qiskit.utils import algorithm_globals - - -@ddt -class TestSPSA(QiskitAlgorithmsTestCase): - """Tests for the SPSA optimizer.""" - - def setUp(self): - super().setUp() - np.random.seed(12) - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 12 - - # @slow_test - @data("spsa", "2spsa", "qnspsa") - def test_pauli_two_design(self, method): - """Test SPSA on the Pauli two-design example.""" - circuit = PauliTwoDesign(3, reps=1, seed=1) - parameters = list(circuit.parameters) - with self.assertWarns(DeprecationWarning): - obs = Z ^ Z ^ I - expr = ~StateFn(obs) @ StateFn(circuit) - - initial_point = np.array( - [0.82311034, 0.02611798, 0.21077064, 0.61842177, 0.09828447, 0.62013131] - ) - - def objective(x): - return expr.bind_parameters(dict(zip(parameters, x))).eval().real - - settings = {"maxiter": 100, "blocking": True, "allowed_increase": 0} - - if method == "2spsa": - settings["second_order"] = True - settings["regularization"] = 0.01 - expected_nfev = settings["maxiter"] * 5 + 1 - elif method == "qnspsa": - settings["fidelity"] = QNSPSA.get_fidelity(circuit) - settings["regularization"] = 0.001 - settings["learning_rate"] = 0.05 - settings["perturbation"] = 0.05 - - expected_nfev = settings["maxiter"] * 7 + 1 - else: - expected_nfev = settings["maxiter"] * 3 + 1 - - if method == "qnspsa": - spsa = QNSPSA(**settings) - else: - spsa = SPSA(**settings) - - with self.assertWarns(DeprecationWarning): - result = spsa.optimize(circuit.num_parameters, objective, initial_point=initial_point) - - with self.subTest("check final accuracy"): - self.assertLess(result[1], -0.95) # final loss - - with self.subTest("check number of function calls"): - self.assertEqual(result[2], expected_nfev) # function evaluations - - def test_recalibrate_at_optimize(self): - """Test SPSA calibrates anew upon each optimization run, if no autocalibration is set.""" - - def objective(x): - return -(x**2) - - spsa = SPSA(maxiter=1) - _ = spsa.minimize(objective, x0=np.array([0.5])) - - self.assertIsNone(spsa.learning_rate) - self.assertIsNone(spsa.perturbation) - - def test_learning_rate_perturbation_as_iterators(self): - """Test the learning rate and perturbation can be callables returning iterators.""" - - def get_learning_rate(): - def learning_rate(): - x = 0.99 - while True: - x *= x - yield x - - return learning_rate - - def get_perturbation(): - def perturbation(): - x = 0.99 - while True: - x *= x - yield max(x, 0.01) - - return perturbation - - def objective(x): - return (np.linalg.norm(x) - 2) ** 2 - - spsa = SPSA(learning_rate=get_learning_rate(), perturbation=get_perturbation()) - result = spsa.minimize(objective, np.array([0.5, 0.5])) - - self.assertAlmostEqual(np.linalg.norm(result.x), 2, places=2) - - def test_learning_rate_perturbation_as_arrays(self): - """Test the learning rate and perturbation can be arrays.""" - - learning_rate = np.linspace(1, 0, num=100, endpoint=False) ** 2 - perturbation = 0.01 * np.ones(100) - - def objective(x): - return (np.linalg.norm(x) - 2) ** 2 - - spsa = SPSA(learning_rate=learning_rate, perturbation=perturbation) - result = spsa.minimize(objective, x0=np.array([0.5, 0.5])) - - self.assertAlmostEqual(np.linalg.norm(result.x), 2, places=2) - - def test_termination_checker(self): - """Test the termination_callback""" - - def objective(x): - return np.linalg.norm(x) + np.random.rand(1) - - class TerminationChecker: - """Example termination checker""" - - def __init__(self): - self.values = [] - - def __call__(self, nfev, point, fvalue, stepsize, accepted) -> bool: - self.values.append(fvalue) - - if len(self.values) > 10: - return True - return False - - maxiter = 400 - spsa = SPSA(maxiter=maxiter, termination_checker=TerminationChecker()) - result = spsa.minimize(objective, x0=[0.5, 0.5]) - - self.assertLess(result.nit, maxiter) - - def test_callback(self): - """Test using the callback.""" - - def objective(x): - return (np.linalg.norm(x) - 2) ** 2 - - history = {"nfevs": [], "points": [], "fvals": [], "updates": [], "accepted": []} - - def callback(nfev, point, fval, update, accepted): - history["nfevs"].append(nfev) - history["points"].append(point) - history["fvals"].append(fval) - history["updates"].append(update) - history["accepted"].append(accepted) - - maxiter = 10 - spsa = SPSA(maxiter=maxiter, learning_rate=0.01, perturbation=0.01, callback=callback) - _ = spsa.minimize(objective, x0=np.array([0.5, 0.5])) - - expected_types = [int, np.ndarray, float, float, bool] - for i, (key, values) in enumerate(history.items()): - self.assertTrue(all(isinstance(value, expected_types[i]) for value in values)) - self.assertEqual(len(history[key]), maxiter) - - @data(1, 2, 3, 4) - def test_estimate_stddev(self, max_evals_grouped): - """Test the estimate_stddev - See https://github.com/Qiskit/qiskit-nature/issues/797""" - - def objective(x): - if len(x.shape) == 2: - return np.array([sum(x_i) for x_i in x]) - return sum(x) - - point = np.ones(5) - result = SPSA.estimate_stddev(objective, point, avg=10, max_evals_grouped=max_evals_grouped) - self.assertAlmostEqual(result, 0) - - def test_qnspsa_fidelity_deprecation(self): - """Test using a backend and expectation converter in get_fidelity warns.""" - ansatz = PauliTwoDesign(2, reps=1, seed=2) - - with self.assertWarns(DeprecationWarning): - QNSPSA.get_fidelity(ansatz, backend=StatevectorSimulatorPy()) - with self.assertWarns(DeprecationWarning): - QNSPSA.get_fidelity(ansatz, expectation=MatrixExpectation()) - - # No warning when used correctly. - QNSPSA.get_fidelity(ansatz) - - def test_qnspsa_fidelity_primitives(self): - """Test the primitives can be used in get_fidelity.""" - ansatz = PauliTwoDesign(2, reps=1, seed=2) - initial_point = np.random.random(ansatz.num_parameters) - - with self.subTest(msg="pass as kwarg"): - fidelity = QNSPSA.get_fidelity(ansatz, sampler=Sampler()) - result = fidelity(initial_point, initial_point) - - self.assertAlmostEqual(result[0], 1) - - # this test can be removed once backend and expectation are removed - with self.subTest(msg="pass positionally"): - fidelity = QNSPSA.get_fidelity(ansatz, Sampler()) - result = fidelity(initial_point, initial_point) - - self.assertAlmostEqual(result[0], 1) - - def test_qnspsa_max_evals_grouped(self): - """Test using max_evals_grouped with QNSPSA.""" - circuit = PauliTwoDesign(3, reps=1, seed=1) - num_parameters = circuit.num_parameters - - with self.assertWarns(DeprecationWarning): - obs = Z ^ Z ^ I - - estimator = Estimator(options={"seed": 12}) - - initial_point = np.array( - [0.82311034, 0.02611798, 0.21077064, 0.61842177, 0.09828447, 0.62013131] - ) - - def objective(x): - x = np.reshape(x, (-1, num_parameters)).tolist() - n = len(x) - return estimator.run(n * [circuit], n * [obs.primitive], x).result().values.real - - fidelity = QNSPSA.get_fidelity(circuit) - optimizer = QNSPSA(fidelity) - optimizer.maxiter = 1 - optimizer.learning_rate = 0.05 - optimizer.perturbation = 0.05 - optimizer.set_max_evals_grouped(50) # greater than 1 - - result = optimizer.minimize(objective, initial_point) - - with self.subTest("check final accuracy"): - self.assertAlmostEqual(result.fun[0], 0.473, places=3) - - with self.subTest("check number of function calls"): - expected_nfev = 8 # 7 * maxiter + 1 - self.assertEqual(result.nfev, expected_nfev) - - def test_point_sample(self): - """Test point sample function in QNSPSA""" - - def fidelity(x, _y): - x = np.asarray(x) - return np.ones_like(x, dtype=float) # some float - - def objective(x): - return x - - def get_perturbation(): - def perturbation(): - while True: - yield 1 - - return perturbation - - qnspsa = QNSPSA(fidelity, maxiter=1, learning_rate=0.1, perturbation=get_perturbation()) - initial_point = 1.0 - result = qnspsa.minimize(objective, initial_point) - - expected_nfev = 8 # 7 * maxiter + 1 - self.assertEqual(result.nfev, expected_nfev) diff --git a/test/python/algorithms/optimizers/test_umda.py b/test/python/algorithms/optimizers/test_umda.py deleted file mode 100644 index 26952881de88..000000000000 --- a/test/python/algorithms/optimizers/test_umda.py +++ /dev/null @@ -1,98 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for the UMDA optimizer.""" - -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from scipy.optimize import rosen - -from qiskit.algorithms.optimizers.umda import UMDA -from qiskit.utils import algorithm_globals - - -class TestUMDA(QiskitAlgorithmsTestCase): - """Tests for the UMDA optimizer.""" - - def test_get_set(self): - """Test if getters and setters work as expected""" - umda = UMDA(maxiter=1, size_gen=20) - umda.disp = True - umda.size_gen = 30 - umda.alpha = 0.6 - umda.maxiter = 100 - - self.assertTrue(umda.disp) - self.assertEqual(umda.size_gen, 30) - self.assertEqual(umda.alpha, 0.6) - self.assertEqual(umda.maxiter, 100) - - def test_settings(self): - """Test if the settings display works well""" - umda = UMDA(maxiter=1, size_gen=20) - umda.disp = True - umda.size_gen = 30 - umda.alpha = 0.6 - umda.maxiter = 100 - - set_ = { - "maxiter": 100, - "alpha": 0.6, - "size_gen": 30, - "callback": None, - } - - self.assertEqual(umda.settings, set_) - - def test_minimize(self): - """optimize function test""" - # UMDA is volatile so we need to set the seeds for the execution - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 52 - - optimizer = UMDA(maxiter=1000, size_gen=100) - x_0 = [1.3, 0.7, 1.5] - res = optimizer.minimize(rosen, x_0) - - self.assertIsNotNone(res.fun) - self.assertEqual(len(res.x), len(x_0)) - np.testing.assert_array_almost_equal(res.x, [1.0] * len(x_0), decimal=2) - - def test_callback(self): - """Test the callback.""" - - def objective(x): - return np.linalg.norm(x) - 1 - - nfevs, parameters, fvals = [], [], [] - - def store_history(*args): - nfevs.append(args[0]) - parameters.append(args[1]) - fvals.append(args[2]) - - optimizer = UMDA(maxiter=1, callback=store_history) - _ = optimizer.minimize(objective, x0=np.arange(5)) - - self.assertEqual(len(nfevs), 1) - self.assertIsInstance(nfevs[0], int) - - self.assertEqual(len(parameters), 1) - self.assertIsInstance(parameters[0], np.ndarray) - self.assertEqual(parameters[0].size, 5) - - self.assertEqual(len(fvals), 1) - self.assertIsInstance(fvals[0], float) diff --git a/test/python/algorithms/optimizers/utils/__init__.py b/test/python/algorithms/optimizers/utils/__init__.py deleted file mode 100644 index f3adc3e3b4da..000000000000 --- a/test/python/algorithms/optimizers/utils/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Tests for Optimizer Utils.""" diff --git a/test/python/algorithms/optimizers/utils/test_learning_rate.py b/test/python/algorithms/optimizers/utils/test_learning_rate.py deleted file mode 100644 index 52acdbf98aaa..000000000000 --- a/test/python/algorithms/optimizers/utils/test_learning_rate.py +++ /dev/null @@ -1,54 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for LearningRate.""" - -from test.python.algorithms import QiskitAlgorithmsTestCase -import numpy as np -from qiskit.algorithms.optimizers.optimizer_utils import LearningRate - - -class TestLearningRate(QiskitAlgorithmsTestCase): - """Tests for the LearningRate class.""" - - def setUp(self): - super().setUp() - np.random.seed(12) - self.initial_point = np.array([1, 1, 1, 1, 0]) - - def objective(self, x): - """Objective Function for the tests""" - return (np.linalg.norm(x) - 1) ** 2 - - def test_learning_rate(self): - """ - Tests if the learning rate is initialized properly for each kind of input: - float, list and iterator. - """ - constant_learning_rate_input = 0.01 - list_learning_rate_input = [0.01 * n for n in range(10)] - generator_learning_rate_input = lambda: (el for el in list_learning_rate_input) - - with self.subTest("Check constant learning rate."): - constant_learning_rate = LearningRate(learning_rate=constant_learning_rate_input) - for _ in range(5): - self.assertEqual(constant_learning_rate_input, next(constant_learning_rate)) - - with self.subTest("Check learning rate list."): - list_learning_rate = LearningRate(learning_rate=list_learning_rate_input) - for i in range(5): - self.assertEqual(list_learning_rate_input[i], next(list_learning_rate)) - - with self.subTest("Check learning rate generator."): - generator_learning_rate = LearningRate(generator_learning_rate_input) - for i in range(5): - self.assertEqual(list_learning_rate_input[i], next(generator_learning_rate)) diff --git a/test/python/algorithms/state_fidelities/__init__.py b/test/python/algorithms/state_fidelities/__init__.py deleted file mode 100644 index d8b7d587c4cc..000000000000 --- a/test/python/algorithms/state_fidelities/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for the primitive-based fidelity interfaces.""" diff --git a/test/python/algorithms/state_fidelities/test_compute_uncompute.py b/test/python/algorithms/state_fidelities/test_compute_uncompute.py deleted file mode 100644 index f3b106d9b5d7..000000000000 --- a/test/python/algorithms/state_fidelities/test_compute_uncompute.py +++ /dev/null @@ -1,264 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for Fidelity.""" - -import unittest - -import numpy as np - -from qiskit.circuit import QuantumCircuit, ParameterVector -from qiskit.circuit.library import RealAmplitudes -from qiskit.primitives import Sampler -from qiskit.algorithms.state_fidelities import ComputeUncompute -from qiskit.test import QiskitTestCase - - -class TestComputeUncompute(QiskitTestCase): - """Test Compute-Uncompute Fidelity class""" - - def setUp(self): - super().setUp() - parameters = ParameterVector("x", 2) - - rx_rotations = QuantumCircuit(2) - rx_rotations.rx(parameters[0], 0) - rx_rotations.rx(parameters[1], 1) - - ry_rotations = QuantumCircuit(2) - ry_rotations.ry(parameters[0], 0) - ry_rotations.ry(parameters[1], 1) - - plus = QuantumCircuit(2) - plus.h([0, 1]) - - zero = QuantumCircuit(2) - - rx_rotation = QuantumCircuit(2) - rx_rotation.rx(parameters[0], 0) - rx_rotation.h(1) - - self._circuit = [rx_rotations, ry_rotations, plus, zero, rx_rotation] - self._sampler = Sampler() - self._left_params = np.array([[0, 0], [np.pi / 2, 0], [0, np.pi / 2], [np.pi, np.pi]]) - self._right_params = np.array([[0, 0], [0, 0], [np.pi / 2, 0], [0, 0]]) - - def test_1param_pair(self): - """test for fidelity with one pair of parameters""" - fidelity = ComputeUncompute(self._sampler) - job = fidelity.run( - self._circuit[0], self._circuit[1], self._left_params[0], self._right_params[0] - ) - result = job.result() - np.testing.assert_allclose(result.fidelities, np.array([1.0])) - - def test_1param_pair_local(self): - """test for fidelity with one pair of parameters""" - fidelity = ComputeUncompute(self._sampler, local=True) - job = fidelity.run( - self._circuit[0], self._circuit[1], self._left_params[0], self._right_params[0] - ) - result = job.result() - np.testing.assert_allclose(result.fidelities, np.array([1.0])) - - def test_local(self): - """test difference between local and global fidelity""" - fidelity_global = ComputeUncompute(self._sampler, local=False) - fidelity_local = ComputeUncompute(self._sampler, local=True) - fidelities = [] - for fidelity in [fidelity_global, fidelity_local]: - job = fidelity.run(self._circuit[2], self._circuit[3]) - result = job.result() - fidelities.append(result.fidelities[0]) - np.testing.assert_allclose(fidelities, np.array([0.25, 0.5]), atol=1e-16) - - def test_4param_pairs(self): - """test for fidelity with four pairs of parameters""" - fidelity = ComputeUncompute(self._sampler) - n = len(self._left_params) - job = fidelity.run( - [self._circuit[0]] * n, [self._circuit[1]] * n, self._left_params, self._right_params - ) - results = job.result() - np.testing.assert_allclose(results.fidelities, np.array([1.0, 0.5, 0.25, 0.0]), atol=1e-16) - - def test_symmetry(self): - """test for fidelity with the same circuit""" - fidelity = ComputeUncompute(self._sampler) - n = len(self._left_params) - job_1 = fidelity.run( - [self._circuit[0]] * n, [self._circuit[0]] * n, self._left_params, self._right_params - ) - job_2 = fidelity.run( - [self._circuit[0]] * n, [self._circuit[0]] * n, self._right_params, self._left_params - ) - results_1 = job_1.result() - results_2 = job_2.result() - np.testing.assert_allclose(results_1.fidelities, results_2.fidelities, atol=1e-16) - - def test_no_params(self): - """test for fidelity without parameters""" - fidelity = ComputeUncompute(self._sampler) - job = fidelity.run([self._circuit[2]], [self._circuit[3]]) - results = job.result() - np.testing.assert_allclose(results.fidelities, np.array([0.25]), atol=1e-16) - - job = fidelity.run([self._circuit[2]], [self._circuit[3]], [], []) - results = job.result() - np.testing.assert_allclose(results.fidelities, np.array([0.25]), atol=1e-16) - - def test_left_param(self): - """test for fidelity with only left parameters""" - fidelity = ComputeUncompute(self._sampler) - n = len(self._left_params) - job = fidelity.run( - [self._circuit[1]] * n, [self._circuit[3]] * n, values_1=self._left_params - ) - results = job.result() - np.testing.assert_allclose(results.fidelities, np.array([1.0, 0.5, 0.5, 0.0]), atol=1e-16) - - def test_right_param(self): - """test for fidelity with only right parameters""" - fidelity = ComputeUncompute(self._sampler) - n = len(self._left_params) - job = fidelity.run( - [self._circuit[3]] * n, [self._circuit[1]] * n, values_2=self._left_params - ) - results = job.result() - np.testing.assert_allclose(results.fidelities, np.array([1.0, 0.5, 0.5, 0.0]), atol=1e-16) - - def test_not_set_circuits(self): - """test for fidelity with no circuits.""" - fidelity = ComputeUncompute(self._sampler) - with self.assertRaises(TypeError): - job = fidelity.run( - circuits_1=None, - circuits_2=None, - values_1=self._left_params, - values_2=self._right_params, - ) - job.result() - - def test_circuit_mismatch(self): - """test for fidelity with different number of left/right circuits.""" - fidelity = ComputeUncompute(self._sampler) - n = len(self._left_params) - with self.assertRaises(ValueError): - job = fidelity.run( - [self._circuit[0]] * n, - [self._circuit[1]] * (n + 1), - self._left_params, - self._right_params, - ) - job.result() - - def test_asymmetric_params(self): - """test for fidelity when the 2 circuits have different number of - left/right parameters.""" - - fidelity = ComputeUncompute(self._sampler) - n = len(self._left_params) - right_params = [[p] for p in self._right_params[:, 0]] - job = fidelity.run( - [self._circuit[0]] * n, [self._circuit[4]] * n, self._left_params, right_params - ) - result = job.result() - np.testing.assert_allclose(result.fidelities, np.array([0.5, 0.25, 0.25, 0.0]), atol=1e-16) - - def test_input_format(self): - """test for different input format variations""" - - fidelity = ComputeUncompute(self._sampler) - circuit = RealAmplitudes(2) - values = np.random.random(circuit.num_parameters) - shift = np.ones_like(values) * 0.01 - - # lists of circuits, lists of numpy arrays - job = fidelity.run([circuit], [circuit], [values], [values + shift]) - result_1 = job.result() - - # lists of circuits, lists of lists - shift_val = values + shift - job = fidelity.run([circuit], [circuit], [values.tolist()], [shift_val.tolist()]) - result_2 = job.result() - - # circuits, lists - shift_val = values + shift - job = fidelity.run(circuit, circuit, values.tolist(), shift_val.tolist()) - result_3 = job.result() - - # circuits, np.arrays - job = fidelity.run(circuit, circuit, values, values + shift) - result_4 = job.result() - - np.testing.assert_allclose(result_1.fidelities, result_2.fidelities, atol=1e-16) - np.testing.assert_allclose(result_1.fidelities, result_3.fidelities, atol=1e-16) - np.testing.assert_allclose(result_1.fidelities, result_4.fidelities, atol=1e-16) - - def test_input_measurements(self): - """test for fidelity with measurements on input circuits""" - fidelity = ComputeUncompute(self._sampler) - circuit_1 = self._circuit[0] - circuit_1.measure_all() - circuit_2 = self._circuit[1] - circuit_2.measure_all() - - job = fidelity.run(circuit_1, circuit_2, self._left_params[0], self._right_params[0]) - result = job.result() - np.testing.assert_allclose(result.fidelities, np.array([1.0])) - - def test_options(self): - """Test fidelity's run options""" - sampler_shots = Sampler(options={"shots": 1024}) - - with self.subTest("sampler"): - # Only options in sampler - fidelity = ComputeUncompute(sampler_shots) - options = fidelity.options - job = fidelity.run(self._circuit[2], self._circuit[3]) - result = job.result() - self.assertEqual(options.__dict__, {"shots": 1024}) - self.assertEqual(result.options.__dict__, {"shots": 1024}) - - with self.subTest("fidelity init"): - # Fidelity default options override sampler - # options and add new fields - fidelity = ComputeUncompute(sampler_shots, options={"shots": 2048, "dummy": 100}) - options = fidelity.options - job = fidelity.run(self._circuit[2], self._circuit[3]) - result = job.result() - self.assertEqual(options.__dict__, {"shots": 2048, "dummy": 100}) - self.assertEqual(result.options.__dict__, {"shots": 2048, "dummy": 100}) - - with self.subTest("fidelity update"): - # Update fidelity options - fidelity = ComputeUncompute(sampler_shots, options={"shots": 2048, "dummy": 100}) - fidelity.update_default_options(shots=100) - options = fidelity.options - job = fidelity.run(self._circuit[2], self._circuit[3]) - result = job.result() - self.assertEqual(options.__dict__, {"shots": 100, "dummy": 100}) - self.assertEqual(result.options.__dict__, {"shots": 100, "dummy": 100}) - - with self.subTest("fidelity run"): - # Run options override fidelity options - fidelity = ComputeUncompute(sampler_shots, options={"shots": 2048, "dummy": 100}) - job = fidelity.run(self._circuit[2], self._circuit[3], shots=50, dummy=None) - options = fidelity.options - result = job.result() - # Only default + sampler options. Not run. - self.assertEqual(options.__dict__, {"shots": 2048, "dummy": 100}) - self.assertEqual(result.options.__dict__, {"shots": 50, "dummy": None}) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_amplitude_estimators.py b/test/python/algorithms/test_amplitude_estimators.py deleted file mode 100644 index ea0a1af099ee..000000000000 --- a/test/python/algorithms/test_amplitude_estimators.py +++ /dev/null @@ -1,724 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test the quantum amplitude estimation algorithm.""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -import numpy as np -from ddt import ddt, idata, data, unpack -from qiskit import QuantumRegister, QuantumCircuit, BasicAer -from qiskit.circuit.library import QFT, GroverOperator -from qiskit.utils import QuantumInstance -from qiskit.algorithms import ( - AmplitudeEstimation, - MaximumLikelihoodAmplitudeEstimation, - IterativeAmplitudeEstimation, - FasterAmplitudeEstimation, - EstimationProblem, -) -from qiskit.quantum_info import Operator, Statevector -from qiskit.primitives import Sampler - - -class BernoulliStateIn(QuantumCircuit): - """A circuit preparing sqrt(1 - p)|0> + sqrt(p)|1>.""" - - def __init__(self, probability): - super().__init__(1) - angle = 2 * np.arcsin(np.sqrt(probability)) - self.ry(angle, 0) - - -class BernoulliGrover(QuantumCircuit): - """The Grover operator corresponding to the Bernoulli A operator.""" - - def __init__(self, probability): - super().__init__(1, global_phase=np.pi) - self.angle = 2 * np.arcsin(np.sqrt(probability)) - self.ry(2 * self.angle, 0) - - def power(self, power, matrix_power=False): - if matrix_power: - return super().power(power, True) - - powered = QuantumCircuit(1) - powered.ry(power * 2 * self.angle, 0) - return powered - - -class SineIntegral(QuantumCircuit): - r"""Construct the A operator to approximate the integral - - \int_0^1 \sin^2(x) d x - - with a specified number of qubits. - """ - - def __init__(self, num_qubits): - qr_state = QuantumRegister(num_qubits, "state") - qr_objective = QuantumRegister(1, "obj") - super().__init__(qr_state, qr_objective) - - # prepare 1/sqrt{2^n} sum_x |x>_n - self.h(qr_state) - - # apply the sine/cosine term - self.ry(2 * 1 / 2 / 2**num_qubits, qr_objective[0]) - for i, qubit in enumerate(qr_state): - self.cry(2 * 2**i / 2**num_qubits, qubit, qr_objective[0]) - - -@ddt -class TestBernoulli(QiskitAlgorithmsTestCase): - """Tests based on the Bernoulli A operator. - - This class tests - * the estimation result - * the constructed circuits - """ - - def setUp(self): - super().setUp() - - with self.assertWarns(DeprecationWarning): - self._statevector = QuantumInstance( - backend=BasicAer.get_backend("statevector_simulator"), - seed_simulator=2, - seed_transpiler=2, - ) - - self._sampler = Sampler(options={"seed": 2}) - - def qasm(shots=100): - with self.assertWarns(DeprecationWarning): - qi = QuantumInstance( - backend=BasicAer.get_backend("qasm_simulator"), - shots=shots, - seed_simulator=2, - seed_transpiler=2, - ) - return qi - - self._qasm = qasm - - def sampler_shots(shots=100): - return Sampler(options={"shots": shots, "seed": 2}) - - self._sampler_shots = sampler_shots - - @idata( - [ - [0.2, AmplitudeEstimation(2), {"estimation": 0.5, "mle": 0.2}], - [0.49, AmplitudeEstimation(3), {"estimation": 0.5, "mle": 0.49}], - [0.2, MaximumLikelihoodAmplitudeEstimation([0, 1, 2]), {"estimation": 0.2}], - [0.49, MaximumLikelihoodAmplitudeEstimation(3), {"estimation": 0.49}], - [0.2, IterativeAmplitudeEstimation(0.1, 0.1), {"estimation": 0.2}], - [0.49, IterativeAmplitudeEstimation(0.001, 0.01), {"estimation": 0.49}], - [0.2, FasterAmplitudeEstimation(0.1, 3, rescale=False), {"estimation": 0.2}], - [0.12, FasterAmplitudeEstimation(0.1, 2, rescale=False), {"estimation": 0.12}], - ] - ) - @unpack - def test_statevector(self, prob, qae, expect): - """statevector test""" - - problem = EstimationProblem(BernoulliStateIn(prob), 0, BernoulliGrover(prob)) - - with self.assertWarns(DeprecationWarning): - qae.quantum_instance = self._statevector - result = qae.estimate(problem) - - self._statevector.reset_execution_results() - for key, value in expect.items(): - self.assertAlmostEqual( - value, getattr(result, key), places=3, msg=f"estimate `{key}` failed" - ) - - @idata( - [ - [0.2, AmplitudeEstimation(2), {"estimation": 0.5, "mle": 0.2}], - [0.49, AmplitudeEstimation(3), {"estimation": 0.5, "mle": 0.49}], - [0.2, MaximumLikelihoodAmplitudeEstimation([0, 1, 2]), {"estimation": 0.2}], - [0.49, MaximumLikelihoodAmplitudeEstimation(3), {"estimation": 0.49}], - [0.2, IterativeAmplitudeEstimation(0.1, 0.1), {"estimation": 0.2}], - [0.49, IterativeAmplitudeEstimation(0.001, 0.01), {"estimation": 0.49}], - [0.2, FasterAmplitudeEstimation(0.1, 3, rescale=False), {"estimation": 0.199}], - [0.12, FasterAmplitudeEstimation(0.1, 2, rescale=False), {"estimation": 0.12}], - ] - ) - @unpack - def test_sampler(self, prob, qae, expect): - """sampler test""" - qae.sampler = self._sampler - problem = EstimationProblem(BernoulliStateIn(prob), 0, BernoulliGrover(prob)) - - result = qae.estimate(problem) - for key, value in expect.items(): - self.assertAlmostEqual( - value, getattr(result, key), places=3, msg=f"estimate `{key}` failed" - ) - - @idata( - [ - [0.2, 100, AmplitudeEstimation(4), {"estimation": 0.14644, "mle": 0.193888}], - [0.0, 1000, AmplitudeEstimation(2), {"estimation": 0.0, "mle": 0.0}], - [ - 0.2, - 100, - MaximumLikelihoodAmplitudeEstimation([0, 1, 2, 4, 8]), - {"estimation": 0.199606}, - ], - [0.8, 10, IterativeAmplitudeEstimation(0.1, 0.05), {"estimation": 0.811711}], - [0.2, 1000, FasterAmplitudeEstimation(0.1, 3, rescale=False), {"estimation": 0.198640}], - [ - 0.12, - 100, - FasterAmplitudeEstimation(0.01, 3, rescale=False), - {"estimation": 0.119037}, - ], - ] - ) - @unpack - def test_qasm(self, prob, shots, qae, expect): - """qasm test""" - - with self.assertWarns(DeprecationWarning): - qae.quantum_instance = self._qasm(shots) - problem = EstimationProblem(BernoulliStateIn(prob), [0], BernoulliGrover(prob)) - result = qae.estimate(problem) - - for key, value in expect.items(): - self.assertAlmostEqual( - value, getattr(result, key), places=3, msg=f"estimate `{key}` failed" - ) - - @idata( - [ - [0.2, 100, AmplitudeEstimation(4), {"estimation": 0.14644, "mle": 0.198783}], - [0.0, 1000, AmplitudeEstimation(2), {"estimation": 0.0, "mle": 0.0}], - [ - 0.2, - 100, - MaximumLikelihoodAmplitudeEstimation([0, 1, 2, 4, 8]), - {"estimation": 0.200308}, - ], - [0.8, 10, IterativeAmplitudeEstimation(0.1, 0.05), {"estimation": 0.811711}], - [0.2, 1000, FasterAmplitudeEstimation(0.1, 3, rescale=False), {"estimation": 0.198640}], - [ - 0.12, - 100, - FasterAmplitudeEstimation(0.01, 3, rescale=False), - {"estimation": 0.120017}, - ], - ] - ) - @unpack - def test_sampler_with_shots(self, prob, shots, qae, expect): - """sampler with shots test""" - qae.sampler = self._sampler_shots(shots) - problem = EstimationProblem(BernoulliStateIn(prob), [0], BernoulliGrover(prob)) - - result = qae.estimate(problem) - for key, value in expect.items(): - self.assertAlmostEqual( - value, getattr(result, key), places=3, msg=f"estimate `{key}` failed" - ) - - @data(True, False) - def test_qae_circuit(self, efficient_circuit): - """Test circuits resulting from canonical amplitude estimation. - - Build the circuit manually and from the algorithm and compare the resulting unitaries. - """ - prob = 0.5 - - problem = EstimationProblem(BernoulliStateIn(prob), objective_qubits=[0]) - for m in [2, 5]: - qae = AmplitudeEstimation(m) - angle = 2 * np.arcsin(np.sqrt(prob)) - - # manually set up the inefficient AE circuit - qr_eval = QuantumRegister(m, "a") - qr_objective = QuantumRegister(1, "q") - circuit = QuantumCircuit(qr_eval, qr_objective) - - # initial Hadamard gates - for i in range(m): - circuit.h(qr_eval[i]) - - # A operator - circuit.ry(angle, qr_objective) - - if efficient_circuit: - qae.grover_operator = BernoulliGrover(prob) - for power in range(m): - circuit.cry(2 * 2**power * angle, qr_eval[power], qr_objective[0]) - else: - oracle = QuantumCircuit(1) - oracle.z(0) - - state_preparation = QuantumCircuit(1) - state_preparation.ry(angle, 0) - grover_op = GroverOperator(oracle, state_preparation) - for power in range(m): - circuit.compose( - grover_op.power(2**power).control(), - qubits=[qr_eval[power], qr_objective[0]], - inplace=True, - ) - - # fourier transform - iqft = QFT(m, do_swaps=False).inverse().reverse_bits() - circuit.append(iqft.to_instruction(), qr_eval) - - actual_circuit = qae.construct_circuit(problem, measurement=False) - - self.assertEqual(Operator(circuit), Operator(actual_circuit)) - - @data(True, False) - def test_iqae_circuits(self, efficient_circuit): - """Test circuits resulting from iterative amplitude estimation. - - Build the circuit manually and from the algorithm and compare the resulting unitaries. - """ - prob = 0.5 - problem = EstimationProblem(BernoulliStateIn(prob), objective_qubits=[0]) - - for k in [2, 5]: - qae = IterativeAmplitudeEstimation(0.01, 0.05) - angle = 2 * np.arcsin(np.sqrt(prob)) - - # manually set up the inefficient AE circuit - q_objective = QuantumRegister(1, "q") - circuit = QuantumCircuit(q_objective) - - # A operator - circuit.ry(angle, q_objective) - - if efficient_circuit: - qae.grover_operator = BernoulliGrover(prob) - circuit.ry(2 * k * angle, q_objective[0]) - - else: - oracle = QuantumCircuit(1) - oracle.z(0) - state_preparation = QuantumCircuit(1) - state_preparation.ry(angle, 0) - grover_op = GroverOperator(oracle, state_preparation) - for _ in range(k): - circuit.compose(grover_op, inplace=True) - - actual_circuit = qae.construct_circuit(problem, k, measurement=False) - self.assertEqual(Operator(circuit), Operator(actual_circuit)) - - @data(True, False) - def test_mlae_circuits(self, efficient_circuit): - """Test the circuits constructed for MLAE""" - prob = 0.5 - problem = EstimationProblem(BernoulliStateIn(prob), objective_qubits=[0]) - - for k in [2, 5]: - qae = MaximumLikelihoodAmplitudeEstimation(k) - angle = 2 * np.arcsin(np.sqrt(prob)) - - # compute all the circuits used for MLAE - circuits = [] - - # 0th power - q_objective = QuantumRegister(1, "q") - circuit = QuantumCircuit(q_objective) - circuit.ry(angle, q_objective) - circuits += [circuit] - - # powers of 2 - for power in range(k): - q_objective = QuantumRegister(1, "q") - circuit = QuantumCircuit(q_objective) - - # A operator - circuit.ry(angle, q_objective) - - # Q^(2^j) operator - if efficient_circuit: - qae.grover_operator = BernoulliGrover(prob) - circuit.ry(2 * 2**power * angle, q_objective[0]) - - else: - oracle = QuantumCircuit(1) - oracle.z(0) - state_preparation = QuantumCircuit(1) - state_preparation.ry(angle, 0) - grover_op = GroverOperator(oracle, state_preparation) - for _ in range(2**power): - circuit.compose(grover_op, inplace=True) - circuits += [circuit] - - actual_circuits = qae.construct_circuits(problem, measurement=False) - - for actual, expected in zip(actual_circuits, circuits): - self.assertEqual(Operator(actual), Operator(expected)) - - -@ddt -class TestSineIntegral(QiskitAlgorithmsTestCase): - """Tests based on the A operator to integrate sin^2(x). - - This class tests - * the estimation result - * the confidence intervals - """ - - def setUp(self): - super().setUp() - - with self.assertWarns(DeprecationWarning): - self._statevector = QuantumInstance( - backend=BasicAer.get_backend("statevector_simulator"), - seed_simulator=123, - seed_transpiler=41, - ) - - self._sampler = Sampler(options={"seed": 123}) - - def qasm(shots=100): - with self.assertWarns(DeprecationWarning): - qi = QuantumInstance( - backend=BasicAer.get_backend("qasm_simulator"), - shots=shots, - seed_simulator=7192, - seed_transpiler=90000, - ) - return qi - - self._qasm = qasm - - def sampler_shots(shots=100): - return Sampler(options={"shots": shots, "seed": 7192}) - - self._sampler_shots = sampler_shots - - @idata( - [ - [2, AmplitudeEstimation(2), {"estimation": 0.5, "mle": 0.270290}], - [4, MaximumLikelihoodAmplitudeEstimation(4), {"estimation": 0.272675}], - [3, IterativeAmplitudeEstimation(0.1, 0.1), {"estimation": 0.272082}], - [3, FasterAmplitudeEstimation(0.01, 1), {"estimation": 0.272082}], - ] - ) - @unpack - def test_statevector(self, n, qae, expect): - """Statevector end-to-end test""" - # construct factories for A and Q - # qae.state_preparation = SineIntegral(n) - estimation_problem = EstimationProblem(SineIntegral(n), objective_qubits=[n]) - - with self.assertWarns(DeprecationWarning): - qae.quantum_instance = self._statevector - # result = qae.run(self._statevector) - result = qae.estimate(estimation_problem) - - self._statevector.reset_execution_results() - for key, value in expect.items(): - self.assertAlmostEqual( - value, getattr(result, key), places=3, msg=f"estimate `{key}` failed" - ) - - @idata( - [ - [2, AmplitudeEstimation(2), {"estimation": 0.5, "mle": 0.2702}], - [4, MaximumLikelihoodAmplitudeEstimation(4), {"estimation": 0.2725}], - [3, IterativeAmplitudeEstimation(0.1, 0.1), {"estimation": 0.2721}], - [3, FasterAmplitudeEstimation(0.01, 1), {"estimation": 0.2792}], - ] - ) - @unpack - def test_sampler(self, n, qae, expect): - """sampler end-to-end test""" - # construct factories for A and Q - # qae.state_preparation = SineIntegral(n) - qae.sampler = self._sampler - estimation_problem = EstimationProblem(SineIntegral(n), objective_qubits=[n]) - - result = qae.estimate(estimation_problem) - for key, value in expect.items(): - self.assertAlmostEqual( - value, getattr(result, key), places=3, msg=f"estimate `{key}` failed" - ) - - @idata( - [ - [4, 100, AmplitudeEstimation(2), {"estimation": 0.5, "mle": 0.281196}], - [3, 10, MaximumLikelihoodAmplitudeEstimation(2), {"estimation": 0.256878}], - [3, 1000, IterativeAmplitudeEstimation(0.01, 0.01), {"estimation": 0.271790}], - [3, 1000, FasterAmplitudeEstimation(0.1, 4), {"estimation": 0.274168}], - ] - ) - @unpack - def test_qasm(self, n, shots, qae, expect): - """QASM simulator end-to-end test.""" - estimation_problem = EstimationProblem(SineIntegral(n), objective_qubits=[n]) - - with self.assertWarns(DeprecationWarning): - qae.quantum_instance = self._qasm(shots) - result = qae.estimate(estimation_problem) - - for key, value in expect.items(): - self.assertAlmostEqual( - value, getattr(result, key), places=3, msg=f"estimate `{key}` failed" - ) - - @idata( - [ - [4, 1000, AmplitudeEstimation(2), {"estimation": 0.5, "mle": 0.2636}], - [3, 10, MaximumLikelihoodAmplitudeEstimation(2), {"estimation": 0.2904}], - [3, 1000, IterativeAmplitudeEstimation(0.01, 0.01), {"estimation": 0.2706}], - [3, 1000, FasterAmplitudeEstimation(0.1, 4), {"estimation": 0.2764}], - ] - ) - @unpack - def test_sampler_with_shots(self, n, shots, qae, expect): - """Sampler with shots end-to-end test.""" - # construct factories for A and Q - qae.sampler = self._sampler_shots(shots) - estimation_problem = EstimationProblem(SineIntegral(n), objective_qubits=[n]) - - result = qae.estimate(estimation_problem) - for key, value in expect.items(): - self.assertAlmostEqual( - value, getattr(result, key), places=3, msg=f"estimate `{key}` failed" - ) - - @idata( - [ - [ - AmplitudeEstimation(3), - "mle", - { - "likelihood_ratio": (0.2494734, 0.3003771), - "fisher": (0.2486176, 0.2999286), - "observed_fisher": (0.2484562, 0.3000900), - }, - ], - [ - MaximumLikelihoodAmplitudeEstimation(3), - "estimation", - { - "likelihood_ratio": (0.2598794, 0.2798536), - "fisher": (0.2584889, 0.2797018), - "observed_fisher": (0.2659279, 0.2722627), - }, - ], - ] - ) - @unpack - def test_confidence_intervals(self, qae, key, expect): - """End-to-end test for all confidence intervals.""" - n = 3 - - estimation_problem = EstimationProblem(SineIntegral(n), objective_qubits=[n]) - with self.assertWarns(DeprecationWarning): - qae.quantum_instance = self._statevector - # statevector simulator - result = qae.estimate(estimation_problem) - - self._statevector.reset_execution_results() - methods = ["lr", "fi", "oi"] # short for likelihood_ratio, fisher, observed_fisher - alphas = [0.1, 0.00001, 0.9] # alpha shouldn't matter in statevector - for alpha, method in zip(alphas, methods): - confint = qae.compute_confidence_interval(result, alpha, method) - # confidence interval based on statevector should be empty, as we are sure of the result - self.assertAlmostEqual(confint[1] - confint[0], 0.0) - self.assertAlmostEqual(confint[0], getattr(result, key)) - - # qasm simulator - shots = 100 - alpha = 0.01 - with self.assertWarns(DeprecationWarning): - qae.quantum_instance = self._qasm(shots) - result = qae.estimate(estimation_problem) - - for method, expected_confint in expect.items(): - confint = qae.compute_confidence_interval(result, alpha, method) - np.testing.assert_array_almost_equal(confint, expected_confint) - self.assertTrue(confint[0] <= getattr(result, key) <= confint[1]) - - def test_iqae_confidence_intervals(self): - """End-to-end test for the IQAE confidence interval.""" - n = 3 - expected_confint = (0.1984050, 0.3511015) - estimation_problem = EstimationProblem(SineIntegral(n), objective_qubits=[n]) - - with self.assertWarns(DeprecationWarning): - qae = IterativeAmplitudeEstimation(0.1, 0.01, quantum_instance=self._statevector) - # statevector simulator - result = qae.estimate(estimation_problem) - - self._statevector.reset_execution_results() - confint = result.confidence_interval - # confidence interval based on statevector should be empty, as we are sure of the result - self.assertAlmostEqual(confint[1] - confint[0], 0.0) - self.assertAlmostEqual(confint[0], result.estimation) - - # qasm simulator - shots = 100 - - with self.assertWarns(DeprecationWarning): - qae.quantum_instance = self._qasm(shots) - result = qae.estimate(estimation_problem) - - confint = result.confidence_interval - np.testing.assert_array_almost_equal(confint, expected_confint) - self.assertTrue(confint[0] <= result.estimation <= confint[1]) - - -class TestAmplitudeEstimation(QiskitAlgorithmsTestCase): - """Specific tests for canonical AE.""" - - def test_warns_if_good_state_set(self): - """Check AE warns if is_good_state is set.""" - circuit = QuantumCircuit(1) - problem = EstimationProblem(circuit, objective_qubits=[0], is_good_state=lambda x: True) - - qae = AmplitudeEstimation(num_eval_qubits=1, sampler=Sampler()) - - with self.assertWarns(Warning): - _ = qae.estimate(problem) - - -@ddt -class TestFasterAmplitudeEstimation(QiskitAlgorithmsTestCase): - """Specific tests for Faster AE.""" - - def setUp(self): - super().setUp() - self._sampler = Sampler(options={"seed": 2}) - - def test_rescaling(self): - """Test the rescaling.""" - amplitude = 0.8 - scaling = 0.25 - circuit = QuantumCircuit(1) - circuit.ry(2 * np.arcsin(amplitude), 0) - problem = EstimationProblem(circuit, objective_qubits=[0]) - - rescaled = problem.rescale(scaling) - rescaled_amplitude = Statevector.from_instruction(rescaled.state_preparation).data[3] - - self.assertAlmostEqual(scaling * amplitude, rescaled_amplitude) - - def test_run_without_rescaling(self): - """Run Faster AE without rescaling if the amplitude is in [0, 1/4].""" - # construct estimation problem - prob = 0.11 - a_op = QuantumCircuit(1) - a_op.ry(2 * np.arcsin(np.sqrt(prob)), 0) - problem = EstimationProblem(a_op, objective_qubits=[0]) - - # construct algo without rescaling - backend = BasicAer.get_backend("statevector_simulator") - - with self.assertWarns(DeprecationWarning): - fae = FasterAmplitudeEstimation(0.1, 1, rescale=False, quantum_instance=backend) - - # run the algo - result = fae.estimate(problem) - - # assert the result is correct - self.assertAlmostEqual(result.estimation, prob) - - # assert no rescaling was used - theta = np.mean(result.theta_intervals[-1]) - value_without_scaling = np.sin(theta) ** 2 - self.assertAlmostEqual(result.estimation, value_without_scaling) - - def test_sampler_run_without_rescaling(self): - """Run Faster AE without rescaling if the amplitude is in [0, 1/4].""" - # construct estimation problem - prob = 0.11 - a_op = QuantumCircuit(1) - a_op.ry(2 * np.arcsin(np.sqrt(prob)), 0) - problem = EstimationProblem(a_op, objective_qubits=[0]) - - # construct algo without rescaling - fae = FasterAmplitudeEstimation(0.1, 1, rescale=False, sampler=self._sampler) - - # run the algo - result = fae.estimate(problem) - - # assert the result is correct - self.assertAlmostEqual(result.estimation, prob, places=2) - - # assert no rescaling was used - theta = np.mean(result.theta_intervals[-1]) - value_without_scaling = np.sin(theta) ** 2 - self.assertAlmostEqual(result.estimation, value_without_scaling) - - def test_rescaling_with_custom_grover_raises(self): - """Test that the rescaling option fails if a custom Grover operator is used.""" - prob = 0.8 - a_op = BernoulliStateIn(prob) - q_op = BernoulliGrover(prob) - problem = EstimationProblem(a_op, objective_qubits=[0], grover_operator=q_op) - - # construct algo without rescaling - backend = BasicAer.get_backend("statevector_simulator") - with self.assertWarns(DeprecationWarning): - fae = FasterAmplitudeEstimation(0.1, 1, quantum_instance=backend) - - # run the algo - with self.assertWarns(Warning): - _ = fae.estimate(problem) - - @data(("statevector_simulator", 0.2), ("qasm_simulator", 0.199440)) - @unpack - def test_good_state(self, backend_str, expect): - """Test with a good state function.""" - - def is_good_state(bitstr): - return bitstr[1] == "1" - - # construct the estimation problem where the second qubit is ignored - a_op = QuantumCircuit(2) - a_op.ry(2 * np.arcsin(np.sqrt(0.2)), 0) - - # oracle only affects first qubit - oracle = QuantumCircuit(2) - oracle.z(0) - - # reflect only on first qubit - q_op = GroverOperator(oracle, a_op, reflection_qubits=[0]) - - # but we measure both qubits (hence both are objective qubits) - problem = EstimationProblem( - a_op, objective_qubits=[0, 1], grover_operator=q_op, is_good_state=is_good_state - ) - - # construct algo - with self.assertWarns(DeprecationWarning): - backend = QuantumInstance( - BasicAer.get_backend(backend_str), seed_simulator=2, seed_transpiler=2 - ) - # cannot use rescaling with a custom grover operator - - with self.assertWarns(DeprecationWarning): - fae = FasterAmplitudeEstimation(0.01, 5, rescale=False, quantum_instance=backend) - - # run the algo - result = fae.estimate(problem) - - # assert the result is correct - self.assertAlmostEqual(result.estimation, expect, places=5) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_aux_ops_evaluator.py b/test/python/algorithms/test_aux_ops_evaluator.py deleted file mode 100644 index bff0b07c09c9..000000000000 --- a/test/python/algorithms/test_aux_ops_evaluator.py +++ /dev/null @@ -1,197 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Tests evaluator of auxiliary operators for algorithms.""" - -import unittest -import warnings -from typing import Tuple, Union - -from test.python.algorithms import QiskitAlgorithmsTestCase -import numpy as np -from ddt import ddt, data - -from qiskit.algorithms.list_or_dict import ListOrDict -from qiskit.providers import Backend -from qiskit.quantum_info import Statevector -from qiskit.algorithms import eval_observables -from qiskit import BasicAer, QuantumCircuit -from qiskit.circuit.library import EfficientSU2 -from qiskit.opflow import ( - PauliSumOp, - X, - Z, - I, - ExpectationFactory, - OperatorBase, - ExpectationBase, - StateFn, -) -from qiskit.utils import QuantumInstance, algorithm_globals - - -@ddt -class TestAuxOpsEvaluator(QiskitAlgorithmsTestCase): - """Tests evaluator of auxiliary operators for algorithms.""" - - def setUp(self): - super().setUp() - self.seed = 50 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - with self.assertWarns(DeprecationWarning): - self.h2_op = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - - self.threshold = 1e-8 - self.backend_names = ["statevector_simulator", "qasm_simulator"] - - def get_exact_expectation(self, ansatz: QuantumCircuit, observables: ListOrDict[OperatorBase]): - """ - Calculates the exact expectation to be used as an expected result for unit tests. - """ - if isinstance(observables, dict): - observables_list = list(observables.values()) - else: - observables_list = observables - - # the exact value is a list of (mean, variance) where we expect 0 variance - exact = [ - (Statevector(ansatz).expectation_value(observable), 0) - for observable in observables_list - ] - - if isinstance(observables, dict): - return dict(zip(observables.keys(), exact)) - - return exact - - def _run_test( - self, - expected_result: ListOrDict[Tuple[complex, complex]], - quantum_state: Union[QuantumCircuit, Statevector], - decimal: int, - expectation: ExpectationBase, - observables: ListOrDict[OperatorBase], - quantum_instance: Union[QuantumInstance, Backend], - ): - - with self.assertWarns(DeprecationWarning): - result = eval_observables( - quantum_instance, quantum_state, observables, expectation, self.threshold - ) - - if isinstance(observables, dict): - np.testing.assert_equal(list(result.keys()), list(expected_result.keys())) - np.testing.assert_array_almost_equal( - list(result.values()), list(expected_result.values()), decimal=decimal - ) - else: - np.testing.assert_array_almost_equal(result, expected_result, decimal=decimal) - - @data( - [ - PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), - PauliSumOp.from_list([("II", 2.0)]), - ], - [ - PauliSumOp.from_list([("ZZ", 2.0)]), - ], - { - "op1": PauliSumOp.from_list([("II", 2.0)]), - "op2": PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), - }, - { - "op1": PauliSumOp.from_list([("ZZ", 2.0)]), - }, - ) - def test_eval_observables(self, observables: ListOrDict[OperatorBase]): - """Tests evaluator of auxiliary operators for algorithms.""" - - ansatz = EfficientSU2(2) - parameters = np.array( - [1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], - dtype=float, - ) - - bound_ansatz = ansatz.assign_parameters(parameters) - expected_result = self.get_exact_expectation(bound_ansatz, observables) - - for backend_name in self.backend_names: - shots = 4096 if backend_name == "qasm_simulator" else 1 - decimal = ( - 1 if backend_name == "qasm_simulator" else 6 - ) # to accommodate for qasm being imperfect - with self.subTest(msg=f"Test {backend_name} backend."): - backend = BasicAer.get_backend(backend_name) - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=backend, - shots=shots, - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - expectation = ExpectationFactory.build( - operator=self.h2_op, - backend=quantum_instance, - ) - - with self.subTest(msg="Test QuantumCircuit."): - self._run_test( - expected_result, - bound_ansatz, - decimal, - expectation, - observables, - quantum_instance, - ) - - with self.subTest(msg="Test QuantumCircuit with Backend."): - self._run_test( - expected_result, - bound_ansatz, - decimal, - expectation, - observables, - backend, - ) - - with self.subTest(msg="Test Statevector."): - statevector = Statevector(bound_ansatz) - self._run_test( - expected_result, - statevector, - decimal, - expectation, - observables, - quantum_instance, - ) - with self.assertWarns(DeprecationWarning): - with self.subTest(msg="Test StateFn."): - statefn = StateFn(bound_ansatz) - self._run_test( - expected_result, - statefn, - decimal, - expectation, - observables, - quantum_instance, - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_backendv1.py b/test/python/algorithms/test_backendv1.py deleted file mode 100644 index d0a544834c72..000000000000 --- a/test/python/algorithms/test_backendv1.py +++ /dev/null @@ -1,148 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Providers that support BackendV1 interface""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit import QuantumCircuit -from qiskit.providers.fake_provider import FakeProvider -from qiskit.utils import QuantumInstance, algorithm_globals -from qiskit.algorithms import VQE, Grover, AmplificationProblem -from qiskit.opflow import X, Z, I -from qiskit.algorithms.optimizers import SPSA -from qiskit.circuit.library import TwoLocal, EfficientSU2 -from qiskit.utils.mitigation import CompleteMeasFitter - - -class TestBackendV1(QiskitAlgorithmsTestCase): - """test BackendV1 interface""" - - def setUp(self): - super().setUp() - self._provider = FakeProvider() - self._qasm = self._provider.get_backend("fake_qasm_simulator") - self.seed = 50 - - def test_vqe_qasm(self): - """Test the VQE on QASM simulator.""" - optimizer = SPSA(maxiter=300, last_avg=5) - wavefunction = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz") - - with self.assertWarns(DeprecationWarning): - h2_op = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - qasm_simulator = QuantumInstance( - self._qasm, shots=1024, seed_simulator=self.seed, seed_transpiler=self.seed - ) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=optimizer, - max_evals_grouped=1, - quantum_instance=qasm_simulator, - ) - result = vqe.compute_minimum_eigenvalue(operator=h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.86, delta=0.05) - - def test_run_circuit_oracle(self): - """Test execution with a quantum circuit oracle""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - problem = AmplificationProblem(oracle, is_good_state=["11"]) - - with self.assertWarns(DeprecationWarning): - qi = QuantumInstance( - self._provider.get_backend("fake_vigo"), seed_simulator=12, seed_transpiler=32 - ) - - with self.assertWarns(DeprecationWarning): - grover = Grover(quantum_instance=qi) - result = grover.amplify(problem) - - self.assertIn(result.top_measurement, ["11"]) - - def test_run_circuit_oracle_single_experiment_backend(self): - """Test execution with a quantum circuit oracle""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - problem = AmplificationProblem(oracle, is_good_state=["11"]) - backend = self._provider.get_backend("fake_vigo") - backend._configuration.max_experiments = 1 - - with self.assertWarns(DeprecationWarning): - qi = QuantumInstance(backend, seed_simulator=12, seed_transpiler=32) - - with self.assertWarns(DeprecationWarning): - grover = Grover(quantum_instance=qi) - result = grover.amplify(problem) - - self.assertIn(result.top_measurement, ["11"]) - - def test_measurement_error_mitigation_with_vqe(self): - """measurement error mitigation test with vqe""" - try: - from qiskit.providers.aer import noise - except ImportError as ex: - self.skipTest(f"Package doesn't appear to be installed. Error: '{str(ex)}'") - return - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - - # build noise model - noise_model = noise.NoiseModel() - read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) - noise_model.add_all_qubit_readout_error(read_err) - - backend = self._qasm - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=backend, - seed_simulator=167, - seed_transpiler=167, - noise_model=noise_model, - measurement_error_mitigation_cls=CompleteMeasFitter, - ) - h2_hamiltonian = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - - optimizer = SPSA(maxiter=200) - ansatz = EfficientSU2(2, reps=1) - - with self.assertWarns(DeprecationWarning): - vqe = VQE(ansatz=ansatz, optimizer=optimizer, quantum_instance=quantum_instance) - result = vqe.compute_minimum_eigenvalue(operator=h2_hamiltonian) - - self.assertGreater(quantum_instance.time_taken, 0.0) - quantum_instance.reset_execution_results() - self.assertAlmostEqual(result.eigenvalue.real, -1.86, delta=0.05) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_backendv2.py b/test/python/algorithms/test_backendv2.py deleted file mode 100644 index 0987a631f690..000000000000 --- a/test/python/algorithms/test_backendv2.py +++ /dev/null @@ -1,102 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Providers that support BackendV2 interface""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit import QuantumCircuit -from qiskit.providers.fake_provider import FakeProvider -from qiskit.providers.fake_provider.fake_backend_v2 import FakeBackendSimple -from qiskit.utils import QuantumInstance -from qiskit.algorithms import VQE, Grover, AmplificationProblem -from qiskit.opflow import X, Z, I -from qiskit.algorithms.optimizers import SPSA -from qiskit.circuit.library import TwoLocal - - -class TestBackendV2(QiskitAlgorithmsTestCase): - """test BackendV2 interface""" - - def setUp(self): - super().setUp() - self._provider = FakeProvider() - self._qasm = FakeBackendSimple() - self.seed = 50 - - def test_vqe_qasm(self): - """Test the VQE on QASM simulator.""" - optimizer = SPSA(maxiter=300, last_avg=5) - wavefunction = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz") - - with self.assertWarns(DeprecationWarning): - h2_op = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - qasm_simulator = QuantumInstance( - self._qasm, shots=1024, seed_simulator=self.seed, seed_transpiler=self.seed - ) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=optimizer, - max_evals_grouped=1, - quantum_instance=qasm_simulator, - ) - result = vqe.compute_minimum_eigenvalue(operator=h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.86, delta=0.05) - - def test_run_circuit_oracle(self): - """Test execution with a quantum circuit oracle""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - problem = AmplificationProblem(oracle, is_good_state=["11"]) - - with self.assertWarns(DeprecationWarning): - qi = QuantumInstance( - self._provider.get_backend("fake_yorktown"), seed_simulator=12, seed_transpiler=32 - ) - - with self.assertWarns(DeprecationWarning): - grover = Grover(quantum_instance=qi) - result = grover.amplify(problem) - - self.assertIn(result.top_measurement, ["11"]) - - def test_run_circuit_oracle_single_experiment_backend(self): - """Test execution with a quantum circuit oracle""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - problem = AmplificationProblem(oracle, is_good_state=["11"]) - backend = self._provider.get_backend("fake_yorktown") - backend._configuration.max_experiments = 1 - - with self.assertWarns(DeprecationWarning): - qi = QuantumInstance( - self._provider.get_backend("fake_yorktown"), seed_simulator=12, seed_transpiler=32 - ) - - with self.assertWarns(DeprecationWarning): - grover = Grover(quantum_instance=qi) - result = grover.amplify(problem) - - self.assertIn(result.top_measurement, ["11"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_entangler_map.py b/test/python/algorithms/test_entangler_map.py deleted file mode 100644 index 56dd5100c64f..000000000000 --- a/test/python/algorithms/test_entangler_map.py +++ /dev/null @@ -1,68 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Entangler Map""" - -import unittest - -from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.utils import get_entangler_map, validate_entangler_map - - -class TestEntanglerMap(QiskitAlgorithmsTestCase): - """Test Entangler Map""" - - def test_map_type_linear(self): - """,ap type linear test""" - ref_map = [[0, 1], [1, 2], [2, 3]] - entangler_map = get_entangler_map("linear", 4) - - for (ref_src, ref_targ), (exp_src, exp_targ) in zip(ref_map, entangler_map): - self.assertEqual(ref_src, exp_src) - self.assertEqual(ref_targ, exp_targ) - - def test_map_type_full(self): - """map type full test""" - ref_map = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]] - entangler_map = get_entangler_map("full", 4) - - for (ref_src, ref_targ), (exp_src, exp_targ) in zip(ref_map, entangler_map): - self.assertEqual(ref_src, exp_src) - self.assertEqual(ref_targ, exp_targ) - - def test_validate_entangler_map(self): - """validate entangler map test""" - valid_map = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]] - self.assertTrue(validate_entangler_map(valid_map, 4)) - - valid_map_2 = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3], [3, 2]] - self.assertTrue(validate_entangler_map(valid_map_2, 4, True)) - - invalid_map = [[0, 4], [4, 2], [0, 3], [1, 2], [1, 3], [2, 3]] - with self.assertRaises(ValueError): - validate_entangler_map(invalid_map, 4) - - invalid_map_2 = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3], [3, 2]] - with self.assertRaises(ValueError): - validate_entangler_map(invalid_map_2, 4) - - wrong_type_map = {0: [1, 2, 3], 1: [2, 3]} - with self.assertRaises(TypeError): - validate_entangler_map(wrong_type_map, 4) - - wrong_type_map_2 = [(0, 1), (0, 2), (0, 3)] - with self.assertRaises(TypeError): - validate_entangler_map(wrong_type_map_2, 4) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_grover.py b/test/python/algorithms/test_grover.py deleted file mode 100644 index 0ba6cbaccf04..000000000000 --- a/test/python/algorithms/test_grover.py +++ /dev/null @@ -1,402 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Grover's algorithm.""" - -import itertools -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from ddt import data, ddt, idata, unpack - -from qiskit import BasicAer, QuantumCircuit -from qiskit.algorithms import AmplificationProblem, Grover -from qiskit.circuit.library import GroverOperator, PhaseOracle -from qiskit.primitives import Sampler -from qiskit.quantum_info import Operator, Statevector -from qiskit.utils import QuantumInstance, algorithm_globals -from qiskit.utils.optionals import HAS_TWEEDLEDUM - - -@ddt -class TestAmplificationProblem(QiskitAlgorithmsTestCase): - """Test the amplification problem.""" - - def setUp(self): - super().setUp() - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - self._expected_grover_op = GroverOperator(oracle=oracle) - - @data("oracle_only", "oracle_and_stateprep") - def test_groverop_getter(self, kind): - """Test the default construction of the Grover operator.""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - - if kind == "oracle_only": - problem = AmplificationProblem(oracle, is_good_state=["11"]) - expected = GroverOperator(oracle) - else: - stateprep = QuantumCircuit(2) - stateprep.ry(0.2, [0, 1]) - problem = AmplificationProblem( - oracle, state_preparation=stateprep, is_good_state=["11"] - ) - expected = GroverOperator(oracle, stateprep) - - self.assertEqual(Operator(expected), Operator(problem.grover_operator)) - - @data("list_str", "list_int", "statevector", "callable") - def test_is_good_state(self, kind): - """Test is_good_state works on different input types.""" - if kind == "list_str": - is_good_state = ["01", "11"] - elif kind == "list_int": - is_good_state = [1] # means bitstr[1] == '1' - elif kind == "statevector": - is_good_state = Statevector(np.array([0, 1, 0, 1]) / np.sqrt(2)) - else: - - def is_good_state(bitstr): - # same as ``bitstr in ['01', '11']`` - return bitstr[1] == "1" - - possible_states = [ - "".join(list(map(str, item))) for item in itertools.product([0, 1], repeat=2) - ] - - oracle = QuantumCircuit(2) - problem = AmplificationProblem(oracle, is_good_state=is_good_state) - - expected = [state in ["01", "11"] for state in possible_states] - actual = [problem.is_good_state(state) for state in possible_states] - - self.assertListEqual(expected, actual) - - -@ddt -class TestGrover(QiskitAlgorithmsTestCase): - """Test for the functionality of Grover""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.statevector = QuantumInstance( - BasicAer.get_backend("statevector_simulator"), seed_simulator=12, seed_transpiler=32 - ) - self.qasm = QuantumInstance( - BasicAer.get_backend("qasm_simulator"), seed_simulator=12, seed_transpiler=32 - ) - self._sampler = Sampler() - self._sampler_with_shots = Sampler(options={"shots": 1024, "seed": 123}) - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 123 - - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum required for this test") - @data("ideal", "shots", False) - def test_implicit_phase_oracle_is_good_state(self, use_sampler): - """Test implicit default for is_good_state with PhaseOracle.""" - grover = self._prepare_grover(use_sampler) - oracle = PhaseOracle("x & y") - problem = AmplificationProblem(oracle) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertEqual(result.top_measurement, "11") - - @idata(itertools.product(["ideal", "shots", False], [[1, 2, 3], None, 2])) - @unpack - def test_iterations_with_good_state(self, use_sampler, iterations): - """Test the algorithm with different iteration types and with good state""" - grover = self._prepare_grover(use_sampler, iterations) - problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertEqual(result.top_measurement, "111") - - @idata(itertools.product(["shots", False], [[1, 2, 3], None, 2])) - @unpack - def test_iterations_with_good_state_sample_from_iterations(self, use_sampler, iterations): - """Test the algorithm with different iteration types and with good state""" - grover = self._prepare_grover(use_sampler, iterations, sample_from_iterations=True) - problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertEqual(result.top_measurement, "111") - - @data("ideal", "shots", False) - def test_fixed_iterations_without_good_state(self, use_sampler): - """Test the algorithm with iterations as an int and without good state""" - grover = self._prepare_grover(use_sampler, iterations=2) - problem = AmplificationProblem(Statevector.from_label("111")) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertEqual(result.top_measurement, "111") - - @idata(itertools.product(["ideal", "shots", False], [[1, 2, 3], None])) - @unpack - def test_iterations_without_good_state(self, use_sampler, iterations): - """Test the correct error is thrown for none/list of iterations and without good state""" - grover = self._prepare_grover(use_sampler, iterations=iterations) - problem = AmplificationProblem(Statevector.from_label("111")) - - with self.assertRaisesRegex( - TypeError, "An is_good_state function is required with the provided oracle" - ): - if not use_sampler: - with self.assertWarns(DeprecationWarning): - grover.amplify(problem) - else: - grover.amplify(problem) - - @data("ideal", "shots", False) - def test_iterator(self, use_sampler): - """Test running the algorithm on an iterator.""" - - # step-function iterator - def iterator(): - wait, value, count = 3, 1, 0 - while True: - yield value - count += 1 - if count % wait == 0: - value += 1 - - grover = self._prepare_grover(use_sampler, iterations=iterator()) - problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertEqual(result.top_measurement, "111") - - @data("ideal", "shots", False) - def test_growth_rate(self, use_sampler): - """Test running the algorithm on a growth rate""" - grover = self._prepare_grover(use_sampler, growth_rate=8 / 7) - problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertEqual(result.top_measurement, "111") - - @data("ideal", "shots", False) - def test_max_num_iterations(self, use_sampler): - """Test the iteration stops when the maximum number of iterations is reached.""" - - def zero(): - while True: - yield 0 - - grover = self._prepare_grover(use_sampler, iterations=zero()) - n = 5 - problem = AmplificationProblem(Statevector.from_label("1" * n), is_good_state=["1" * n]) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertEqual(len(result.iterations), 2**n) - - @data("ideal", "shots", False) - def test_max_power(self, use_sampler): - """Test the iteration stops when the maximum power is reached.""" - lam = 10.0 - grover = self._prepare_grover(use_sampler, growth_rate=lam) - problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) - result = grover.amplify(problem) - self.assertEqual(len(result.iterations), 0) - - @data("ideal", "shots", False) - def test_run_circuit_oracle(self, use_sampler): - """Test execution with a quantum circuit oracle""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - problem = AmplificationProblem(oracle, is_good_state=["11"]) - grover = self._prepare_grover(use_sampler) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertIn(result.top_measurement, ["11"]) - - @data("ideal", "shots", False) - def test_run_state_vector_oracle(self, use_sampler): - """Test execution with a state vector oracle""" - mark_state = Statevector.from_label("11") - problem = AmplificationProblem(mark_state, is_good_state=["11"]) - grover = self._prepare_grover(use_sampler) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertIn(result.top_measurement, ["11"]) - - @data("ideal", "shots", False) - def test_run_custom_grover_operator(self, use_sampler): - """Test execution with a grover operator oracle""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - grover_op = GroverOperator(oracle) - problem = AmplificationProblem( - oracle=oracle, grover_operator=grover_op, is_good_state=["11"] - ) - grover = self._prepare_grover(use_sampler) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertIn(result.top_measurement, ["11"]) - - def test_optimal_num_iterations(self): - """Test optimal_num_iterations""" - num_qubits = 7 - for num_solutions in range(1, 2**num_qubits): - amplitude = np.sqrt(num_solutions / 2**num_qubits) - expected = round(np.arccos(amplitude) / (2 * np.arcsin(amplitude))) - actual = Grover.optimal_num_iterations(num_solutions, num_qubits) - self.assertEqual(actual, expected) - - def test_construct_circuit(self): - """Test construct_circuit""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - problem = AmplificationProblem(oracle, is_good_state=["11"]) - grover = Grover() - constructed = grover.construct_circuit(problem, 2, measurement=False) - - grover_op = GroverOperator(oracle) - expected = QuantumCircuit(2) - expected.h([0, 1]) - expected.compose(grover_op.power(2), inplace=True) - - self.assertTrue(Operator(constructed).equiv(Operator(expected))) - - @data("ideal", "shots", False) - def test_circuit_result(self, use_sampler): - """Test circuit_result""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - # is_good_state=['00'] is intentionally selected to obtain a list of results - problem = AmplificationProblem(oracle, is_good_state=["00"]) - grover = self._prepare_grover(use_sampler, iterations=[1, 2, 3, 4]) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - if use_sampler: - for i, dist in enumerate(result.circuit_results): - keys, values = zip(*sorted(dist.items())) - if i in (0, 3): - self.assertTupleEqual(keys, ("11",)) - np.testing.assert_allclose(values, [1], atol=0.2) - else: - self.assertTupleEqual(keys, ("00", "01", "10", "11")) - np.testing.assert_allclose(values, [0.25, 0.25, 0.25, 0.25], atol=0.2) - else: - expected_results = [ - {"11": 1024}, - {"00": 238, "01": 253, "10": 263, "11": 270}, - {"00": 238, "01": 253, "10": 263, "11": 270}, - {"11": 1024}, - ] - self.assertEqual(result.circuit_results, expected_results) - - @data("ideal", "shots", False) - def test_max_probability(self, use_sampler): - """Test max_probability""" - oracle = QuantumCircuit(2) - oracle.cz(0, 1) - problem = AmplificationProblem(oracle, is_good_state=["11"]) - grover = self._prepare_grover(use_sampler) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertAlmostEqual(result.max_probability, 1.0) - - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum required for this test") - @data("ideal", "shots", False) - def test_oracle_evaluation(self, use_sampler): - """Test oracle_evaluation for PhaseOracle""" - oracle = PhaseOracle("x1 & x2 & (not x3)") - problem = AmplificationProblem(oracle, is_good_state=oracle.evaluate_bitstring) - grover = self._prepare_grover(use_sampler) - if not use_sampler: - with self.assertWarns(DeprecationWarning): - result = grover.amplify(problem) - else: - result = grover.amplify(problem) - self.assertTrue(result.oracle_evaluation) - self.assertEqual("011", result.top_measurement) - - def test_sampler_setter(self): - """Test sampler setter""" - grover = Grover() - grover.sampler = self._sampler - self.assertEqual(grover.sampler, self._sampler) - - def _prepare_grover( - self, use_sampler, iterations=None, growth_rate=None, sample_from_iterations=False - ): - """Prepare Grover instance for test""" - if use_sampler == "ideal": - grover = Grover( - sampler=self._sampler, - iterations=iterations, - growth_rate=growth_rate, - sample_from_iterations=sample_from_iterations, - ) - elif use_sampler == "shots": - grover = Grover( - sampler=self._sampler_with_shots, - iterations=iterations, - growth_rate=growth_rate, - sample_from_iterations=sample_from_iterations, - ) - else: - with self.assertWarns(DeprecationWarning): - grover = Grover( - quantum_instance=self.qasm, - iterations=iterations, - growth_rate=growth_rate, - sample_from_iterations=sample_from_iterations, - ) - return grover - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_measure_error_mitigation.py b/test/python/algorithms/test_measure_error_mitigation.py deleted file mode 100644 index ed9e972c524e..000000000000 --- a/test/python/algorithms/test_measure_error_mitigation.py +++ /dev/null @@ -1,524 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Measurement Error Mitigation""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import ddt, data, unpack -import numpy as np -import rustworkx as rx -from qiskit import QuantumCircuit, execute -from qiskit.quantum_info import Pauli -from qiskit.exceptions import QiskitError -from qiskit.utils import QuantumInstance, algorithm_globals -from qiskit.algorithms import VQE, QAOA -from qiskit.opflow import I, X, Z, PauliSumOp -from qiskit.algorithms.optimizers import SPSA, COBYLA -from qiskit.circuit.library import EfficientSU2 -from qiskit.utils.mitigation import CompleteMeasFitter, TensoredMeasFitter -from qiskit.utils.measurement_error_mitigation import build_measurement_error_mitigation_circuits -from qiskit.utils import optionals - -if optionals.HAS_AER: - # pylint: disable=no-name-in-module - from qiskit import Aer - from qiskit.providers.aer import noise -if optionals.HAS_IGNIS: - # pylint: disable=no-name-in-module - from qiskit.ignis.mitigation.measurement import ( - CompleteMeasFitter as CompleteMeasFitter_IG, - TensoredMeasFitter as TensoredMeasFitter_IG, - ) - - -@ddt -class TestMeasurementErrorMitigation(QiskitAlgorithmsTestCase): - """Test measurement error mitigation.""" - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required for this test") - @data( - ("CompleteMeasFitter", None, False), - ("TensoredMeasFitter", None, False), - ("TensoredMeasFitter", [[0, 1]], True), - ("TensoredMeasFitter", [[1], [0]], False), - ) - @unpack - def test_measurement_error_mitigation_with_diff_qubit_order( - self, - fitter_str, - mit_pattern, - fails, - ): - """measurement error mitigation with different qubit order""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - - # build noise model - noise_model = noise.NoiseModel() - read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) - noise_model.add_all_qubit_readout_error(read_err) - - fitter_cls = ( - CompleteMeasFitter if fitter_str == "CompleteMeasFitter" else TensoredMeasFitter - ) - backend = Aer.get_backend("aer_simulator") - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=backend, - seed_simulator=1679, - seed_transpiler=167, - shots=1000, - noise_model=noise_model, - measurement_error_mitigation_cls=fitter_cls, - cals_matrix_refresh_period=0, - mit_pattern=mit_pattern, - ) - - # circuit - qc1 = QuantumCircuit(2, 2) - qc1.h(0) - qc1.cx(0, 1) - qc1.measure(0, 0) - qc1.measure(1, 1) - qc2 = QuantumCircuit(2, 2) - qc2.h(0) - qc2.cx(0, 1) - qc2.measure(1, 0) - qc2.measure(0, 1) - - with self.assertWarns(DeprecationWarning): - if fails: - self.assertRaisesRegex( - QiskitError, - "Each element in the mit pattern should have length 1.", - quantum_instance.execute, - [qc1, qc2], - ) - else: - quantum_instance.execute([qc1, qc2]) - - self.assertGreater(quantum_instance.time_taken, 0.0) - quantum_instance.reset_execution_results() - - # failure case - qc3 = QuantumCircuit(3, 3) - qc3.h(2) - qc3.cx(1, 2) - qc3.measure(2, 1) - qc3.measure(1, 2) - - with self.assertWarns(DeprecationWarning): - self.assertRaises(QiskitError, quantum_instance.execute, [qc1, qc3]) - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required for this test") - @data(("CompleteMeasFitter", None), ("TensoredMeasFitter", [[0], [1]])) - def test_measurement_error_mitigation_with_vqe(self, config): - """measurement error mitigation test with vqe""" - - fitter_str, mit_pattern = config - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - - # build noise model - noise_model = noise.NoiseModel() - read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) - noise_model.add_all_qubit_readout_error(read_err) - - fitter_cls = ( - CompleteMeasFitter if fitter_str == "CompleteMeasFitter" else TensoredMeasFitter - ) - backend = Aer.get_backend("aer_simulator") - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=backend, - seed_simulator=167, - seed_transpiler=167, - noise_model=noise_model, - measurement_error_mitigation_cls=fitter_cls, - mit_pattern=mit_pattern, - ) - - optimizer = SPSA(maxiter=200) - ansatz = EfficientSU2(2, reps=1) - - with self.assertWarns(DeprecationWarning): - h2_hamiltonian = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - - with self.assertWarns(DeprecationWarning): - vqe = VQE(ansatz=ansatz, optimizer=optimizer, quantum_instance=quantum_instance) - result = vqe.compute_minimum_eigenvalue(operator=h2_hamiltonian) - - self.assertGreater(quantum_instance.time_taken, 0.0) - quantum_instance.reset_execution_results() - self.assertAlmostEqual(result.eigenvalue.real, -1.86, delta=0.05) - - def _get_operator(self, weight_matrix): - """Generate Hamiltonian for the max-cut problem of a graph. - - Args: - weight_matrix (numpy.ndarray) : adjacency matrix. - - Returns: - PauliSumOp: operator for the Hamiltonian - float: a constant shift for the obj function. - - """ - num_nodes = weight_matrix.shape[0] - pauli_list = [] - shift = 0 - for i in range(num_nodes): - for j in range(i): - if weight_matrix[i, j] != 0: - x_p = np.zeros(num_nodes, dtype=bool) - z_p = np.zeros(num_nodes, dtype=bool) - z_p[i] = True - z_p[j] = True - pauli_list.append([0.5 * weight_matrix[i, j], Pauli((z_p, x_p))]) - shift -= 0.5 * weight_matrix[i, j] - opflow_list = [(pauli[1].to_label(), pauli[0]) for pauli in pauli_list] - - with self.assertWarns(DeprecationWarning): - return PauliSumOp.from_list(opflow_list), shift - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required for this test") - def test_measurement_error_mitigation_qaoa(self): - """measurement error mitigation test with QAOA""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 167 - - backend = Aer.get_backend("aer_simulator") - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - w = rx.adjacency_matrix( - rx.undirected_gnp_random_graph(5, 0.5, seed=algorithm_globals.random_seed) - ) - qubit_op, _ = self._get_operator(w) - initial_point = np.asarray([0.0, 0.0]) - - # Compute first without noise - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=backend, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - - with self.assertWarns(DeprecationWarning): - qaoa = QAOA( - optimizer=COBYLA(maxiter=3), - quantum_instance=quantum_instance, - initial_point=initial_point, - ) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - ref_eigenvalue = result.eigenvalue.real - - # compute with noise - # build noise model - noise_model = noise.NoiseModel() - read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) - noise_model.add_all_qubit_readout_error(read_err) - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=backend, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - noise_model=noise_model, - measurement_error_mitigation_cls=CompleteMeasFitter, - shots=10000, - ) - - with self.assertWarns(DeprecationWarning): - - qaoa = QAOA( - optimizer=COBYLA(maxiter=3), - quantum_instance=quantum_instance, - initial_point=initial_point, - ) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - self.assertAlmostEqual(result.eigenvalue.real, ref_eigenvalue, delta=0.05) - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required for this test") - @unittest.skipUnless(optionals.HAS_IGNIS, "qiskit-ignis is required to run this test") - @data("CompleteMeasFitter", "TensoredMeasFitter") - def test_measurement_error_mitigation_with_diff_qubit_order_ignis(self, fitter_str): - """measurement error mitigation with different qubit order""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - - # build noise model - noise_model = noise.NoiseModel() - read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) - noise_model.add_all_qubit_readout_error(read_err) - - fitter_cls = ( - CompleteMeasFitter_IG if fitter_str == "CompleteMeasFitter" else TensoredMeasFitter_IG - ) - backend = Aer.get_backend("aer_simulator") - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=backend, - seed_simulator=1679, - seed_transpiler=167, - shots=1000, - noise_model=noise_model, - measurement_error_mitigation_cls=fitter_cls, - cals_matrix_refresh_period=0, - ) - - # circuit - qc1 = QuantumCircuit(2, 2) - qc1.h(0) - qc1.cx(0, 1) - qc1.measure(0, 0) - qc1.measure(1, 1) - qc2 = QuantumCircuit(2, 2) - qc2.h(0) - qc2.cx(0, 1) - qc2.measure(1, 0) - qc2.measure(0, 1) - - if fitter_cls == TensoredMeasFitter_IG: - with self.assertWarnsRegex(DeprecationWarning, r".*ignis.*"): - self.assertRaisesRegex( - QiskitError, - "TensoredMeasFitter doesn't support subset_fitter.", - quantum_instance.execute, - [qc1, qc2], - ) - else: - # this should run smoothly - with self.assertWarnsRegex(DeprecationWarning, r".*ignis.*"): - quantum_instance.execute([qc1, qc2]) - - self.assertGreater(quantum_instance.time_taken, 0.0) - quantum_instance.reset_execution_results() - - # failure case - qc3 = QuantumCircuit(3, 3) - qc3.h(2) - qc3.cx(1, 2) - qc3.measure(2, 1) - qc3.measure(1, 2) - - self.assertRaises(QiskitError, quantum_instance.execute, [qc1, qc3]) - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required for this test") - @unittest.skipUnless(optionals.HAS_IGNIS, "qiskit-ignis is required to run this test") - @data(("CompleteMeasFitter", None), ("TensoredMeasFitter", [[0], [1]])) - def test_measurement_error_mitigation_with_vqe_ignis(self, config): - """measurement error mitigation test with vqe""" - fitter_str, mit_pattern = config - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - - # build noise model - noise_model = noise.NoiseModel() - read_err = noise.errors.readout_error.ReadoutError([[0.9, 0.1], [0.25, 0.75]]) - noise_model.add_all_qubit_readout_error(read_err) - - fitter_cls = ( - CompleteMeasFitter_IG if fitter_str == "CompleteMeasFitter" else TensoredMeasFitter_IG - ) - backend = Aer.get_backend("aer_simulator") - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=backend, - seed_simulator=167, - seed_transpiler=167, - noise_model=noise_model, - measurement_error_mitigation_cls=fitter_cls, - mit_pattern=mit_pattern, - ) - - h2_hamiltonian = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - optimizer = SPSA(maxiter=200) - ansatz = EfficientSU2(2, reps=1) - - with self.assertWarnsRegex(DeprecationWarning): - vqe = VQE(ansatz=ansatz, optimizer=optimizer, quantum_instance=quantum_instance) - result = vqe.compute_minimum_eigenvalue(operator=h2_hamiltonian) - - self.assertGreater(quantum_instance.time_taken, 0.0) - quantum_instance.reset_execution_results() - self.assertAlmostEqual(result.eigenvalue.real, -1.86, delta=0.05) - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required for this test") - @unittest.skipUnless(optionals.HAS_IGNIS, "qiskit-ignis is required to run this test") - def test_calibration_results(self): - """check that results counts are the same with/without error mitigation""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 1679 - np.random.seed(algorithm_globals.random_seed) - - qc = QuantumCircuit(1) - qc.x(0) - - qc_meas = qc.copy() - qc_meas.measure_all() - backend = Aer.get_backend("aer_simulator") - - counts_array = [None, None] - for idx, is_use_mitigation in enumerate([True, False]): - with self.assertWarns(DeprecationWarning): - if is_use_mitigation: - quantum_instance = QuantumInstance( - backend, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - shots=1024, - measurement_error_mitigation_cls=CompleteMeasFitter_IG, - ) - with self.assertWarnsRegex(DeprecationWarning, r".*ignis.*"): - counts_array[idx] = quantum_instance.execute(qc_meas).get_counts() - else: - quantum_instance = QuantumInstance( - backend, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - shots=1024, - ) - counts_array[idx] = quantum_instance.execute(qc_meas).get_counts() - self.assertEqual( - counts_array[0], counts_array[1], msg="Counts different with/without fitter." - ) - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required for this test") - def test_circuit_modified(self): - """tests that circuits don't get modified on QI execute with error mitigation - as per issue #7449 - """ - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 1679 - np.random.seed(algorithm_globals.random_seed) - - circuit = QuantumCircuit(1) - circuit.x(0) - circuit.measure_all() - - with self.assertWarns(DeprecationWarning): - qi = QuantumInstance( - Aer.get_backend("aer_simulator"), - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - shots=1024, - measurement_error_mitigation_cls=CompleteMeasFitter, - ) - # The error happens on transpiled circuits since "execute" was changing the input array - # Non transpiled circuits didn't have a problem because a new transpiled array was created - # internally. - circuits_ref = qi.transpile(circuit) # always returns a new array - circuits_input = circuits_ref.copy() - - with self.assertWarns(DeprecationWarning): - _ = qi.execute(circuits_input, had_transpiled=True) - self.assertEqual(circuits_ref, circuits_input, msg="Transpiled circuit array modified.") - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required for this test") - def test_tensor_subset_fitter(self): - """Test the subset fitter method of the tensor fitter.""" - - # Construct a noise model where readout has errors of different strengths. - noise_model = noise.NoiseModel() - # big error - read_err0 = noise.errors.readout_error.ReadoutError([[0.90, 0.10], [0.25, 0.75]]) - # ideal - read_err1 = noise.errors.readout_error.ReadoutError([[1.00, 0.00], [0.00, 1.00]]) - # small error - read_err2 = noise.errors.readout_error.ReadoutError([[0.98, 0.02], [0.03, 0.97]]) - noise_model.add_readout_error(read_err0, (0,)) - noise_model.add_readout_error(read_err1, (1,)) - noise_model.add_readout_error(read_err2, (2,)) - - mit_pattern = [[idx] for idx in range(3)] - backend = Aer.get_backend("aer_simulator") - backend.set_options(seed_simulator=123) - - with self.assertWarns(DeprecationWarning): - mit_circuits = build_measurement_error_mitigation_circuits( - [0, 1, 2], - TensoredMeasFitter, - backend, - backend_config={}, - compile_config={}, - mit_pattern=mit_pattern, - ) - result = execute(mit_circuits[0], backend, noise_model=noise_model).result() - fitter = TensoredMeasFitter(result, mit_pattern=mit_pattern) - - cal_matrices = fitter.cal_matrices - - # Check that permutations and permuted subsets match. - for subset in [[1, 0], [1, 2], [0, 2], [2, 0, 1]]: - with self.subTest(subset=subset): - with self.assertWarns(DeprecationWarning): - new_fitter = fitter.subset_fitter(subset) - - for idx, qubit in enumerate(subset): - self.assertTrue(np.allclose(new_fitter.cal_matrices[idx], cal_matrices[qubit])) - - self.assertRaisesRegex( - QiskitError, - "Qubit 3 is not in the mit pattern", - fitter.subset_fitter, - [0, 2, 3], - ) - - # Test that we properly correct a circuit with permuted measurements. - circuit = QuantumCircuit(3, 3) - circuit.x(range(3)) - circuit.measure(1, 0) - circuit.measure(2, 1) - circuit.measure(0, 2) - - result = execute( - circuit, backend, noise_model=noise_model, shots=1000, seed_simulator=0 - ).result() - with self.subTest(subset=subset): - with self.assertWarns(DeprecationWarning): - new_result = fitter.subset_fitter([1, 2, 0]).filter.apply(result) - - # The noisy result should have a poor 111 state, the mit. result should be good. - self.assertTrue(result.get_counts()["111"] < 800) - self.assertTrue(new_result.get_counts()["111"] > 990) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_numpy_eigen_solver.py b/test/python/algorithms/test_numpy_eigen_solver.py deleted file mode 100644 index 36d2b66148d0..000000000000 --- a/test/python/algorithms/test_numpy_eigen_solver.py +++ /dev/null @@ -1,210 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test NumPy Eigen solver""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from ddt import data, ddt - -from qiskit.algorithms import NumPyEigensolver -from qiskit.opflow import PauliSumOp, X, Y, Z - - -@ddt -class TestNumPyEigensolver(QiskitAlgorithmsTestCase): - """Test NumPy Eigen solver""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.qubit_op = PauliSumOp.from_list( - [ - ("II", -1.052373245772859), - ("ZI", 0.39793742484318045), - ("IZ", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] - ) - - def test_ce(self): - """Test basics""" - with self.assertWarns(DeprecationWarning): - algo = NumPyEigensolver() - result = algo.compute_eigenvalues(operator=self.qubit_op, aux_operators=[]) - - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - - def test_ce_k4(self): - """Test for k=4 eigenvalues""" - with self.assertWarns(DeprecationWarning): - algo = NumPyEigensolver(k=4) - result = algo.compute_eigenvalues(operator=self.qubit_op, aux_operators=[]) - - self.assertEqual(len(result.eigenvalues), 4) - self.assertEqual(len(result.eigenstates), 4) - self.assertEqual(result.eigenvalues.dtype, np.float64) - np.testing.assert_array_almost_equal( - result.eigenvalues, [-1.85727503, -1.24458455, -0.88272215, -0.22491125] - ) - - def test_ce_k4_filtered(self): - """Test for k=4 eigenvalues with filter""" - - # define filter criterion - # pylint: disable=unused-argument - def criterion(x, v, a_v): - return v >= -1 - - with self.assertWarns(DeprecationWarning): - algo = NumPyEigensolver(k=4, filter_criterion=criterion) - result = algo.compute_eigenvalues(operator=self.qubit_op, aux_operators=[]) - - self.assertEqual(len(result.eigenvalues), 2) - self.assertEqual(len(result.eigenstates), 2) - self.assertEqual(result.eigenvalues.dtype, np.float64) - np.testing.assert_array_almost_equal(result.eigenvalues, [-0.88272215, -0.22491125]) - - def test_ce_k4_filtered_empty(self): - """Test for k=4 eigenvalues with filter always returning False""" - - # define filter criterion - # pylint: disable=unused-argument - def criterion(x, v, a_v): - return False - - with self.assertWarns(DeprecationWarning): - algo = NumPyEigensolver(k=4, filter_criterion=criterion) - result = algo.compute_eigenvalues(operator=self.qubit_op, aux_operators=[]) - self.assertEqual(len(result.eigenvalues), 0) - self.assertEqual(len(result.eigenstates), 0) - - @data(X, Y, Z) - def test_ce_k1_1q(self, op): - """Test for 1 qubit operator""" - - with self.assertWarns(DeprecationWarning): - algo = NumPyEigensolver(k=1) - result = algo.compute_eigenvalues(operator=op) - np.testing.assert_array_almost_equal(result.eigenvalues, [-1]) - - @data(X, Y, Z) - def test_ce_k2_1q(self, op): - """Test for 1 qubit operator""" - - with self.assertWarns(DeprecationWarning): - algo = NumPyEigensolver(k=2) - result = algo.compute_eigenvalues(operator=op) - np.testing.assert_array_almost_equal(result.eigenvalues, [-1, 1]) - - def test_aux_operators_list(self): - """Test list-based aux_operators.""" - - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - - with self.assertWarns(DeprecationWarning): - algo = NumPyEigensolver() - result = algo.compute_eigenvalues(operator=self.qubit_op, aux_operators=aux_ops) - - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operator_eigenvalues), 1) - self.assertEqual(len(result.aux_operator_eigenvalues[0]), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][0], 0, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][1], 0.0) - - # Go again with additional None and zero operators - extra_ops = [*aux_ops, None, 0] - - with self.assertWarns(DeprecationWarning): - result = algo.compute_eigenvalues(operator=self.qubit_op, aux_operators=extra_ops) - - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operator_eigenvalues), 1) - self.assertEqual(len(result.aux_operator_eigenvalues[0]), 4) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][0], 0, places=6) - self.assertIsNone(result.aux_operator_eigenvalues[0][2], None) - self.assertEqual(result.aux_operator_eigenvalues[0][3][0], 0.0) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][1], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[0][3][1], 0.0) - - def test_aux_operators_dict(self): - """Test dict-based aux_operators.""" - - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = {"aux_op1": aux_op1, "aux_op2": aux_op2} - - with self.assertWarns(DeprecationWarning): - algo = NumPyEigensolver() - result = algo.compute_eigenvalues(operator=self.qubit_op, aux_operators=aux_ops) - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operator_eigenvalues), 1) - self.assertEqual(len(result.aux_operator_eigenvalues[0]), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op2"][0], 0, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op1"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op2"][1], 0.0) - - # Go again with additional None and zero operators - extra_ops = {**aux_ops, "None_operator": None, "zero_operator": 0} - - with self.assertWarns(DeprecationWarning): - result = algo.compute_eigenvalues(operator=self.qubit_op, aux_operators=extra_ops) - - self.assertEqual(len(result.eigenvalues), 1) - self.assertEqual(len(result.eigenstates), 1) - self.assertEqual(result.eigenvalues.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operator_eigenvalues), 1) - self.assertEqual(len(result.aux_operator_eigenvalues[0]), 3) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op2"][0], 0, places=6) - self.assertEqual(result.aux_operator_eigenvalues[0]["zero_operator"][0], 0.0) - self.assertTrue("None_operator" not in result.aux_operator_eigenvalues[0].keys()) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op1"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op2"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["zero_operator"][1], 0.0) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_numpy_minimum_eigen_solver.py b/test/python/algorithms/test_numpy_minimum_eigen_solver.py deleted file mode 100644 index 8f50d5738338..000000000000 --- a/test/python/algorithms/test_numpy_minimum_eigen_solver.py +++ /dev/null @@ -1,277 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test NumPy Minimum Eigensolver""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from ddt import ddt, data - -from qiskit.algorithms import NumPyMinimumEigensolver -from qiskit.opflow import PauliSumOp, X, Y, Z - - -@ddt -class TestNumPyMinimumEigensolver(QiskitAlgorithmsTestCase): - """Test NumPy Minimum Eigensolver""" - - def setUp(self): - super().setUp() - with self.assertWarns(DeprecationWarning): - self.qubit_op = PauliSumOp.from_list( - [ - ("II", -1.052373245772859), - ("ZI", 0.39793742484318045), - ("IZ", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] - ) - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - - self.aux_ops_list = [aux_op1, aux_op2] - self.aux_ops_dict = {"aux_op1": aux_op1, "aux_op2": aux_op2} - - def test_cme(self): - """Basic test""" - - with self.assertWarns(DeprecationWarning): - algo = NumPyMinimumEigensolver() - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=self.aux_ops_list - ) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[0], [2, 0]) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[1], [0, 0]) - - def test_cme_reuse(self): - """Test reuse""" - # Start with no operator or aux_operators, give via compute method - with self.assertWarns(DeprecationWarning): - algo = NumPyMinimumEigensolver() - result = algo.compute_minimum_eigenvalue(operator=self.qubit_op) - - self.assertEqual(result.eigenvalue.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalue, -1.85727503) - self.assertIsNone(result.aux_operator_eigenvalues) - - # Add aux_operators and go again - with self.assertWarns(DeprecationWarning): - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=self.aux_ops_list - ) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[0], [2, 0]) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[1], [0, 0]) - - # "Remove" aux_operators and go again - with self.assertWarns(DeprecationWarning): - result = algo.compute_minimum_eigenvalue(operator=self.qubit_op, aux_operators=[]) - - self.assertEqual(result.eigenvalue.dtype, np.float64) - self.assertAlmostEqual(result.eigenvalue, -1.85727503) - self.assertIsNone(result.aux_operator_eigenvalues) - - # Set aux_operators and go again - with self.assertWarns(DeprecationWarning): - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=self.aux_ops_list - ) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[0], [2, 0]) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[1], [0, 0]) - - # Finally just set one of aux_operators and main operator, remove aux_operators - - with self.assertWarns(DeprecationWarning): - result = algo.compute_minimum_eigenvalue( - operator=self.aux_ops_list[0], aux_operators=[] - ) - - self.assertAlmostEqual(result.eigenvalue, 2 + 0j) - self.assertIsNone(result.aux_operator_eigenvalues) - - def test_cme_filter(self): - """Basic test""" - - # define filter criterion - # pylint: disable=unused-argument - def criterion(x, v, a_v): - return v >= -0.5 - - with self.assertWarns(DeprecationWarning): - algo = NumPyMinimumEigensolver(filter_criterion=criterion) - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=self.aux_ops_list - ) - - self.assertAlmostEqual(result.eigenvalue, -0.22491125 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[0], [2, 0]) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[1], [0, 0]) - - def test_cme_filter_empty(self): - """Test with filter always returning False""" - - # define filter criterion - # pylint: disable=unused-argument - def criterion(x, v, a_v): - return False - - with self.assertWarns(DeprecationWarning): - algo = NumPyMinimumEigensolver(filter_criterion=criterion) - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=self.aux_ops_list - ) - - self.assertEqual(result.eigenvalue, None) - self.assertEqual(result.eigenstate, None) - self.assertEqual(result.aux_operator_eigenvalues, None) - - @data(X, Y, Z) - def test_cme_1q(self, op): - """Test for 1 qubit operator""" - - with self.assertWarns(DeprecationWarning): - algo = NumPyMinimumEigensolver() - result = algo.compute_minimum_eigenvalue(operator=op) - - self.assertAlmostEqual(result.eigenvalue, -1) - - def test_cme_aux_ops_dict(self): - """Test dictionary compatibility of aux_operators""" - # Start with an empty dictionary - with self.assertWarns(DeprecationWarning): - algo = NumPyMinimumEigensolver() - result = algo.compute_minimum_eigenvalue(operator=self.qubit_op, aux_operators={}) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertIsNone(result.aux_operator_eigenvalues) - - # Add aux_operators dictionary and go again - with self.assertWarns(DeprecationWarning): - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=self.aux_ops_dict - ) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues["aux_op1"], [2, 0]) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues["aux_op2"], [0, 0]) - - # Add None and zero operators and go again - extra_ops = {"None_op": None, "zero_op": 0, **self.aux_ops_dict} - with self.assertWarns(DeprecationWarning): - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=extra_ops - ) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 3) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues["aux_op1"], [2, 0]) - np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues["aux_op2"], [0, 0]) - self.assertEqual(result.aux_operator_eigenvalues["zero_op"], (0.0, 0)) - - def test_aux_operators_list(self): - """Test list-based aux_operators.""" - - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - - with self.assertWarns(DeprecationWarning): - algo = NumPyMinimumEigensolver() - result = algo.compute_minimum_eigenvalue(operator=self.qubit_op, aux_operators=aux_ops) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.0) - - # Go again with additional None and zero operators - extra_ops = [*aux_ops, None, 0] - - with self.assertWarns(DeprecationWarning): - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=extra_ops - ) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 4) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0, places=6) - self.assertIsNone(result.aux_operator_eigenvalues[2], None) - self.assertEqual(result.aux_operator_eigenvalues[3][0], 0.0) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[3][1], 0.0) - - def test_aux_operators_dict(self): - """Test dict-based aux_operators.""" - - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = {"aux_op1": aux_op1, "aux_op2": aux_op2} - - with self.assertWarns(DeprecationWarning): - algo = NumPyMinimumEigensolver() - result = algo.compute_minimum_eigenvalue(operator=self.qubit_op, aux_operators=aux_ops) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][0], 0, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][1], 0.0) - - # Go again with additional None and zero operators - extra_ops = {**aux_ops, "None_operator": None, "zero_operator": 0} - - with self.assertWarns(DeprecationWarning): - result = algo.compute_minimum_eigenvalue( - operator=self.qubit_op, aux_operators=extra_ops - ) - - self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j) - self.assertEqual(len(result.aux_operator_eigenvalues), 3) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][0], 0, places=6) - self.assertEqual(result.aux_operator_eigenvalues["zero_operator"][0], 0.0) - self.assertTrue("None_operator" not in result.aux_operator_eigenvalues.keys()) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues["zero_operator"][1], 0.0) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_observables_evaluator.py b/test/python/algorithms/test_observables_evaluator.py deleted file mode 100644 index d4482c9467b5..000000000000 --- a/test/python/algorithms/test_observables_evaluator.py +++ /dev/null @@ -1,189 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests evaluator of auxiliary operators for algorithms.""" - -from __future__ import annotations -import unittest -import warnings -from typing import Tuple - -from test.python.algorithms import QiskitAlgorithmsTestCase -import numpy as np -from ddt import ddt, data - -from qiskit.algorithms.list_or_dict import ListOrDict -from qiskit.quantum_info.operators.base_operator import BaseOperator -from qiskit.algorithms import estimate_observables -from qiskit.primitives import Estimator -from qiskit.quantum_info import Statevector, SparsePauliOp -from qiskit import QuantumCircuit -from qiskit.circuit.library import EfficientSU2 -from qiskit.opflow import PauliSumOp -from qiskit.utils import algorithm_globals - - -@ddt -class TestObservablesEvaluator(QiskitAlgorithmsTestCase): - """Tests evaluator of auxiliary operators for algorithms.""" - - def setUp(self): - super().setUp() - self.seed = 50 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - - self.threshold = 1e-8 - - def get_exact_expectation( - self, ansatz: QuantumCircuit, observables: ListOrDict[BaseOperator | PauliSumOp] - ): - """ - Calculates the exact expectation to be used as an expected result for unit tests. - """ - if isinstance(observables, dict): - observables_list = list(observables.values()) - else: - observables_list = observables - # the exact value is a list of (mean, (variance, shots)) where we expect 0 variance and - # 0 shots - exact = [ - (Statevector(ansatz).expectation_value(observable), {}) - for observable in observables_list - ] - - if isinstance(observables, dict): - return dict(zip(observables.keys(), exact)) - - return exact - - def _run_test( - self, - expected_result: ListOrDict[Tuple[complex, complex]], - quantum_state: QuantumCircuit, - decimal: int, - observables: ListOrDict[BaseOperator | PauliSumOp], - estimator: Estimator, - ): - result = estimate_observables(estimator, quantum_state, observables, None, self.threshold) - - if isinstance(observables, dict): - np.testing.assert_equal(list(result.keys()), list(expected_result.keys())) - means = [element[0] for element in result.values()] - expected_means = [element[0] for element in expected_result.values()] - np.testing.assert_array_almost_equal(means, expected_means, decimal=decimal) - - vars_and_shots = [element[1] for element in result.values()] - expected_vars_and_shots = [element[1] for element in expected_result.values()] - np.testing.assert_array_equal(vars_and_shots, expected_vars_and_shots) - else: - means = [element[0] for element in result] - expected_means = [element[0] for element in expected_result] - np.testing.assert_array_almost_equal(means, expected_means, decimal=decimal) - - vars_and_shots = [element[1] for element in result] - expected_vars_and_shots = [element[1] for element in expected_result] - np.testing.assert_array_equal(vars_and_shots, expected_vars_and_shots) - - @data( - [ - PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), - PauliSumOp.from_list([("II", 2.0)]), - ], - [ - PauliSumOp.from_list([("ZZ", 2.0)]), - ], - { - "op1": PauliSumOp.from_list([("II", 2.0)]), - "op2": PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]), - }, - { - "op1": PauliSumOp.from_list([("ZZ", 2.0)]), - }, - [], - {}, - ) - def test_estimate_observables(self, observables: ListOrDict[BaseOperator | PauliSumOp]): - """Tests evaluator of auxiliary operators for algorithms.""" - - ansatz = EfficientSU2(2) - parameters = np.array( - [1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], - dtype=float, - ) - - bound_ansatz = ansatz.assign_parameters(parameters) - states = bound_ansatz - expected_result = self.get_exact_expectation(bound_ansatz, observables) - estimator = Estimator() - decimal = 6 - self._run_test( - expected_result, - states, - decimal, - observables, - estimator, - ) - - def test_estimate_observables_zero_op(self): - """Tests if a zero operator is handled correctly.""" - ansatz = EfficientSU2(2) - parameters = np.array( - [1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], - dtype=float, - ) - - bound_ansatz = ansatz.assign_parameters(parameters) - state = bound_ansatz - estimator = Estimator() - observables = [SparsePauliOp(["XX", "YY"]), 0] - result = estimate_observables(estimator, state, observables, None, self.threshold) - expected_result = [(0.015607318055509564, {}), (0.0, {})] - means = [element[0] for element in result] - expected_means = [element[0] for element in expected_result] - np.testing.assert_array_almost_equal(means, expected_means, decimal=0.01) - - vars_and_shots = [element[1] for element in result] - expected_vars_and_shots = [element[1] for element in expected_result] - np.testing.assert_array_equal(vars_and_shots, expected_vars_and_shots) - - def test_estimate_observables_shots(self): - """Tests that variances and shots are returned properly.""" - ansatz = EfficientSU2(2) - parameters = np.array( - [1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0], - dtype=float, - ) - - bound_ansatz = ansatz.assign_parameters(parameters) - state = bound_ansatz - estimator = Estimator(options={"shots": 2048}) - with self.assertWarns(DeprecationWarning): - observables = [PauliSumOp.from_list([("ZZ", 2.0)])] - result = estimate_observables(estimator, state, observables, None, self.threshold) - exact_result = self.get_exact_expectation(bound_ansatz, observables) - expected_result = [(exact_result[0][0], {"variance": 1.0898, "shots": 2048})] - - means = [element[0] for element in result] - expected_means = [element[0] for element in expected_result] - np.testing.assert_array_almost_equal(means, expected_means, decimal=0.01) - - vars_and_shots = [element[1] for element in result] - expected_vars_and_shots = [element[1] for element in expected_result] - for computed, expected in zip(vars_and_shots, expected_vars_and_shots): - self.assertAlmostEqual(computed.pop("variance"), expected.pop("variance"), 2) - self.assertEqual(computed.pop("shots"), expected.pop("shots")) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_phase_estimator.py b/test/python/algorithms/test_phase_estimator.py deleted file mode 100644 index 9040bc1d6912..000000000000 --- a/test/python/algorithms/test_phase_estimator.py +++ /dev/null @@ -1,665 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test phase estimation""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import ddt, data, unpack -import numpy as np -from qiskit.circuit.library import ZGate, XGate, HGate, IGate -from qiskit.quantum_info import Pauli, SparsePauliOp, Statevector, Operator -from qiskit.synthesis import MatrixExponential, SuzukiTrotter -from qiskit.primitives import Sampler -from qiskit.algorithms import PhaseEstimationScale -from qiskit.algorithms.phase_estimators import ( - PhaseEstimation, - HamiltonianPhaseEstimation, - IterativePhaseEstimation, -) -import qiskit -from qiskit import QuantumCircuit -from qiskit.opflow import ( - H, - X, - Y, - Z, - I, - StateFn, - PauliTrotterEvolution, - MatrixEvolution, - PauliSumOp, -) -from qiskit.test import slow_test - - -@ddt -class TestHamiltonianPhaseEstimation(QiskitAlgorithmsTestCase): - """Tests for obtaining eigenvalues from phase estimation""" - - def hamiltonian_pe( - self, - hamiltonian, - state_preparation=None, - num_evaluation_qubits=6, - backend=None, - evolution=None, - bound=None, - ): - """Run HamiltonianPhaseEstimation and return result with all phases.""" - if backend is None: - backend = qiskit.BasicAer.get_backend("statevector_simulator") - - with self.assertWarns(DeprecationWarning): - quantum_instance = qiskit.utils.QuantumInstance(backend=backend, shots=10000) - - with self.assertWarns(DeprecationWarning): - phase_est = HamiltonianPhaseEstimation( - num_evaluation_qubits=num_evaluation_qubits, quantum_instance=quantum_instance - ) - result = phase_est.estimate( - hamiltonian=hamiltonian, - state_preparation=state_preparation, - evolution=evolution, - bound=bound, - ) - return result - - @data(MatrixEvolution(), PauliTrotterEvolution("suzuki", 4)) - def test_pauli_sum_1(self, evolution): - """Two eigenvalues from Pauli sum with X, Z""" - with self.assertWarns(DeprecationWarning): - hamiltonian = 0.5 * X + Z - state_preparation = StateFn(H.to_circuit()) - result = self.hamiltonian_pe(hamiltonian, state_preparation, evolution=evolution) - - phase_dict = result.filter_phases(0.162, as_float=True) - phases = list(phase_dict.keys()) - phases.sort() - - self.assertAlmostEqual(phases[0], -1.125, delta=0.001) - self.assertAlmostEqual(phases[1], 1.125, delta=0.001) - - @data(MatrixEvolution(), PauliTrotterEvolution("suzuki", 3)) - def test_pauli_sum_2(self, evolution): - """Two eigenvalues from Pauli sum with X, Y, Z""" - with self.assertWarns(DeprecationWarning): - hamiltonian = 0.5 * X + Y + Z - state_preparation = None - result = self.hamiltonian_pe(hamiltonian, state_preparation, evolution=evolution) - - phase_dict = result.filter_phases(0.1, as_float=True) - phases = list(phase_dict.keys()) - phases.sort() - - self.assertAlmostEqual(phases[0], -1.484, delta=0.001) - self.assertAlmostEqual(phases[1], 1.484, delta=0.001) - - def test_single_pauli_op(self): - """Two eigenvalues from Pauli sum with X, Y, Z""" - hamiltonian = Z - state_preparation = None - with self.assertWarns(DeprecationWarning): - result = self.hamiltonian_pe(hamiltonian, state_preparation, evolution=None) - - eigv = result.most_likely_eigenvalue - with self.subTest("First eigenvalue"): - self.assertAlmostEqual(eigv, 1.0, delta=0.001) - - with self.assertWarns(DeprecationWarning): - state_preparation = StateFn(X.to_circuit()) - result = self.hamiltonian_pe(hamiltonian, state_preparation, bound=1.05) - - eigv = result.most_likely_eigenvalue - with self.subTest("Second eigenvalue"): - self.assertAlmostEqual(eigv, -0.98, delta=0.01) - - @slow_test - def test_H2_hamiltonian(self): - """Test H2 hamiltonian""" - with self.assertWarns(DeprecationWarning): - hamiltonian = ( - (-1.0523732457728587 * (I ^ I)) - + (0.3979374248431802 * (I ^ Z)) - + (-0.3979374248431802 * (Z ^ I)) - + (-0.011280104256235324 * (Z ^ Z)) - + (0.18093119978423147 * (X ^ X)) - ) - state_preparation = StateFn((I ^ H).to_circuit()) - evo = PauliTrotterEvolution(trotter_mode="suzuki", reps=4) - with self.assertWarns(DeprecationWarning): - result = self.hamiltonian_pe(hamiltonian, state_preparation, evolution=evo) - with self.subTest("Most likely eigenvalues"): - self.assertAlmostEqual(result.most_likely_eigenvalue, -1.855, delta=0.001) - with self.subTest("Most likely phase"): - self.assertAlmostEqual(result.phase, 0.5937, delta=0.001) - with self.subTest("All eigenvalues"): - phase_dict = result.filter_phases(0.1) - phases = list(phase_dict.keys()) - self.assertAlmostEqual(phases[0], -0.8979, delta=0.001) - self.assertAlmostEqual(phases[1], -1.8551, delta=0.001) - self.assertAlmostEqual(phases[2], -1.2376, delta=0.001) - - def test_matrix_evolution(self): - """1Q Hamiltonian with MatrixEvolution""" - with self.assertWarns(DeprecationWarning): - hamiltonian = (0.5 * X) + (0.6 * Y) + (0.7 * I) - state_preparation = None - result = self.hamiltonian_pe( - hamiltonian, state_preparation, evolution=MatrixEvolution() - ) - phase_dict = result.filter_phases(0.2, as_float=True) - phases = list(phase_dict.keys()) - self.assertAlmostEqual(phases[0], 1.490, delta=0.001) - self.assertAlmostEqual(phases[1], -0.090, delta=0.001) - - def _setup_from_bound(self, evolution, op_class): - with self.assertWarns(DeprecationWarning): - hamiltonian = 0.5 * X + Y + Z - state_preparation = None - bound = 1.2 * sum(abs(hamiltonian.coeff * coeff) for coeff in hamiltonian.coeffs) - if op_class == "MatrixOp": - hamiltonian = hamiltonian.to_matrix_op() - backend = qiskit.BasicAer.get_backend("statevector_simulator") - - with self.assertWarns(DeprecationWarning): - qi = qiskit.utils.QuantumInstance(backend=backend, shots=10000) - with self.assertWarns(DeprecationWarning): - phase_est = HamiltonianPhaseEstimation(num_evaluation_qubits=6, quantum_instance=qi) - - result = phase_est.estimate( - hamiltonian=hamiltonian, - bound=bound, - evolution=evolution, - state_preparation=state_preparation, - ) - return result - - def test_from_bound(self): - """HamiltonianPhaseEstimation with bound""" - with self.assertWarns(DeprecationWarning): - for op_class in ("SummedOp", "MatrixOp"): - result = self._setup_from_bound(MatrixEvolution(), op_class) - cutoff = 0.01 - phases = result.filter_phases(cutoff) - with self.subTest(f"test phases has the correct length: {op_class}"): - self.assertEqual(len(phases), 2) - with self.subTest(f"test scaled phases are correct: {op_class}"): - self.assertEqual(list(phases.keys()), [1.5, -1.5]) - phases = result.filter_phases(cutoff, scaled=False) - with self.subTest(f"test unscaled phases are correct: {op_class}"): - self.assertEqual(list(phases.keys()), [0.25, 0.75]) - - def test_trotter_from_bound(self): - """HamiltonianPhaseEstimation with bound and Trotterization""" - with self.assertWarns(DeprecationWarning): - result = self._setup_from_bound( - PauliTrotterEvolution(trotter_mode="suzuki", reps=3), op_class="SummedOp" - ) - phase_dict = result.filter_phases(0.1) - phases = list(phase_dict.keys()) - with self.subTest("test phases has the correct length"): - self.assertEqual(len(phases), 2) - with self.subTest("test phases has correct values"): - self.assertAlmostEqual(phases[0], 1.5, delta=0.001) - self.assertAlmostEqual(phases[1], -1.5, delta=0.001) - - # sampler tests - def hamiltonian_pe_sampler( - self, - hamiltonian, - state_preparation=None, - num_evaluation_qubits=6, - evolution=None, - bound=None, - uses_opflow=True, - ): - """Run HamiltonianPhaseEstimation and return result with all phases.""" - sampler = Sampler() - phase_est = HamiltonianPhaseEstimation( - num_evaluation_qubits=num_evaluation_qubits, sampler=sampler - ) - if uses_opflow: - with self.assertWarns(DeprecationWarning): - result = phase_est.estimate( - hamiltonian=hamiltonian, - state_preparation=state_preparation, - evolution=evolution, - bound=bound, - ) - else: - result = phase_est.estimate( - hamiltonian=hamiltonian, - state_preparation=state_preparation, - evolution=evolution, - bound=bound, - ) - return result - - @data(MatrixExponential(), SuzukiTrotter(reps=4)) - def test_pauli_sum_1_sampler(self, evolution): - """Two eigenvalues from Pauli sum with X, Z""" - with self.assertWarns(DeprecationWarning): - hamiltonian = PauliSumOp(SparsePauliOp.from_list([("X", 0.5), ("Z", 1)])) - state_preparation = QuantumCircuit(1).compose(HGate()) - - result = self.hamiltonian_pe_sampler(hamiltonian, state_preparation, evolution=evolution) - phase_dict = result.filter_phases(0.162, as_float=True) - phases = list(phase_dict.keys()) - phases.sort() - - self.assertAlmostEqual(phases[0], -1.125, delta=0.001) - self.assertAlmostEqual(phases[1], 1.125, delta=0.001) - - @data(MatrixExponential(), SuzukiTrotter(reps=3)) - def test_pauli_sum_2_sampler(self, evolution): - """Two eigenvalues from Pauli sum with X, Y, Z""" - with self.assertWarns(DeprecationWarning): - hamiltonian = PauliSumOp(SparsePauliOp.from_list([("X", 0.5), ("Z", 1), ("Y", 1)])) - state_preparation = None - - result = self.hamiltonian_pe_sampler(hamiltonian, state_preparation, evolution=evolution) - phase_dict = result.filter_phases(0.1, as_float=True) - phases = list(phase_dict.keys()) - phases.sort() - - self.assertAlmostEqual(phases[0], -1.484, delta=0.001) - self.assertAlmostEqual(phases[1], 1.484, delta=0.001) - - def test_single_pauli_op_sampler(self): - """Two eigenvalues from Pauli sum with X, Y, Z""" - hamiltonian = SparsePauliOp(Pauli("Z")) - state_preparation = None - - result = self.hamiltonian_pe_sampler( - hamiltonian, state_preparation, evolution=None, uses_opflow=False - ) - eigv = result.most_likely_eigenvalue - with self.subTest("First eigenvalue"): - self.assertAlmostEqual(eigv, 1.0, delta=0.001) - - state_preparation = QuantumCircuit(1).compose(XGate()) - - result = self.hamiltonian_pe_sampler( - hamiltonian, state_preparation, bound=1.05, uses_opflow=False - ) - eigv = result.most_likely_eigenvalue - with self.subTest("Second eigenvalue"): - self.assertAlmostEqual(eigv, -0.98, delta=0.01) - - @data( - Statevector(QuantumCircuit(2).compose(IGate()).compose(HGate())), - QuantumCircuit(2).compose(IGate()).compose(HGate()), - ) - def test_H2_hamiltonian_sampler(self, state_preparation): - """Test H2 hamiltonian""" - - with self.assertWarns(DeprecationWarning): - hamiltonian = PauliSumOp( - SparsePauliOp.from_list( - [ - ("II", -1.0523732457728587), - ("IZ", 0.3979374248431802), - ("ZI", -0.3979374248431802), - ("ZZ", -0.011280104256235324), - ("XX", 0.18093119978423147), - ] - ) - ) - - evo = SuzukiTrotter(reps=4) - result = self.hamiltonian_pe_sampler(hamiltonian, state_preparation, evolution=evo) - with self.subTest("Most likely eigenvalues"): - self.assertAlmostEqual(result.most_likely_eigenvalue, -1.855, delta=0.001) - with self.subTest("Most likely phase"): - self.assertAlmostEqual(result.phase, 0.5937, delta=0.001) - with self.subTest("All eigenvalues"): - phase_dict = result.filter_phases(0.1) - phases = sorted(phase_dict.keys()) - self.assertAlmostEqual(phases[0], -1.8551, delta=0.001) - self.assertAlmostEqual(phases[1], -1.2376, delta=0.001) - self.assertAlmostEqual(phases[2], -0.8979, delta=0.001) - - def test_matrix_evolution_sampler(self): - """1Q Hamiltonian with MatrixEvolution""" - with self.assertWarns(DeprecationWarning): - hamiltonian = PauliSumOp(SparsePauliOp.from_list([("X", 0.5), ("Y", 0.6), ("I", 0.7)])) - state_preparation = None - result = self.hamiltonian_pe_sampler( - hamiltonian, state_preparation, evolution=MatrixExponential() - ) - phase_dict = result.filter_phases(0.2, as_float=True) - phases = sorted(phase_dict.keys()) - self.assertAlmostEqual(phases[0], -0.090, delta=0.001) - self.assertAlmostEqual(phases[1], 1.490, delta=0.001) - - -@ddt -class TestPhaseEstimation(QiskitAlgorithmsTestCase): - """Evolution tests.""" - - def one_phase( - self, - unitary_circuit, - state_preparation=None, - backend_type=None, - phase_estimator=None, - num_iterations=6, - ): - """Run phase estimation with operator, eigenvalue pair `unitary_circuit`, - `state_preparation`. Return the estimated phase as a value in :math:`[0,1)`. - """ - if backend_type is None: - backend_type = "qasm_simulator" - backend = qiskit.BasicAer.get_backend(backend_type) - if phase_estimator is None: - phase_estimator = IterativePhaseEstimation - - with self.assertWarns(DeprecationWarning): - qi = qiskit.utils.QuantumInstance(backend=backend, shots=10000) - - with self.assertWarns(DeprecationWarning): - - if phase_estimator == IterativePhaseEstimation: - p_est = IterativePhaseEstimation(num_iterations=num_iterations, quantum_instance=qi) - elif phase_estimator == PhaseEstimation: - p_est = PhaseEstimation(num_evaluation_qubits=6, quantum_instance=qi) - else: - raise ValueError("Unrecognized phase_estimator") - - result = p_est.estimate(unitary=unitary_circuit, state_preparation=state_preparation) - phase = result.phase - return phase - - @data( - (X.to_circuit(), 0.5, "statevector_simulator", IterativePhaseEstimation), - (X.to_circuit(), 0.5, "qasm_simulator", IterativePhaseEstimation), - (None, 0.0, "qasm_simulator", IterativePhaseEstimation), - (X.to_circuit(), 0.5, "qasm_simulator", PhaseEstimation), - (None, 0.0, "qasm_simulator", PhaseEstimation), - (X.to_circuit(), 0.5, "statevector_simulator", PhaseEstimation), - ) - @unpack - def test_qpe_Z(self, state_preparation, expected_phase, backend_type, phase_estimator): - """eigenproblem Z, |0> and |1>""" - unitary_circuit = Z.to_circuit() - with self.assertWarns(DeprecationWarning): - phase = self.one_phase( - unitary_circuit, - state_preparation, - backend_type=backend_type, - phase_estimator=phase_estimator, - ) - self.assertEqual(phase, expected_phase) - - @data( - (H.to_circuit(), 0.0, IterativePhaseEstimation), - ((H @ X).to_circuit(), 0.5, IterativePhaseEstimation), - (H.to_circuit(), 0.0, PhaseEstimation), - ((H @ X).to_circuit(), 0.5, PhaseEstimation), - ) - @unpack - def test_qpe_X_plus_minus(self, state_preparation, expected_phase, phase_estimator): - """eigenproblem X, (|+>, |->)""" - unitary_circuit = X.to_circuit() - with self.assertWarns(DeprecationWarning): - phase = self.one_phase( - unitary_circuit, state_preparation, phase_estimator=phase_estimator - ) - self.assertEqual(phase, expected_phase) - - @data( - (X.to_circuit(), 0.125, IterativePhaseEstimation), - (I.to_circuit(), 0.875, IterativePhaseEstimation), - (X.to_circuit(), 0.125, PhaseEstimation), - (I.to_circuit(), 0.875, PhaseEstimation), - ) - @unpack - def test_qpe_RZ(self, state_preparation, expected_phase, phase_estimator): - """eigenproblem RZ, (|0>, |1>)""" - alpha = np.pi / 2 - unitary_circuit = QuantumCircuit(1) - unitary_circuit.rz(alpha, 0) - with self.assertWarns(DeprecationWarning): - phase = self.one_phase( - unitary_circuit, state_preparation, phase_estimator=phase_estimator - ) - self.assertEqual(phase, expected_phase) - - def test_check_num_iterations(self): - """test check for num_iterations greater than zero""" - unitary_circuit = X.to_circuit() - state_preparation = None - with self.assertRaises(ValueError): - self.one_phase(unitary_circuit, state_preparation, num_iterations=-1) - - def phase_estimation( - self, - unitary_circuit, - state_preparation=None, - num_evaluation_qubits=6, - backend=None, - construct_circuit=False, - ): - """Run phase estimation with operator, eigenvalue pair `unitary_circuit`, - `state_preparation`. Return all results - """ - if backend is None: - backend = qiskit.BasicAer.get_backend("statevector_simulator") - - with self.assertWarns(DeprecationWarning): - qi = qiskit.utils.QuantumInstance(backend=backend, shots=10000) - with self.assertWarns(DeprecationWarning): - phase_est = PhaseEstimation( - num_evaluation_qubits=num_evaluation_qubits, quantum_instance=qi - ) - if construct_circuit: - pe_circuit = phase_est.construct_circuit(unitary_circuit, state_preparation) - result = phase_est.estimate_from_pe_circuit(pe_circuit, unitary_circuit.num_qubits) - else: - result = phase_est.estimate( - unitary=unitary_circuit, state_preparation=state_preparation - ) - - return result - - @data(True, False) - def test_qpe_Zplus(self, construct_circuit): - """superposition eigenproblem Z, |+>""" - unitary_circuit = Z.to_circuit() - state_preparation = H.to_circuit() # prepare |+> - - with self.assertWarns(DeprecationWarning): - result = self.phase_estimation( - unitary_circuit, - state_preparation, - backend=qiskit.BasicAer.get_backend("statevector_simulator"), - construct_circuit=construct_circuit, - ) - - phases = result.filter_phases(1e-15, as_float=True) - with self.subTest("test phases has correct values"): - self.assertEqual(list(phases.keys()), [0.0, 0.5]) - - with self.subTest("test phases has correct probabilities"): - np.testing.assert_allclose(list(phases.values()), [0.5, 0.5]) - - with self.subTest("test bitstring representation"): - phases = result.filter_phases(1e-15, as_float=False) - self.assertEqual(list(phases.keys()), ["000000", "100000"]) - - # sampler tests - def one_phase_sampler( - self, - unitary_circuit, - state_preparation=None, - phase_estimator=None, - num_iterations=6, - shots=None, - ): - """Run phase estimation with operator, eigenvalue pair `unitary_circuit`, - `state_preparation`. Return the estimated phase as a value in :math:`[0,1)`. - """ - if shots is not None: - options = {"shots": shots} - else: - options = {} - sampler = Sampler(options=options) - if phase_estimator is None: - phase_estimator = IterativePhaseEstimation - if phase_estimator == IterativePhaseEstimation: - p_est = IterativePhaseEstimation(num_iterations=num_iterations, sampler=sampler) - elif phase_estimator == PhaseEstimation: - p_est = PhaseEstimation(num_evaluation_qubits=6, sampler=sampler) - else: - raise ValueError("Unrecognized phase_estimator") - result = p_est.estimate(unitary=unitary_circuit, state_preparation=state_preparation) - phase = result.phase - return phase - - @data( - (QuantumCircuit(1).compose(XGate()), 0.5, None, IterativePhaseEstimation), - (QuantumCircuit(1).compose(XGate()), 0.5, 1000, IterativePhaseEstimation), - (None, 0.0, 1000, IterativePhaseEstimation), - (QuantumCircuit(1).compose(XGate()), 0.5, 1000, PhaseEstimation), - (None, 0.0, 1000, PhaseEstimation), - (QuantumCircuit(1).compose(XGate()), 0.5, None, PhaseEstimation), - ) - @unpack - def test_qpe_Z_sampler(self, state_preparation, expected_phase, shots, phase_estimator): - """eigenproblem Z, |0> and |1>""" - unitary_circuit = QuantumCircuit(1).compose(ZGate()) - phase = self.one_phase_sampler( - unitary_circuit, - state_preparation=state_preparation, - phase_estimator=phase_estimator, - shots=shots, - ) - self.assertEqual(phase, expected_phase) - - @data( - (QuantumCircuit(1).compose(HGate()), 0.0, IterativePhaseEstimation), - (QuantumCircuit(1).compose(HGate()).compose(ZGate()), 0.5, IterativePhaseEstimation), - (QuantumCircuit(1).compose(HGate()), 0.0, PhaseEstimation), - (QuantumCircuit(1).compose(HGate()).compose(ZGate()), 0.5, PhaseEstimation), - ) - @unpack - def test_qpe_X_plus_minus_sampler(self, state_preparation, expected_phase, phase_estimator): - """eigenproblem X, (|+>, |->)""" - unitary_circuit = QuantumCircuit(1).compose(XGate()) - phase = self.one_phase_sampler( - unitary_circuit, - state_preparation, - phase_estimator, - ) - self.assertEqual(phase, expected_phase) - - @data( - (QuantumCircuit(1).compose(XGate()), 0.125, IterativePhaseEstimation), - (QuantumCircuit(1).compose(IGate()), 0.875, IterativePhaseEstimation), - (QuantumCircuit(1).compose(XGate()), 0.125, PhaseEstimation), - (QuantumCircuit(1).compose(IGate()), 0.875, PhaseEstimation), - ) - @unpack - def test_qpe_RZ_sampler(self, state_preparation, expected_phase, phase_estimator): - """eigenproblem RZ, (|0>, |1>)""" - alpha = np.pi / 2 - unitary_circuit = QuantumCircuit(1) - unitary_circuit.rz(alpha, 0) - phase = self.one_phase_sampler( - unitary_circuit, - state_preparation, - phase_estimator, - ) - self.assertEqual(phase, expected_phase) - - @data( - ((X ^ X).to_circuit(), 0.25, IterativePhaseEstimation), - ((I ^ X).to_circuit(), 0.125, IterativePhaseEstimation), - ((X ^ X).to_circuit(), 0.25, PhaseEstimation), - ((I ^ X).to_circuit(), 0.125, PhaseEstimation), - ) - @unpack - def test_qpe_two_qubit_unitary(self, state_preparation, expected_phase, phase_estimator): - """two qubit unitary T ^ T""" - unitary_circuit = QuantumCircuit(2) - unitary_circuit.t(0) - unitary_circuit.t(1) - phase = self.one_phase_sampler( - unitary_circuit, - state_preparation, - phase_estimator, - ) - self.assertEqual(phase, expected_phase) - - def test_check_num_iterations_sampler(self): - """test check for num_iterations greater than zero""" - unitary_circuit = QuantumCircuit(1).compose(XGate()) - state_preparation = None - with self.assertRaises(ValueError): - self.one_phase_sampler(unitary_circuit, state_preparation, num_iterations=-1) - - def test_phase_estimation_scale_from_operator(self): - """test that PhaseEstimationScale from_pauli_sum works with Operator""" - circ = QuantumCircuit(2) - op = Operator(circ) - scale = PhaseEstimationScale.from_pauli_sum(op) - self.assertEqual(scale._bound, 4.0) - - def phase_estimation_sampler( - self, - unitary_circuit, - sampler: Sampler, - state_preparation=None, - num_evaluation_qubits=6, - construct_circuit=False, - ): - """Run phase estimation with operator, eigenvalue pair `unitary_circuit`, - `state_preparation`. Return all results - """ - phase_est = PhaseEstimation(num_evaluation_qubits=num_evaluation_qubits, sampler=sampler) - if construct_circuit: - pe_circuit = phase_est.construct_circuit(unitary_circuit, state_preparation) - result = phase_est.estimate_from_pe_circuit(pe_circuit, unitary_circuit.num_qubits) - else: - result = phase_est.estimate( - unitary=unitary_circuit, state_preparation=state_preparation - ) - return result - - @data(True, False) - def test_qpe_Zplus_sampler(self, construct_circuit): - """superposition eigenproblem Z, |+>""" - unitary_circuit = QuantumCircuit(1).compose(ZGate()) - state_preparation = QuantumCircuit(1).compose(HGate()) # prepare |+> - sampler = Sampler() - result = self.phase_estimation_sampler( - unitary_circuit, - sampler, - state_preparation, - construct_circuit=construct_circuit, - ) - - phases = result.filter_phases(1e-15, as_float=True) - with self.subTest("test phases has correct values"): - self.assertEqual(list(phases.keys()), [0.0, 0.5]) - - with self.subTest("test phases has correct probabilities"): - np.testing.assert_allclose(list(phases.values()), [0.5, 0.5]) - - with self.subTest("test bitstring representation"): - phases = result.filter_phases(1e-15, as_float=False) - self.assertEqual(list(phases.keys()), ["000000", "100000"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_qaoa.py b/test/python/algorithms/test_qaoa.py deleted file mode 100644 index 5edb525022a3..000000000000 --- a/test/python/algorithms/test_qaoa.py +++ /dev/null @@ -1,410 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test QAOA""" - -import unittest -import warnings -from test.python.algorithms import QiskitAlgorithmsTestCase - -from functools import partial -import math -import numpy as np -from scipy.optimize import minimize as scipy_minimize -from ddt import ddt, idata, unpack -import rustworkx as rx - -from qiskit.algorithms import QAOA -from qiskit.algorithms.optimizers import COBYLA, NELDER_MEAD - -from qiskit.opflow import I, X, Z, PauliSumOp - -from qiskit import BasicAer, QuantumCircuit, QuantumRegister - -from qiskit.circuit import Parameter -from qiskit.quantum_info import Pauli -from qiskit.utils import QuantumInstance, algorithm_globals - -W1 = np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]) -P1 = 1 -M1 = (I ^ I ^ I ^ X) + (I ^ I ^ X ^ I) + (I ^ X ^ I ^ I) + (X ^ I ^ I ^ I) -S1 = {"0101", "1010"} - - -W2 = np.array( - [ - [0.0, 8.0, -9.0, 0.0], - [8.0, 0.0, 7.0, 9.0], - [-9.0, 7.0, 0.0, -8.0], - [0.0, 9.0, -8.0, 0.0], - ] -) -P2 = 1 -M2 = None -S2 = {"1011", "0100"} - -CUSTOM_SUPERPOSITION = [1 / math.sqrt(15)] * 15 + [0] - - -@ddt -class TestQAOA(QiskitAlgorithmsTestCase): - """Test QAOA with MaxCut.""" - - def setUp(self): - super().setUp() - self.seed = 10598 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - - with self.assertWarns(DeprecationWarning): - self.qasm_simulator = QuantumInstance( - BasicAer.get_backend("qasm_simulator"), - shots=4096, - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - self.statevector_simulator = QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - - @idata( - [ - [W1, P1, M1, S1, False], - [W2, P2, M2, S2, False], - [W1, P1, M1, S1, True], - [W2, P2, M2, S2, True], - ] - ) - @unpack - def test_qaoa(self, w, prob, m, solutions, convert_to_matrix_op): - """QAOA test""" - self.log.debug("Testing %s-step QAOA with MaxCut on graph\n%s", prob, w) - - qubit_op, _ = self._get_operator(w) - - if convert_to_matrix_op: - with self.assertWarns(DeprecationWarning): - qubit_op = qubit_op.to_matrix_op() - - with self.assertWarns(DeprecationWarning): - qaoa = QAOA(COBYLA(), prob, mixer=m, quantum_instance=self.statevector_simulator) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, solutions) - - @idata( - [ - [W1, P1, S1, False], - [W2, P2, S2, False], - [W1, P1, S1, True], - [W2, P2, S2, True], - ] - ) - @unpack - def test_qaoa_qc_mixer(self, w, prob, solutions, convert_to_matrix_op): - """QAOA test with a mixer as a parameterized circuit""" - self.log.debug( - "Testing %s-step QAOA with MaxCut on graph with a mixer as a parameterized circuit\n%s", - prob, - w, - ) - - optimizer = COBYLA() - qubit_op, _ = self._get_operator(w) - if convert_to_matrix_op: - with self.assertWarns(DeprecationWarning): - qubit_op = qubit_op.to_matrix_op() - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - theta = Parameter("θ") - mixer.rx(theta, range(num_qubits)) - - with self.assertWarns(DeprecationWarning): - qaoa = QAOA(optimizer, prob, mixer=mixer, quantum_instance=self.statevector_simulator) - - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, solutions) - - def test_qaoa_qc_mixer_many_parameters(self): - """QAOA test with a mixer as a parameterized circuit with the num of parameters > 1.""" - optimizer = COBYLA() - qubit_op, _ = self._get_operator(W1) - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - for i in range(num_qubits): - theta = Parameter("θ" + str(i)) - mixer.rx(theta, range(num_qubits)) - - with self.assertWarns(DeprecationWarning): - qaoa = QAOA(optimizer, reps=2, mixer=mixer, quantum_instance=self.statevector_simulator) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - x = self._sample_most_likely(result.eigenstate) - self.log.debug(x) - graph_solution = self._get_graph_solution(x) - self.assertIn(graph_solution, S1) - - def test_qaoa_qc_mixer_no_parameters(self): - """QAOA test with a mixer as a parameterized circuit with zero parameters.""" - qubit_op, _ = self._get_operator(W1) - - num_qubits = qubit_op.num_qubits - mixer = QuantumCircuit(num_qubits) - # just arbitrary circuit - mixer.rx(np.pi / 2, range(num_qubits)) - - with self.assertWarns(DeprecationWarning): - qaoa = QAOA(COBYLA(), reps=1, mixer=mixer, quantum_instance=self.statevector_simulator) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - # we just assert that we get a result, it is not meaningful. - self.assertIsNotNone(result.eigenstate) - - def test_change_operator_size(self): - """QAOA change operator size test""" - qubit_op, _ = self._get_operator( - np.array([[0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0]]) - ) - with self.assertWarns(DeprecationWarning): - qaoa = QAOA(COBYLA(), 1, quantum_instance=self.statevector_simulator) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - with self.subTest(msg="QAOA 4x4"): - self.assertIn(graph_solution, {"0101", "1010"}) - - qubit_op, _ = self._get_operator( - np.array( - [ - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1], - [1, 0, 1, 0, 1, 0], - ] - ) - ) - with self.assertWarns(DeprecationWarning): - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - with self.subTest(msg="QAOA 6x6"): - self.assertIn(graph_solution, {"010101", "101010"}) - - @idata([[W2, S2, None], [W2, S2, [0.0, 0.0]], [W2, S2, [1.0, 0.8]]]) - @unpack - def test_qaoa_initial_point(self, w, solutions, init_pt): - """Check first parameter value used is initial point as expected""" - qubit_op, _ = self._get_operator(w) - - first_pt = [] - - def cb_callback(eval_count, parameters, mean, std): - nonlocal first_pt - if eval_count == 1: - first_pt = list(parameters) - - with self.assertWarns(DeprecationWarning): - qaoa = QAOA( - COBYLA(), - initial_point=init_pt, - callback=cb_callback, - quantum_instance=self.statevector_simulator, - ) - - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - x = self._sample_most_likely(result.eigenstate) - graph_solution = self._get_graph_solution(x) - - with self.subTest("Initial Point"): - # If None the preferred random initial point of QAOA variational form - if init_pt is None: - self.assertLess(result.eigenvalue, -0.97) - else: - self.assertListEqual(init_pt, first_pt) - - with self.subTest("Solution"): - self.assertIn(graph_solution, solutions) - - @idata([[W2, None], [W2, [1.0] + 15 * [0.0]], [W2, CUSTOM_SUPERPOSITION]]) - @unpack - def test_qaoa_initial_state(self, w, init_state): - """QAOA initial state test""" - optimizer = COBYLA() - qubit_op, _ = self._get_operator(w) - - init_pt = np.asarray([0.0, 0.0]) # Avoid generating random initial point - - if init_state is None: - initial_state = None - else: - initial_state = QuantumCircuit(QuantumRegister(4, "q")) - initial_state.initialize(init_state, initial_state.qubits) - - zero_init_state = QuantumCircuit(QuantumRegister(qubit_op.num_qubits, "q")) - - with self.assertWarns(DeprecationWarning): - qaoa_zero_init_state = QAOA( - optimizer=optimizer, - initial_state=zero_init_state, - initial_point=init_pt, - quantum_instance=self.statevector_simulator, - ) - qaoa = QAOA( - optimizer=optimizer, - initial_state=initial_state, - initial_point=init_pt, - quantum_instance=self.statevector_simulator, - ) - zero_circuits = qaoa_zero_init_state.construct_circuit(init_pt, qubit_op) - custom_circuits = qaoa.construct_circuit(init_pt, qubit_op) - - self.assertEqual(len(zero_circuits), len(custom_circuits)) - - for zero_circ, custom_circ in zip(zero_circuits, custom_circuits): - - z_length = len(zero_circ.data) - c_length = len(custom_circ.data) - - self.assertGreaterEqual(c_length, z_length) - self.assertTrue(zero_circ.data == custom_circ.data[-z_length:]) - - custom_init_qc = QuantumCircuit(custom_circ.num_qubits) - custom_init_qc.data = custom_circ.data[0 : c_length - z_length] - - if initial_state is None: - original_init_qc = QuantumCircuit(qubit_op.num_qubits) - original_init_qc.h(range(qubit_op.num_qubits)) - else: - original_init_qc = initial_state - - with self.assertWarns(DeprecationWarning): - job_init_state = self.statevector_simulator.execute(original_init_qc) - job_qaoa_init_state = self.statevector_simulator.execute(custom_init_qc) - - statevector_original = job_init_state.get_statevector(original_init_qc) - statevector_custom = job_qaoa_init_state.get_statevector(custom_init_qc) - - self.assertListEqual(statevector_original.tolist(), statevector_custom.tolist()) - - def test_qaoa_random_initial_point(self): - """QAOA random initial point""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - w = rx.adjacency_matrix( - rx.undirected_gnp_random_graph(5, 0.5, seed=algorithm_globals.random_seed) - ) - qubit_op, _ = self._get_operator(w) - - with self.assertWarns(DeprecationWarning): - qaoa = QAOA( - optimizer=NELDER_MEAD(disp=True), reps=1, quantum_instance=self.qasm_simulator - ) - result = qaoa.compute_minimum_eigenvalue(operator=qubit_op) - - self.assertLess(result.eigenvalue, -0.97) - - def test_qaoa_construct_circuit_update(self): - """Test updating operators with QAOA construct_circuit""" - with self.assertWarns(DeprecationWarning): - qaoa = QAOA() - ref = qaoa.construct_circuit([0, 0], I ^ Z)[0] - circ2 = qaoa.construct_circuit([0, 0], I ^ Z)[0] - self.assertEqual(circ2, ref) - circ3 = qaoa.construct_circuit([0, 0], Z ^ I)[0] - self.assertNotEqual(circ3, ref) - circ4 = qaoa.construct_circuit([0, 0], I ^ Z)[0] - self.assertEqual(circ4, ref) - - def test_optimizer_scipy_callable(self): - """Test passing a SciPy optimizer directly as callable.""" - with self.assertWarns(DeprecationWarning): - qaoa = QAOA( - optimizer=partial(scipy_minimize, method="Nelder-Mead", options={"maxiter": 2}), - quantum_instance=self.statevector_simulator, - ) - result = qaoa.compute_minimum_eigenvalue(Z) - self.assertEqual(result.cost_function_evals, 4) - - def _get_operator(self, weight_matrix): - """Generate Hamiltonian for the max-cut problem of a graph. - - Args: - weight_matrix (numpy.ndarray) : adjacency matrix. - - Returns: - PauliSumOp: operator for the Hamiltonian - float: a constant shift for the obj function. - - """ - num_nodes = weight_matrix.shape[0] - pauli_list = [] - shift = 0 - for i in range(num_nodes): - for j in range(i): - if weight_matrix[i, j] != 0: - x_p = np.zeros(num_nodes, dtype=bool) - z_p = np.zeros(num_nodes, dtype=bool) - z_p[i] = True - z_p[j] = True - pauli_list.append([0.5 * weight_matrix[i, j], Pauli((z_p, x_p))]) - shift -= 0.5 * weight_matrix[i, j] - opflow_list = [(pauli[1].to_label(), pauli[0]) for pauli in pauli_list] - - with self.assertWarns(DeprecationWarning): - return PauliSumOp.from_list(opflow_list), shift - - def _get_graph_solution(self, x: np.ndarray) -> str: - """Get graph solution from binary string. - - Args: - x : binary string as numpy array. - - Returns: - a graph solution as string. - """ - - return "".join([str(int(i)) for i in 1 - x]) - - def _sample_most_likely(self, state_vector): - """Compute the most likely binary string from state vector. - Args: - state_vector (numpy.ndarray or dict): state vector or counts. - - Returns: - numpy.ndarray: binary string as numpy.ndarray of ints. - """ - n = int(np.log2(state_vector.shape[0])) - k = np.argmax(np.abs(state_vector)) - x = np.zeros(n) - for i in range(n): - x[i] = k % 2 - k >>= 1 - return x - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_skip_qobj_validation.py b/test/python/algorithms/test_skip_qobj_validation.py index c5af2b7dc279..4ebf8a0fb7b0 100644 --- a/test/python/algorithms/test_skip_qobj_validation.py +++ b/test/python/algorithms/test_skip_qobj_validation.py @@ -107,7 +107,7 @@ def test_w_noise(self): # Asymmetric readout error on qubit-0 only try: from qiskit.providers.aer.noise import NoiseModel - from qiskit import Aer + from qiskit_aer import Aer self.backend = Aer.get_backend("qasm_simulator") diff --git a/test/python/algorithms/test_validation.py b/test/python/algorithms/test_validation.py deleted file mode 100644 index 90a1b9eed143..000000000000 --- a/test/python/algorithms/test_validation.py +++ /dev/null @@ -1,94 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Validation""" - -import unittest - -from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.utils.validation import ( - validate_in_set, - validate_min, - validate_min_exclusive, - validate_max, - validate_max_exclusive, - validate_range, - validate_range_exclusive, - validate_range_exclusive_min, - validate_range_exclusive_max, -) - - -class TestValidation(QiskitAlgorithmsTestCase): - """Validation tests.""" - - def test_validate_in_set(self): - """validate in set test""" - test_value = "value1" - with self.assertWarns(DeprecationWarning): - validate_in_set("test_value", test_value, {"value1", "value2"}) - with self.assertRaises(ValueError): - validate_in_set("test_value", test_value, {"value3", "value4"}) - - def test_validate_min(self): - """validate min test""" - test_value = 2.5 - with self.assertWarns(DeprecationWarning): - validate_min("test_value", test_value, -1) - validate_min("test_value", test_value, 2.5) - with self.assertRaises(ValueError): - validate_min("test_value", test_value, 4) - validate_min_exclusive("test_value", test_value, -1) - with self.assertRaises(ValueError): - validate_min_exclusive("test_value", test_value, 2.5) - with self.assertRaises(ValueError): - validate_min_exclusive("test_value", test_value, 4) - - def test_validate_max(self): - """validate max test""" - test_value = 2.5 - with self.assertWarns(DeprecationWarning): - with self.assertRaises(ValueError): - validate_max("test_value", test_value, -1) - validate_max("test_value", test_value, 2.5) - validate_max("test_value", test_value, 4) - with self.assertRaises(ValueError): - validate_max_exclusive("test_value", test_value, -1) - with self.assertRaises(ValueError): - validate_max_exclusive("test_value", test_value, 2.5) - validate_max_exclusive("test_value", test_value, 4) - - def test_validate_range(self): - """validate range test""" - test_value = 2.5 - with self.assertWarns(DeprecationWarning): - with self.assertRaises(ValueError): - validate_range("test_value", test_value, 0, 2) - with self.assertRaises(ValueError): - validate_range("test_value", test_value, 3, 4) - validate_range("test_value", test_value, 2.5, 3) - validate_range_exclusive("test_value", test_value, 0, 3) - with self.assertRaises(ValueError): - validate_range_exclusive("test_value", test_value, 0, 2.5) - validate_range_exclusive("test_value", test_value, 2.5, 3) - validate_range_exclusive_min("test_value", test_value, 0, 3) - with self.assertRaises(ValueError): - validate_range_exclusive_min("test_value", test_value, 2.5, 3) - validate_range_exclusive_min("test_value", test_value, 0, 2.5) - validate_range_exclusive_max("test_value", test_value, 2.5, 3) - with self.assertRaises(ValueError): - validate_range_exclusive_max("test_value", test_value, 0, 2.5) - validate_range_exclusive_max("test_value", test_value, 2.5, 3) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_vqd.py b/test/python/algorithms/test_vqd.py deleted file mode 100644 index e153ba50ddb7..000000000000 --- a/test/python/algorithms/test_vqd.py +++ /dev/null @@ -1,663 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test VQD""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -import numpy as np -from ddt import data, ddt, unpack - -from qiskit import BasicAer, QuantumCircuit -from qiskit.algorithms import VQD, AlgorithmError -from qiskit.algorithms.optimizers import ( - COBYLA, - L_BFGS_B, - SLSQP, -) -from qiskit.circuit.library import EfficientSU2, RealAmplitudes, TwoLocal -from qiskit.exceptions import MissingOptionalLibraryError -from qiskit.opflow import ( - AerPauliExpectation, - I, - MatrixExpectation, - MatrixOp, - PauliExpectation, - PauliSumOp, - PrimitiveOp, - X, - Z, -) - -from qiskit.utils import QuantumInstance, algorithm_globals, has_aer -from qiskit.test import slow_test - - -if has_aer(): - from qiskit import Aer - - -@ddt -class TestVQD(QiskitAlgorithmsTestCase): - """Test VQD""" - - def setUp(self): - super().setUp() - self.seed = 50 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - self.h2_energy = -1.85727503 - self.h2_energy_excited = [-1.85727503, -1.24458455] - - self.test_results = [-3, -1] - - self.ryrz_wavefunction = TwoLocal( - rotation_blocks=["ry", "rz"], entanglement_blocks="cz", reps=1 - ) - self.ry_wavefunction = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz") - - with self.assertWarns(DeprecationWarning): - self.h2_op = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - self.test_op = MatrixOp(np.diagflat([3, 5, -1, 0.8, 0.2, 2, 1, -3])).to_pauli_op() - self.qasm_simulator = QuantumInstance( - BasicAer.get_backend("qasm_simulator"), - shots=2048, - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - self.statevector_simulator = QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - shots=1, - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - - @slow_test - def test_basic_aer_statevector(self): - """Test the VQD on BasicAer's statevector simulator.""" - wavefunction = self.ryrz_wavefunction - - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=2, - ansatz=wavefunction, - optimizer=COBYLA(), - quantum_instance=QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - basis_gates=["u1", "u2", "u3", "cx", "id"], - coupling_map=[[0, 1]], - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ), - ) - - result = vqd.compute_eigenvalues(operator=self.h2_op) - - with self.subTest(msg="test eigenvalue"): - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=1 - ) - - with self.subTest(msg="test dimension of optimal point"): - self.assertEqual(len(result.optimal_point[-1]), 8) - - with self.subTest(msg="assert cost_function_evals is set"): - self.assertIsNotNone(result.cost_function_evals) - - with self.subTest(msg="assert optimizer_time is set"): - self.assertIsNotNone(result.optimizer_time) - - def test_mismatching_num_qubits(self): - """Ensuring circuit and operator mismatch is caught""" - wavefunction = QuantumCircuit(1) - optimizer = SLSQP(maxiter=50) - - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=1, - ansatz=wavefunction, - optimizer=optimizer, - quantum_instance=self.statevector_simulator, - ) - with self.assertRaises(AlgorithmError): - _ = vqd.compute_eigenvalues(operator=self.h2_op) - - @data( - (MatrixExpectation(), 1), - (AerPauliExpectation(), 1), - (PauliExpectation(), 2), - ) - @unpack - def test_construct_circuit(self, expectation, num_circuits): - """Test construct circuits returns QuantumCircuits and the right number of them.""" - try: - wavefunction = EfficientSU2(2, reps=1) - - with self.assertWarns(DeprecationWarning): - vqd = VQD(k=2, ansatz=wavefunction, expectation=expectation) - params = [0] * wavefunction.num_parameters - circuits = vqd.construct_circuit(parameter=params, operator=self.h2_op) - self.assertEqual(len(circuits), num_circuits) - - for circuit in circuits: - self.assertIsInstance(circuit, QuantumCircuit) - - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - return - - def test_missing_varform_params(self): - """Test specifying a variational form with no parameters raises an error.""" - - circuit = QuantumCircuit(self.h2_op.num_qubits) - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=1, ansatz=circuit, quantum_instance=BasicAer.get_backend("statevector_simulator") - ) - with self.assertRaises(RuntimeError): - vqd.compute_eigenvalues(operator=self.h2_op) - - def test_basic_aer_qasm(self): - """Test the VQD on BasicAer's QASM simulator.""" - optimizer = COBYLA(maxiter=1000) - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - vqd = VQD( - ansatz=wavefunction, - optimizer=optimizer, - max_evals_grouped=1, - quantum_instance=self.qasm_simulator, - ) - # TODO benchmark this later. - result = vqd.compute_eigenvalues(operator=self.h2_op) - - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=1 - ) - - @unittest.skipUnless(has_aer(), "qiskit-aer doesn't appear to be installed.") - def test_with_aer_statevector(self): - """Test VQD with Aer's statevector_simulator.""" - backend = Aer.get_backend("aer_simulator_statevector") - wavefunction = self.ry_wavefunction - optimizer = L_BFGS_B() - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=2, - ansatz=wavefunction, - optimizer=optimizer, - max_evals_grouped=1, - quantum_instance=quantum_instance, - ) - result = vqd.compute_eigenvalues(operator=self.h2_op) - - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=2 - ) - - @unittest.skipUnless(has_aer(), "qiskit-aer doesn't appear to be installed.") - def test_with_aer_qasm(self): - """Test VQD with Aer's qasm_simulator.""" - backend = Aer.get_backend("aer_simulator") - optimizer = COBYLA(maxiter=1000) - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=2, - ansatz=wavefunction, - optimizer=optimizer, - expectation=PauliExpectation(), - quantum_instance=quantum_instance, - ) - result = vqd.compute_eigenvalues(operator=self.h2_op) - - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=1 - ) - - @unittest.skipUnless(has_aer(), "qiskit-aer doesn't appear to be installed.") - def test_with_aer_qasm_snapshot_mode(self): - """Test the VQD using Aer's qasm_simulator snapshot mode.""" - - backend = Aer.get_backend("aer_simulator") - optimizer = COBYLA(maxiter=400) - wavefunction = self.ryrz_wavefunction - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend, - shots=100, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=2, - ansatz=wavefunction, - optimizer=optimizer, - expectation=AerPauliExpectation(), - quantum_instance=quantum_instance, - ) - result = vqd.compute_eigenvalues(operator=self.test_op) - - np.testing.assert_array_almost_equal(result.eigenvalues.real, self.test_results, decimal=1) - - def test_callback(self): - """Test the callback on VQD.""" - history = {"eval_count": [], "parameters": [], "mean": [], "std": [], "step": []} - - def store_intermediate_result(eval_count, parameters, mean, std, step): - history["eval_count"].append(eval_count) - history["parameters"].append(parameters) - history["mean"].append(mean) - history["std"].append(std) - history["step"].append(step) - - optimizer = COBYLA(maxiter=3) - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - vqd = VQD( - ansatz=wavefunction, - optimizer=optimizer, - callback=store_intermediate_result, - quantum_instance=self.qasm_simulator, - ) - vqd.compute_eigenvalues(operator=self.h2_op) - - self.assertTrue(all(isinstance(count, int) for count in history["eval_count"])) - self.assertTrue(all(isinstance(mean, float) for mean in history["mean"])) - self.assertTrue(all(isinstance(std, float) for std in history["std"])) - self.assertTrue(all(isinstance(count, int) for count in history["step"])) - for params in history["parameters"]: - self.assertTrue(all(isinstance(param, float) for param in params)) - - ref_eval_count = [1, 2, 3, 1, 2, 3] - ref_mean = [-1.063, -1.457, -1.360, 37.340, 48.543, 28.586] - ref_std = [0.011, 0.010, 0.014, 0.011, 0.010, 0.015] - ref_step = [1, 1, 1, 2, 2, 2] - - np.testing.assert_array_almost_equal(history["eval_count"], ref_eval_count, decimal=0) - np.testing.assert_array_almost_equal(history["mean"], ref_mean, decimal=2) - np.testing.assert_array_almost_equal(history["std"], ref_std, decimal=2) - np.testing.assert_array_almost_equal(history["step"], ref_step, decimal=0) - - def test_reuse(self): - """Test re-using a VQD algorithm instance.""" - - with self.assertWarns(DeprecationWarning): - vqd = VQD(k=1) - - with self.subTest(msg="assert running empty raises AlgorithmError"): - with self.assertWarns(DeprecationWarning), self.assertRaises(AlgorithmError): - _ = vqd.compute_eigenvalues(operator=self.h2_op) - - ansatz = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - vqd.ansatz = ansatz - - with self.subTest(msg="assert missing operator raises AlgorithmError"): - with self.assertWarns(DeprecationWarning), self.assertRaises(AlgorithmError): - _ = vqd.compute_eigenvalues(operator=self.h2_op) - - with self.assertWarns(DeprecationWarning): - vqd.expectation = MatrixExpectation() - vqd.quantum_instance = self.statevector_simulator - - with self.subTest(msg="assert VQE works once all info is available"): - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(operator=self.h2_op) - np.testing.assert_array_almost_equal(result.eigenvalues.real, self.h2_energy, decimal=2) - - with self.assertWarns(DeprecationWarning): - operator = PrimitiveOp( - np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]]) - ) - - with self.subTest(msg="assert minimum eigensolver interface works"): - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(operator=operator) - self.assertAlmostEqual(result.eigenvalues.real[0], -1.0, places=5) - - def test_vqd_optimizer(self): - """Test running same VQD twice to re-use optimizer, then switch optimizer""" - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=2, - optimizer=SLSQP(), - quantum_instance=QuantumInstance(BasicAer.get_backend("statevector_simulator")), - ) - - def run_check(): - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(operator=self.h2_op) - - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=3 - ) - - run_check() - - with self.subTest("Optimizer re-use"): - run_check() - - with self.subTest("Optimizer replace"): - vqd.optimizer = L_BFGS_B() - run_check() - - @data(MatrixExpectation(), None) - def test_backend_change(self, user_expectation): - """Test that VQE works when backend changes.""" - - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=1, - ansatz=TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz"), - optimizer=SLSQP(maxiter=2), - expectation=user_expectation, - quantum_instance=BasicAer.get_backend("statevector_simulator"), - ) - result0 = vqd.compute_eigenvalues(operator=self.h2_op) - if user_expectation is not None: - with self.subTest("User expectation kept."): - self.assertEqual(vqd.expectation, user_expectation) - - with self.assertWarns(DeprecationWarning): - vqd.quantum_instance = BasicAer.get_backend("qasm_simulator") - # works also if no expectation is set, since it will be determined automatically - - result1 = vqd.compute_eigenvalues(operator=self.h2_op) - - if user_expectation is not None: - with self.subTest("Change backend with user expectation, it is kept."): - self.assertEqual(vqd.expectation, user_expectation) - - with self.subTest("Check results."): - self.assertEqual(len(result0.optimal_point), len(result1.optimal_point)) - - def test_set_ansatz_to_none(self): - """Tests that setting the ansatz to None results in the default behavior""" - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=1, - ansatz=self.ryrz_wavefunction, - optimizer=L_BFGS_B(), - quantum_instance=self.statevector_simulator, - ) - vqd.ansatz = None - self.assertIsInstance(vqd.ansatz, RealAmplitudes) - - def test_set_optimizer_to_none(self): - """Tests that setting the optimizer to None results in the default behavior""" - - with self.assertWarns(DeprecationWarning): - vqd = VQD( - k=1, - ansatz=self.ryrz_wavefunction, - optimizer=L_BFGS_B(), - quantum_instance=self.statevector_simulator, - ) - vqd.optimizer = None - self.assertIsInstance(vqd.optimizer, SLSQP) - - def test_aux_operators_list(self): - """Test list-based aux_operators.""" - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - vqd = VQD(k=2, ansatz=wavefunction, quantum_instance=self.statevector_simulator) - - # Start with an empty list - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=[]) - - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=2 - ) - self.assertIsNone(result.aux_operator_eigenvalues) - - # Go again with two auxiliary operators - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=aux_ops) - - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=2 - ) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][0], 2, places=2) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][0], 0, places=2) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][1], 0.0) - - # Go again with additional None and zero operators - extra_ops = [*aux_ops, None, 0] - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=extra_ops) - - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=2 - ) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][0], 2, places=2) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][0], 0, places=2) - self.assertEqual(result.aux_operator_eigenvalues[0][2][0], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[0][3][0], 0.0) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][1], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[0][3][1], 0.0) - - def test_aux_operators_dict(self): - """Test dictionary compatibility of aux_operators""" - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - vqd = VQD(ansatz=wavefunction, quantum_instance=self.statevector_simulator) - - # Start with an empty dictionary - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(self.h2_op, aux_operators={}) - - np.testing.assert_array_almost_equal( - result.eigenvalues.real, self.h2_energy_excited, decimal=2 - ) - self.assertIsNone(result.aux_operator_eigenvalues) - - # Go again with two auxiliary operators - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = {"aux_op1": aux_op1, "aux_op2": aux_op2} - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=aux_ops) - - self.assertEqual(len(result.eigenvalues), 2) - self.assertEqual(len(result.eigenstates), 2) - self.assertEqual(result.eigenvalues.dtype, np.complex128) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - self.assertEqual(len(result.aux_operator_eigenvalues[0]), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op2"][0], 0, places=1) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op1"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op2"][1], 0.0) - - # Go again with additional None and zero operators - extra_ops = {**aux_ops, "None_operator": None, "zero_operator": 0} - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=extra_ops) - self.assertEqual(len(result.eigenvalues), 2) - self.assertEqual(len(result.eigenstates), 2) - self.assertEqual(result.eigenvalues.dtype, np.complex128) - self.assertAlmostEqual(result.eigenvalues[0], -1.85727503) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - self.assertEqual(len(result.aux_operator_eigenvalues[0]), 3) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op2"][0], 0, places=6) - self.assertEqual(result.aux_operator_eigenvalues[0]["zero_operator"][0], 0.0) - self.assertTrue("None_operator" not in result.aux_operator_eigenvalues[0].keys()) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op1"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["aux_op2"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0]["zero_operator"][1], 0.0) - - def test_aux_operator_std_dev_pauli(self): - """Test non-zero standard deviations of aux operators with PauliExpectation.""" - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - vqd = VQD( - ansatz=wavefunction, - expectation=PauliExpectation(), - initial_point=[ - 1.70256666, - -5.34843975, - -0.39542903, - 5.99477786, - -2.74374986, - -4.85284669, - 0.2442925, - -1.51638917, - ], - optimizer=COBYLA(maxiter=0), - quantum_instance=self.qasm_simulator, - ) - - with self.assertWarns(DeprecationWarning): - # Go again with two auxiliary operators - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=aux_ops) - - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][0], 2.0, places=1) - self.assertAlmostEqual( - result.aux_operator_eigenvalues[0][1][0], 0.0019531249999999445, places=1 - ) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][1], 0.0) - self.assertAlmostEqual( - result.aux_operator_eigenvalues[0][1][1], 0.015183867579396111, places=1 - ) - - # Go again with additional None and zero operators - aux_ops = [*aux_ops, None, 0] - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=aux_ops) - - self.assertEqual(len(result.aux_operator_eigenvalues[0]), 4) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][0], 2.0, places=1) - self.assertAlmostEqual( - result.aux_operator_eigenvalues[0][1][0], 0.0019531249999999445, places=1 - ) - self.assertEqual(result.aux_operator_eigenvalues[0][2][0], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[0][3][0], 0.0) - # # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][1], 0.0) - self.assertAlmostEqual( - result.aux_operator_eigenvalues[0][1][1], 0.01548658094658011, places=1 - ) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][2][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][3][1], 0.0) - - @unittest.skipUnless(has_aer(), "qiskit-aer doesn't appear to be installed.") - def test_aux_operator_std_dev_aer_pauli(self): - """Test non-zero standard deviations of aux operators with AerPauliExpectation.""" - wavefunction = self.ry_wavefunction - with self.assertWarns(DeprecationWarning): - vqd = VQD( - ansatz=wavefunction, - expectation=AerPauliExpectation(), - optimizer=COBYLA(maxiter=0), - quantum_instance=QuantumInstance( - backend=Aer.get_backend("qasm_simulator"), - shots=1, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ), - ) - - with self.assertWarns(DeprecationWarning): - # Go again with two auxiliary operators - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=aux_ops) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][0], 2.0, places=1) - self.assertAlmostEqual( - result.aux_operator_eigenvalues[0][1][0], 0.6698863565455391, places=1 - ) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][1], 0.0, places=6) - - # Go again with additional None and zero operators - aux_ops = [*aux_ops, None, 0] - with self.assertWarns(DeprecationWarning): - result = vqd.compute_eigenvalues(self.h2_op, aux_operators=aux_ops) - self.assertEqual(len(result.aux_operator_eigenvalues[-1]), 4) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][0], 2.0, places=6) - self.assertAlmostEqual( - result.aux_operator_eigenvalues[0][1][0], 0.6036400943063891, places=6 - ) - self.assertEqual(result.aux_operator_eigenvalues[0][2][0], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[0][3][0], 0.0) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1][1], 0.0, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][2][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][3][1], 0.0) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/test_vqe.py b/test/python/algorithms/test_vqe.py deleted file mode 100644 index 781728400f2b..000000000000 --- a/test/python/algorithms/test_vqe.py +++ /dev/null @@ -1,856 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test VQE""" - -import logging -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from test.python.transpiler._dummy_passes import DummyAP - -from functools import partial -import numpy as np -from scipy.optimize import minimize as scipy_minimize -from ddt import data, ddt, unpack - -from qiskit import BasicAer, QuantumCircuit -from qiskit.algorithms import VQE, AlgorithmError -from qiskit.algorithms.optimizers import ( - CG, - COBYLA, - L_BFGS_B, - P_BFGS, - QNSPSA, - SLSQP, - SPSA, - TNC, - OptimizerResult, -) -from qiskit.circuit.library import EfficientSU2, RealAmplitudes, TwoLocal -from qiskit.exceptions import MissingOptionalLibraryError -from qiskit.opflow import ( - AerPauliExpectation, - Gradient, - I, - MatrixExpectation, - PauliExpectation, - PauliSumOp, - PrimitiveOp, - TwoQubitReduction, - X, - Z, -) -from qiskit.quantum_info import Statevector -from qiskit.transpiler import PassManager, PassManagerConfig -from qiskit.transpiler.preset_passmanagers import level_1_pass_manager -from qiskit.utils import QuantumInstance, algorithm_globals, optionals - -logger = "LocalLogger" - - -class LogPass(DummyAP): - """A dummy analysis pass that logs when executed""" - - def __init__(self, message): - super().__init__() - self.message = message - - def run(self, dag): - logging.getLogger(logger).info(self.message) - - -# pylint: disable=invalid-name, unused-argument -def _mock_optimizer(fun, x0, jac=None, bounds=None) -> OptimizerResult: - """A mock of a callable that can be used as minimizer in the VQE.""" - result = OptimizerResult() - result.x = np.zeros_like(x0) - result.fun = fun(result.x) - result.nit = 0 - return result - - -@ddt -class TestVQE(QiskitAlgorithmsTestCase): - """Test VQE""" - - def setUp(self): - super().setUp() - self.seed = 50 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - self.h2_energy = -1.85727503 - - self.ryrz_wavefunction = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - self.ry_wavefunction = TwoLocal(rotation_blocks="ry", entanglement_blocks="cz") - - with self.assertWarns(DeprecationWarning): - self.h2_op = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - self.qasm_simulator = QuantumInstance( - BasicAer.get_backend("qasm_simulator"), - shots=1024, - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - self.statevector_simulator = QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - shots=1, - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - - def test_basic_aer_statevector(self): - """Test the VQE on BasicAer's statevector simulator.""" - wavefunction = self.ryrz_wavefunction - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=L_BFGS_B(), - quantum_instance=QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - basis_gates=["u1", "u2", "u3", "cx", "id"], - coupling_map=[[0, 1]], - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ), - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - with self.subTest(msg="test eigenvalue"): - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy) - - with self.subTest(msg="test dimension of optimal point"): - self.assertEqual(len(result.optimal_point), 16) - - with self.subTest(msg="assert cost_function_evals is set"): - self.assertIsNotNone(result.cost_function_evals) - - with self.subTest(msg="assert optimizer_time is set"): - self.assertIsNotNone(result.optimizer_time) - - def test_circuit_input(self): - """Test running the VQE on a plain QuantumCircuit object.""" - wavefunction = QuantumCircuit(2).compose(EfficientSU2(2)) - optimizer = SLSQP(maxiter=50) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=optimizer, - quantum_instance=self.statevector_simulator, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - @data( - (MatrixExpectation(), 1), - (AerPauliExpectation(), 1), - (PauliExpectation(), 2), - ) - @unpack - def test_construct_circuit(self, expectation, num_circuits): - """Test construct circuits returns QuantumCircuits and the right number of them.""" - try: - wavefunction = EfficientSU2(2, reps=1) - - with self.assertWarns(DeprecationWarning): - vqe = VQE(ansatz=wavefunction, expectation=expectation) - params = [0] * wavefunction.num_parameters - circuits = vqe.construct_circuit(parameter=params, operator=self.h2_op) - - self.assertEqual(len(circuits), num_circuits) - for circuit in circuits: - self.assertIsInstance(circuit, QuantumCircuit) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - return - - def test_missing_varform_params(self): - """Test specifying a variational form with no parameters raises an error.""" - circuit = QuantumCircuit(self.h2_op.num_qubits) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=circuit, quantum_instance=BasicAer.get_backend("statevector_simulator") - ) - with self.assertRaises(RuntimeError): - vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - @data( - (SLSQP(maxiter=50), 5, 4), - (SPSA(maxiter=150), 2, 2), # max_evals_grouped=n or =2 if n>2 - ) - @unpack - def test_max_evals_grouped(self, optimizer, places, max_evals_grouped): - """VQE Optimizers test""" - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=self.ryrz_wavefunction, - optimizer=optimizer, - max_evals_grouped=max_evals_grouped, - quantum_instance=self.statevector_simulator, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=places) - - def test_basic_aer_qasm(self): - """Test the VQE on BasicAer's QASM simulator.""" - optimizer = SPSA(maxiter=300, last_avg=5) - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=optimizer, - max_evals_grouped=1, - quantum_instance=self.qasm_simulator, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.86823, places=2) - - def test_qasm_eigenvector_normalized(self): - """Test VQE with qasm_simulator returns normalized eigenvector.""" - wavefunction = self.ry_wavefunction - with self.assertWarns(DeprecationWarning): - vqe = VQE(ansatz=wavefunction, quantum_instance=self.qasm_simulator) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - amplitudes = list(result.eigenstate.values()) - self.assertAlmostEqual(np.linalg.norm(amplitudes), 1.0, places=4) - - @unittest.skipUnless(optionals.HAS_AER, "Qiskit aer is required to run these tests") - def test_with_aer_statevector(self): - """Test VQE with Aer's statevector_simulator.""" - from qiskit_aer import AerSimulator - - backend = AerSimulator(method="statevector") - wavefunction = self.ry_wavefunction - optimizer = L_BFGS_B() - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=optimizer, - max_evals_grouped=1, - quantum_instance=quantum_instance, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - - @unittest.skipUnless(optionals.HAS_AER, "Qiskit aer is required to run these tests") - def test_with_aer_qasm(self): - """Test VQE with Aer's qasm_simulator.""" - from qiskit_aer import AerSimulator - - backend = AerSimulator() - optimizer = SPSA(maxiter=200, last_avg=5) - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=optimizer, - expectation=PauliExpectation(), - quantum_instance=quantum_instance, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.86305, places=2) - - @unittest.skipUnless(optionals.HAS_AER, "Qiskit aer is required to run these tests") - def test_with_aer_qasm_snapshot_mode(self): - """Test the VQE using Aer's qasm_simulator snapshot mode.""" - from qiskit_aer import AerSimulator - - backend = AerSimulator() - optimizer = L_BFGS_B() - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend, - shots=1, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=optimizer, - expectation=AerPauliExpectation(), - quantum_instance=quantum_instance, - ) - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - - @unittest.skipUnless(optionals.HAS_AER, "Qiskit aer is required to run these tests") - @data( - CG(maxiter=1), - L_BFGS_B(maxfun=1), - P_BFGS(maxfun=1, max_processes=0), - SLSQP(maxiter=1), - TNC(maxiter=1), - ) - def test_with_gradient(self, optimizer): - """Test VQE using Gradient().""" - from qiskit_aer import AerSimulator - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=AerSimulator(), - shots=1, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ) - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=self.ry_wavefunction, - optimizer=optimizer, - gradient=Gradient(), - expectation=AerPauliExpectation(), - quantum_instance=quantum_instance, - max_evals_grouped=1000, - ) - vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - def test_with_two_qubit_reduction(self): - """Test the VQE using TwoQubitReduction.""" - - with self.assertWarns(DeprecationWarning): - qubit_op = PauliSumOp.from_list( - [ - ("IIII", -0.8105479805373266), - ("IIIZ", 0.17218393261915552), - ("IIZZ", -0.22575349222402472), - ("IZZI", 0.1721839326191556), - ("ZZII", -0.22575349222402466), - ("IIZI", 0.1209126326177663), - ("IZZZ", 0.16892753870087912), - ("IXZX", -0.045232799946057854), - ("ZXIX", 0.045232799946057854), - ("IXIX", 0.045232799946057854), - ("ZXZX", -0.045232799946057854), - ("ZZIZ", 0.16614543256382414), - ("IZIZ", 0.16614543256382414), - ("ZZZZ", 0.17464343068300453), - ("ZIZI", 0.1209126326177663), - ] - ) - tapered_qubit_op = TwoQubitReduction(num_particles=2).convert(qubit_op) - - for simulator in [self.qasm_simulator, self.statevector_simulator]: - with self.subTest(f"Test for {simulator}."), self.assertWarns(DeprecationWarning): - vqe = VQE( - self.ry_wavefunction, - SPSA(maxiter=300, last_avg=5), - quantum_instance=simulator, - ) - result = vqe.compute_minimum_eigenvalue(tapered_qubit_op) - energy = -1.868 if simulator == self.qasm_simulator else self.h2_energy - self.assertAlmostEqual(result.eigenvalue.real, energy, places=2) - - def test_callback(self): - """Test the callback on VQE.""" - history = {"eval_count": [], "parameters": [], "mean": [], "std": []} - - def store_intermediate_result(eval_count, parameters, mean, std): - history["eval_count"].append(eval_count) - history["parameters"].append(parameters) - history["mean"].append(mean) - history["std"].append(std) - - optimizer = COBYLA(maxiter=3) - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - optimizer=optimizer, - callback=store_intermediate_result, - quantum_instance=self.qasm_simulator, - ) - vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertTrue(all(isinstance(count, int) for count in history["eval_count"])) - self.assertTrue(all(isinstance(mean, float) for mean in history["mean"])) - self.assertTrue(all(isinstance(std, float) for std in history["std"])) - for params in history["parameters"]: - self.assertTrue(all(isinstance(param, float) for param in params)) - - def test_reuse(self): - """Test re-using a VQE algorithm instance.""" - - with self.assertWarns(DeprecationWarning): - vqe = VQE() - with self.subTest(msg="assert running empty raises AlgorithmError"): - with self.assertWarns(DeprecationWarning), self.assertRaises(AlgorithmError): - _ = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - ansatz = TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - vqe.ansatz = ansatz - with self.subTest(msg="assert missing operator raises AlgorithmError"): - with self.assertWarns(DeprecationWarning), self.assertRaises(AlgorithmError): - _ = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - with self.assertWarns(DeprecationWarning): - vqe.expectation = MatrixExpectation() - vqe.quantum_instance = self.statevector_simulator - - with self.subTest(msg="assert VQE works once all info is available"), self.assertWarns( - DeprecationWarning - ): - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=5) - - with self.assertWarns(DeprecationWarning): - operator = PrimitiveOp( - np.array([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]]) - ) - - with self.subTest(msg="assert minimum eigensolver interface works"), self.assertWarns( - DeprecationWarning - ): - result = vqe.compute_minimum_eigenvalue(operator=operator) - self.assertAlmostEqual(result.eigenvalue.real, -1.0, places=5) - - def test_vqe_optimizer(self): - """Test running same VQE twice to re-use optimizer, then switch optimizer""" - with self.assertWarns(DeprecationWarning): - vqe = VQE( - optimizer=SLSQP(), - quantum_instance=QuantumInstance(BasicAer.get_backend("statevector_simulator")), - ) - - def run_check(): - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - self.assertAlmostEqual(result.eigenvalue.real, -1.85727503, places=5) - - run_check() - - with self.subTest("Optimizer re-use"): - run_check() - - with self.subTest("Optimizer replace"): - vqe.optimizer = L_BFGS_B() - run_check() - - @data(MatrixExpectation(), None) - def test_backend_change(self, user_expectation): - """Test that VQE works when backend changes.""" - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=TwoLocal(rotation_blocks=["ry", "rz"], entanglement_blocks="cz"), - optimizer=SLSQP(maxiter=2), - expectation=user_expectation, - quantum_instance=BasicAer.get_backend("statevector_simulator"), - ) - result0 = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - if user_expectation is not None: - with self.subTest("User expectation kept."): - self.assertEqual(vqe.expectation, user_expectation) - - # works also if no expectation is set, since it will be determined automatically - with self.assertWarns(DeprecationWarning): - vqe.quantum_instance = BasicAer.get_backend("qasm_simulator") - result1 = vqe.compute_minimum_eigenvalue(operator=self.h2_op) - - if user_expectation is not None: - with self.subTest("Change backend with user expectation, it is kept."): - self.assertEqual(vqe.expectation, user_expectation) - - with self.subTest("Check results."): - self.assertEqual(len(result0.optimal_point), len(result1.optimal_point)) - - def test_batch_evaluate_with_qnspsa(self): - """Test batch evaluating with QNSPSA works.""" - ansatz = TwoLocal(2, rotation_blocks=["ry", "rz"], entanglement_blocks="cz") - - wrapped_backend = BasicAer.get_backend("qasm_simulator") - inner_backend = BasicAer.get_backend("statevector_simulator") - - callcount = {"count": 0} - - def wrapped_run(circuits, **kwargs): - kwargs["callcount"]["count"] += 1 - return inner_backend.run(circuits) - - wrapped_backend.run = partial(wrapped_run, callcount=callcount) - - with self.assertWarns(DeprecationWarning): - fidelity = QNSPSA.get_fidelity(ansatz, backend=wrapped_backend) - qnspsa = QNSPSA(fidelity, maxiter=5) - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=ansatz, - optimizer=qnspsa, - max_evals_grouped=100, - quantum_instance=wrapped_backend, - ) - _ = vqe.compute_minimum_eigenvalue(Z ^ Z) - - # 1 calibration + 1 stddev estimation + 1 initial blocking - # + 5 (1 loss + 1 fidelity + 1 blocking) + 1 return loss + 1 VQE eval - expected = 1 + 1 + 1 + 5 * 3 + 1 + 1 - - self.assertEqual(callcount["count"], expected) - - def test_set_ansatz_to_none(self): - """Tests that setting the ansatz to None results in the default behavior""" - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=self.ryrz_wavefunction, - optimizer=L_BFGS_B(), - quantum_instance=self.statevector_simulator, - ) - - vqe.ansatz = None - self.assertIsInstance(vqe.ansatz, RealAmplitudes) - - def test_set_optimizer_to_none(self): - """Tests that setting the optimizer to None results in the default behavior""" - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=self.ryrz_wavefunction, - optimizer=L_BFGS_B(), - quantum_instance=self.statevector_simulator, - ) - - vqe.optimizer = None - self.assertIsInstance(vqe.optimizer, SLSQP) - - def test_optimizer_scipy_callable(self): - """Test passing a SciPy optimizer directly as callable.""" - - with self.assertWarns(DeprecationWarning): - vqe = VQE( - optimizer=partial(scipy_minimize, method="L-BFGS-B", options={"maxiter": 2}), - quantum_instance=self.statevector_simulator, - ) - result = vqe.compute_minimum_eigenvalue(Z) - - self.assertEqual(result.cost_function_evals, 20) - - def test_optimizer_callable(self): - """Test passing a optimizer directly as callable.""" - ansatz = RealAmplitudes(1, reps=1) - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=ansatz, - optimizer=_mock_optimizer, - quantum_instance=self.statevector_simulator, - ) - result = vqe.compute_minimum_eigenvalue(Z) - self.assertTrue(np.all(result.optimal_point == np.zeros(ansatz.num_parameters))) - - def test_aux_operators_list(self): - """Test list-based aux_operators.""" - wavefunction = self.ry_wavefunction - - # Start with an empty list - with self.assertWarns(DeprecationWarning): - vqe = VQE(ansatz=wavefunction, quantum_instance=self.statevector_simulator) - - # Start with an empty list - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=[]) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertIsNone(result.aux_operator_eigenvalues) - - # Go again with two auxiliary operators - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=aux_ops) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.0) - - # Go again with additional None and zero operators - extra_ops = [*aux_ops, None, 0] - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=extra_ops) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertEqual(len(result.aux_operator_eigenvalues), 4) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0, places=6) - self.assertEqual(result.aux_operator_eigenvalues[2][0], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[3][0], 0.0) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[2][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[3][1], 0.0) - - def test_aux_operators_dict(self): - """Test dictionary compatibility of aux_operators""" - wavefunction = self.ry_wavefunction - - with self.assertWarns(DeprecationWarning): - vqe = VQE(ansatz=wavefunction, quantum_instance=self.statevector_simulator) - - # Start with an empty dictionary - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators={}) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertIsNone(result.aux_operator_eigenvalues) - - # Go again with two auxiliary operators - with self.assertWarns(DeprecationWarning): - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = {"aux_op1": aux_op1, "aux_op2": aux_op2} - - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=aux_ops) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][0], 0, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][1], 0.0) - - # Go again with additional None and zero operators - extra_ops = {**aux_ops, "None_operator": None, "zero_operator": 0} - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=extra_ops) - - self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6) - self.assertEqual(len(result.aux_operator_eigenvalues), 3) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][0], 2, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][0], 0, places=6) - self.assertEqual(result.aux_operator_eigenvalues["zero_operator"][0], 0.0) - self.assertTrue("None_operator" not in result.aux_operator_eigenvalues.keys()) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues["zero_operator"][1], 0.0) - - def test_aux_operator_std_dev_pauli(self): - """Test non-zero standard deviations of aux operators with PauliExpectation.""" - wavefunction = self.ry_wavefunction - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - expectation=PauliExpectation(), - optimizer=COBYLA(maxiter=0), - quantum_instance=self.qasm_simulator, - ) - - with self.assertWarns(DeprecationWarning): - # Go again with two auxiliary operators - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=aux_ops) - - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2.0, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0.6796875, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.02534712219145965, places=6) - - # Go again with additional None and zero operators - aux_ops = [*aux_ops, None, 0] - - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=aux_ops) - - self.assertEqual(len(result.aux_operator_eigenvalues), 4) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2.0, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0.57421875, places=6) - self.assertEqual(result.aux_operator_eigenvalues[2][0], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[3][0], 0.0) - # # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0) - self.assertAlmostEqual( - result.aux_operator_eigenvalues[1][1], 0.026562146577166837, places=6 - ) - self.assertAlmostEqual(result.aux_operator_eigenvalues[2][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[3][1], 0.0) - - @unittest.skipUnless(optionals.HAS_AER, "Qiskit aer is required to run these tests") - def test_aux_operator_std_dev_aer_pauli(self): - """Test non-zero standard deviations of aux operators with AerPauliExpectation.""" - from qiskit_aer import AerSimulator - - wavefunction = self.ry_wavefunction - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, - expectation=AerPauliExpectation(), - optimizer=COBYLA(maxiter=0), - quantum_instance=QuantumInstance( - backend=AerSimulator(), - shots=1, - seed_simulator=algorithm_globals.random_seed, - seed_transpiler=algorithm_globals.random_seed, - ), - ) - with self.assertWarns(DeprecationWarning): - # Go again with two auxiliary operators - aux_op1 = PauliSumOp.from_list([("II", 2.0)]) - aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5), ("XX", -0.5)]) - aux_ops = [aux_op1, aux_op2] - - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=aux_ops) - - self.assertEqual(len(result.aux_operator_eigenvalues), 2) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2.0, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0.6698863565455391, places=6) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.0, places=6) - - # Go again with additional None and zero operators - aux_ops = [*aux_ops, None, 0] - with self.assertWarns(DeprecationWarning): - result = vqe.compute_minimum_eigenvalue(self.h2_op, aux_operators=aux_ops) - - self.assertEqual(len(result.aux_operator_eigenvalues), 4) - # expectation values - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2.0, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0.6036400943063891, places=6) - self.assertEqual(result.aux_operator_eigenvalues[2][0], 0.0) - self.assertEqual(result.aux_operator_eigenvalues[3][0], 0.0) - # standard deviations - self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.0, places=6) - self.assertAlmostEqual(result.aux_operator_eigenvalues[2][1], 0.0) - self.assertAlmostEqual(result.aux_operator_eigenvalues[3][1], 0.0) - - def test_2step_transpile(self): - """Test the two-step transpiler pass.""" - # count how often the pass for parameterized circuits is called - pre_counter = LogPass("pre_passmanager") - pre_pass = PassManager(pre_counter) - config = PassManagerConfig(basis_gates=["u3", "cx"]) - pre_pass += level_1_pass_manager(config) - - # ... and the pass for bound circuits - bound_counter = LogPass("bound_pass_manager") - bound_pass = PassManager(bound_counter) - - optimizer = SPSA(maxiter=5, learning_rate=0.01, perturbation=0.01) - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=BasicAer.get_backend("statevector_simulator"), - basis_gates=["u3", "cx"], - pass_manager=pre_pass, - bound_pass_manager=bound_pass, - ) - - with self.assertWarns(DeprecationWarning): - vqe = VQE(optimizer=optimizer, quantum_instance=quantum_instance) - with self.assertLogs(logger, level="INFO") as cm: - _ = vqe.compute_minimum_eigenvalue(Z) - - expected = [ - "pre_passmanager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "bound_pass_manager", - "pre_passmanager", - "bound_pass_manager", - ] - self.assertEqual([record.message for record in cm.records], expected) - - def test_construct_eigenstate_from_optpoint(self): - """Test constructing the eigenstate from the optimal point, if the default ansatz is used.""" - - # use Hamiltonian yielding more than 11 parameters in the default ansatz - with self.assertWarns(DeprecationWarning): - hamiltonian = Z ^ Z ^ Z - - optimizer = SPSA(maxiter=1, learning_rate=0.01, perturbation=0.01) - - with self.assertWarns(DeprecationWarning): - quantum_instance = QuantumInstance( - backend=BasicAer.get_backend("statevector_simulator"), basis_gates=["u3", "cx"] - ) - - with self.assertWarns(DeprecationWarning): - vqe = VQE(optimizer=optimizer, quantum_instance=quantum_instance) - result = vqe.compute_minimum_eigenvalue(hamiltonian) - - optimal_circuit = vqe.ansatz.assign_parameters(result.optimal_point) - self.assertTrue(Statevector(result.eigenstate).equiv(optimal_circuit)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/__init__.py b/test/python/algorithms/time_evolvers/__init__.py deleted file mode 100644 index fdb172d367f0..000000000000 --- a/test/python/algorithms/time_evolvers/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/time_evolvers/classical_methods/__init__.py b/test/python/algorithms/time_evolvers/classical_methods/__init__.py deleted file mode 100644 index fdb172d367f0..000000000000 --- a/test/python/algorithms/time_evolvers/classical_methods/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/time_evolvers/classical_methods/test_scipy_imaginary_evolver.py b/test/python/algorithms/time_evolvers/classical_methods/test_scipy_imaginary_evolver.py deleted file mode 100644 index 5671bf221284..000000000000 --- a/test/python/algorithms/time_evolvers/classical_methods/test_scipy_imaginary_evolver.py +++ /dev/null @@ -1,183 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Classical Imaginary Evolver.""" -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import data, ddt, unpack -import numpy as np -from qiskit.algorithms.time_evolvers.time_evolution_problem import TimeEvolutionProblem - -from qiskit.quantum_info.states.statevector import Statevector -from qiskit.quantum_info import SparsePauliOp - -from qiskit import QuantumCircuit -from qiskit.algorithms import SciPyImaginaryEvolver - -from qiskit.opflow import PauliSumOp - - -@ddt -class TestSciPyImaginaryEvolver(QiskitAlgorithmsTestCase): - """Test SciPy Imaginary Evolver.""" - - def create_hamiltonian_lattice(self, num_sites: int) -> SparsePauliOp: - """Creates an Ising Hamiltonian on a lattice.""" - j_const = 0.1 - g_const = -1.0 - - zz_op = ["I" * i + "ZZ" + "I" * (num_sites - i - 2) for i in range(num_sites - 1)] - x_op = ["I" * i + "X" + "I" * (num_sites - i - 1) for i in range(num_sites)] - return SparsePauliOp(zz_op) * j_const + SparsePauliOp(x_op) * g_const - - @data( - (Statevector.from_label("0"), 100, SparsePauliOp("X"), Statevector.from_label("-")), - (Statevector.from_label("0"), 100, SparsePauliOp("-X"), Statevector.from_label("+")), - ) - @unpack - def test_evolve( - self, - initial_state: Statevector, - tau: float, - hamiltonian: SparsePauliOp, - expected_state: Statevector, - ): - """Initializes a classical imaginary evolver and evolves a state to find the ground state. - It compares the solution with the first eigenstate of the hamiltonian. - """ - expected_state_matrix = expected_state.data - - evolution_problem = TimeEvolutionProblem(hamiltonian, tau, initial_state) - classic_evolver = SciPyImaginaryEvolver(num_timesteps=300) - result = classic_evolver.evolve(evolution_problem) - - with self.subTest("Amplitudes"): - np.testing.assert_allclose( - np.absolute(result.evolved_state.data), - np.absolute(expected_state_matrix), - atol=1e-10, - rtol=0, - ) - - with self.subTest("Phases"): - np.testing.assert_allclose( - np.angle(result.evolved_state.data), - np.angle(expected_state_matrix), - atol=1e-10, - rtol=0, - ) - - @data( - ( - Statevector.from_label("0" * 5), - SparsePauliOp.from_sparse_list([("X", [i], 1) for i in range(5)], num_qubits=5), - 5, - ), - (Statevector.from_label("0"), SparsePauliOp("X"), 1), - ) - @unpack - def test_observables( - self, initial_state: Statevector, hamiltonian: SparsePauliOp, nqubits: int - ): - """Tests if the observables are properly evaluated at each timestep.""" - - time_ev = 5.0 - observables = {"Energy": hamiltonian, "Z": SparsePauliOp("Z" * nqubits)} - evolution_problem = TimeEvolutionProblem( - hamiltonian, time_ev, initial_state, aux_operators=observables - ) - - classic_evolver = SciPyImaginaryEvolver(num_timesteps=300) - result = classic_evolver.evolve(evolution_problem) - - z_mean, z_std = result.observables["Z"] - - time_vector = result.times - expected_z = 1 / (np.cosh(time_vector) ** 2 + np.sinh(time_vector) ** 2) - expected_z_std = np.zeros_like(expected_z) - - np.testing.assert_allclose(z_mean, expected_z**nqubits, atol=1e-10, rtol=0) - np.testing.assert_allclose(z_std, expected_z_std, atol=1e-10, rtol=0) - - def test_quantum_circuit_initial_state(self): - """Tests if the system can be evolved with a quantum circuit as an initial state.""" - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, range(1, 3)) - - evolution_problem = TimeEvolutionProblem( - hamiltonian=SparsePauliOp("X" * 3), time=1.0, initial_state=qc - ) - classic_evolver = SciPyImaginaryEvolver(num_timesteps=5) - result = classic_evolver.evolve(evolution_problem) - self.assertEqual(result.evolved_state, Statevector(qc)) - - def test_paulisumop_hamiltonian(self): - """Tests if the hamiltonian can be a PauliSumOp""" - with self.assertWarns(DeprecationWarning): - hamiltonian = PauliSumOp.from_list( - [ - ("XI", 1), - ("IX", 1), - ] - ) - observable = PauliSumOp.from_list([("ZZ", 1)]) - evolution_problem = TimeEvolutionProblem( - hamiltonian=hamiltonian, - time=1.0, - initial_state=Statevector.from_label("00"), - aux_operators={"ZZ": observable}, - ) - classic_evolver = SciPyImaginaryEvolver(num_timesteps=5) - result = classic_evolver.evolve(evolution_problem) - expected = 1 / (np.cosh(1.0) ** 2 + np.sinh(1.0) ** 2) - np.testing.assert_almost_equal(result.aux_ops_evaluated["ZZ"][0], expected**2) - - def test_error_time_dependency(self): - """Tests if an error is raised for a time dependent Hamiltonian.""" - evolution_problem = TimeEvolutionProblem( - hamiltonian=SparsePauliOp("X" * 3), - time=1.0, - initial_state=Statevector.from_label("0" * 3), - t_param=0, - ) - classic_evolver = SciPyImaginaryEvolver(num_timesteps=5) - with self.assertRaises(ValueError): - classic_evolver.evolve(evolution_problem) - - def test_no_time_steps(self): - """Tests if the evolver handles some edge cases related to the number of timesteps.""" - evolution_problem = TimeEvolutionProblem( - hamiltonian=SparsePauliOp("X"), - time=1.0, - initial_state=Statevector.from_label("0"), - aux_operators={"Energy": SparsePauliOp("X")}, - ) - - with self.subTest("0 timesteps"): - with self.assertRaises(ValueError): - classic_evolver = SciPyImaginaryEvolver(num_timesteps=0) - classic_evolver.evolve(evolution_problem) - - with self.subTest("1 timestep"): - classic_evolver = SciPyImaginaryEvolver(num_timesteps=1) - result = classic_evolver.evolve(evolution_problem) - np.testing.assert_equal(result.times, np.array([0.0, 1.0])) - - with self.subTest("Negative timesteps"): - with self.assertRaises(ValueError): - classic_evolver = SciPyImaginaryEvolver(num_timesteps=-5) - classic_evolver.evolve(evolution_problem) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/classical_methods/test_scipy_real_evolver.py b/test/python/algorithms/time_evolvers/classical_methods/test_scipy_real_evolver.py deleted file mode 100644 index 40fb1e1e1102..000000000000 --- a/test/python/algorithms/time_evolvers/classical_methods/test_scipy_real_evolver.py +++ /dev/null @@ -1,154 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Classical Real Evolver.""" -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import data, ddt, unpack -import numpy as np -from qiskit import QuantumCircuit, QuantumRegister -from qiskit.algorithms import SciPyRealEvolver, TimeEvolutionProblem -from qiskit.quantum_info import Statevector, SparsePauliOp - - -def zero(n): - """Auxiliary function to create an initial state on n qubits.""" - qr = QuantumRegister(n) - qc = QuantumCircuit(qr) - return Statevector(qc) - - -def one(n): - """Auxiliary function to create an initial state on n qubits.""" - qr = QuantumRegister(n) - qc = QuantumCircuit(qr) - qc.x(qr) - return Statevector(qc) - - -@ddt -class TestClassicalRealEvolver(QiskitAlgorithmsTestCase): - """Test Classical Real Evolver.""" - - @data( - (one(1), np.pi / 2, SparsePauliOp("X"), -1.0j * zero(1)), - ( - one(1).expand(zero(1)), - np.pi / 2, - SparsePauliOp(["XX", "YY"], [0.5, 0.5]), - -1.0j * zero(1).expand(one(1)), - ), - ( - one(1).expand(zero(1)), - np.pi / 4, - SparsePauliOp(["XX", "YY"], [0.5, 0.5]), - ((one(1).expand(zero(1)) - 1.0j * zero(1).expand(one(1)))) / np.sqrt(2), - ), - (zero(12), np.pi / 2, SparsePauliOp("X" * 12), -1.0j * (one(12))), - ) - @unpack - def test_evolve( - self, - initial_state: Statevector, - time_ev: float, - hamiltonian: SparsePauliOp, - expected_state: Statevector, - ): - """Initializes a classical real evolver and evolves a state.""" - evolution_problem = TimeEvolutionProblem(hamiltonian, time_ev, initial_state) - classic_evolver = SciPyRealEvolver(num_timesteps=1) - result = classic_evolver.evolve(evolution_problem) - - np.testing.assert_allclose( - result.evolved_state.data, - expected_state.data, - atol=1e-10, - rtol=0, - ) - - def test_observables(self): - """Tests if the observables are properly evaluated at each timestep.""" - - initial_state = zero(1) - time_ev = 10.0 - hamiltonian = SparsePauliOp("X") - observables = {"Energy": SparsePauliOp("X"), "Z": SparsePauliOp("Z")} - evolution_problem = TimeEvolutionProblem( - hamiltonian, time_ev, initial_state, aux_operators=observables - ) - classic_evolver = SciPyRealEvolver(num_timesteps=10) - result = classic_evolver.evolve(evolution_problem) - - z_mean, z_std = result.observables["Z"] - - timesteps = z_mean.shape[0] - time_vector = np.linspace(0, time_ev, timesteps) - expected_z = 1 - 2 * (np.sin(time_vector)) ** 2 - expected_z_std = np.zeros_like(expected_z) - - np.testing.assert_allclose(z_mean, expected_z, atol=1e-10, rtol=0) - np.testing.assert_allclose(z_std, expected_z_std, atol=1e-10, rtol=0) - np.testing.assert_equal(time_vector, result.times) - - def test_quantum_circuit_initial_state(self): - """Tests if the system can be evolved with a quantum circuit as an initial state.""" - qc = QuantumCircuit(3) - qc.h(0) - qc.cx(0, range(1, 3)) - - evolution_problem = TimeEvolutionProblem( - hamiltonian=SparsePauliOp("X" * 3), time=2 * np.pi, initial_state=qc - ) - classic_evolver = SciPyRealEvolver(num_timesteps=500) - result = classic_evolver.evolve(evolution_problem) - np.testing.assert_almost_equal( - result.evolved_state.data, - np.array([1, 0, 0, 0, 0, 0, 0, 1]) / np.sqrt(2), - decimal=10, - ) - - def test_error_time_dependency(self): - """Tests if an error is raised for time dependent hamiltonian.""" - evolution_problem = TimeEvolutionProblem( - hamiltonian=SparsePauliOp("X" * 3), time=1.0, initial_state=zero(3), t_param=0 - ) - classic_evolver = SciPyRealEvolver(num_timesteps=5) - with self.assertRaises(ValueError): - classic_evolver.evolve(evolution_problem) - - def test_no_time_steps(self): - """Tests if the evolver handles some edge cases related to the number of timesteps.""" - evolution_problem = TimeEvolutionProblem( - hamiltonian=SparsePauliOp("X"), - time=1.0, - initial_state=zero(1), - aux_operators={"Energy": SparsePauliOp("X")}, - ) - - with self.subTest("0 timesteps"): - with self.assertRaises(ValueError): - classic_evolver = SciPyRealEvolver(num_timesteps=0) - classic_evolver.evolve(evolution_problem) - - with self.subTest("1 timestep"): - classic_evolver = SciPyRealEvolver(num_timesteps=1) - result = classic_evolver.evolve(evolution_problem) - np.testing.assert_equal(result.times, np.array([0.0, 1.0])) - - with self.subTest("Negative timesteps"): - with self.assertRaises(ValueError): - classic_evolver = SciPyRealEvolver(num_timesteps=-5) - classic_evolver.evolve(evolution_problem) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/test_pvqd.py b/test/python/algorithms/time_evolvers/test_pvqd.py deleted file mode 100644 index fc48e7b7159e..000000000000 --- a/test/python/algorithms/time_evolvers/test_pvqd.py +++ /dev/null @@ -1,342 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for PVQD.""" -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from functools import partial - -import numpy as np -from ddt import data, ddt, unpack - -from qiskit import QiskitError -from qiskit.algorithms.time_evolvers import TimeEvolutionProblem -from qiskit.algorithms.optimizers import L_BFGS_B, SPSA, GradientDescent, OptimizerResult -from qiskit.algorithms.state_fidelities import ComputeUncompute -from qiskit.algorithms.time_evolvers.pvqd import PVQD -from qiskit.circuit import Gate, Parameter, QuantumCircuit -from qiskit.circuit.library import EfficientSU2 -from qiskit.opflow import PauliSumOp -from qiskit.primitives import Estimator, Sampler -from qiskit.quantum_info import Pauli, SparsePauliOp -from qiskit.test import QiskitTestCase -from qiskit.utils import algorithm_globals - - -# pylint: disable=unused-argument, invalid-name -def gradient_supplied(fun, x0, jac, info): - """A mock optimizer that checks whether the gradient is supported or not.""" - result = OptimizerResult() - result.x = x0 - result.fun = 0 - info["has_gradient"] = jac is not None - - return result - - -class WhatAmI(Gate): - """A custom opaque gate that can be inverted but not decomposed.""" - - def __init__(self, angle): - super().__init__(name="whatami", num_qubits=2, params=[angle]) - - def inverse(self): - return WhatAmI(-self.params[0]) - - -@ddt -class TestPVQD(QiskitAlgorithmsTestCase): - """Tests for the pVQD algorithm.""" - - def setUp(self): - super().setUp() - self.hamiltonian = 0.1 * SparsePauliOp([Pauli("ZZ"), Pauli("IX"), Pauli("XI")]) - self.observable = Pauli("ZZ") - self.ansatz = EfficientSU2(2, reps=1) - self.initial_parameters = np.zeros(self.ansatz.num_parameters) - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 123 - - @data(("ising", True, 2), ("pauli", False, None), ("pauli_sum_op", True, 2)) - @unpack - def test_pvqd(self, hamiltonian_type, gradient, num_timesteps): - """Test a simple evolution.""" - time = 0.02 - - if hamiltonian_type == "ising": - hamiltonian = self.hamiltonian - elif hamiltonian_type == "pauli_sum_op": - with self.assertWarns(DeprecationWarning): - hamiltonian = PauliSumOp(self.hamiltonian) - else: # hamiltonian_type == "pauli": - hamiltonian = Pauli("XX") - - # parse input arguments - if gradient: - optimizer = GradientDescent(maxiter=1) - else: - optimizer = L_BFGS_B(maxiter=1) - - sampler = Sampler() - estimator = Estimator() - fidelity_primitive = ComputeUncompute(sampler) - - # run pVQD keeping track of the energy and the magnetization - pvqd = PVQD( - fidelity_primitive, - self.ansatz, - self.initial_parameters, - estimator, - optimizer=optimizer, - num_timesteps=num_timesteps, - ) - problem = TimeEvolutionProblem( - hamiltonian, time, aux_operators=[hamiltonian, self.observable] - ) - result = pvqd.evolve(problem) - - self.assertTrue(len(result.fidelities) == 3) - self.assertTrue(np.all(result.times == [0.0, 0.01, 0.02])) - self.assertTrue(np.asarray(result.observables).shape == (3, 2)) - num_parameters = self.ansatz.num_parameters - self.assertTrue( - len(result.parameters) == 3 - and np.all([len(params) == num_parameters for params in result.parameters]) - ) - - def test_step(self): - """Test calling the step method directly.""" - sampler = Sampler() - estimator = Estimator() - fidelity_primitive = ComputeUncompute(sampler) - pvqd = PVQD( - fidelity_primitive, - self.ansatz, - self.initial_parameters, - estimator, - optimizer=L_BFGS_B(maxiter=100), - ) - - # perform optimization for a timestep of 0, then the optimal parameters are the current - # ones and the fidelity is 1 - theta_next, fidelity = pvqd.step( - self.hamiltonian, - self.ansatz, - self.initial_parameters, - dt=0.0, - initial_guess=np.zeros_like(self.initial_parameters), - ) - - self.assertTrue(np.allclose(theta_next, self.initial_parameters)) - self.assertAlmostEqual(fidelity, 1) - - def test_get_loss(self): - """Test getting the loss function directly.""" - - sampler = Sampler() - estimator = Estimator() - fidelity_primitive = ComputeUncompute(sampler) - - pvqd = PVQD( - fidelity_primitive, - self.ansatz, - self.initial_parameters, - estimator, - use_parameter_shift=False, - ) - - theta = np.ones(self.ansatz.num_parameters) - loss, gradient = pvqd.get_loss( - self.hamiltonian, self.ansatz, dt=0.0, current_parameters=theta - ) - - displacement = np.arange(self.ansatz.num_parameters) - - with self.subTest(msg="check gradient is None"): - self.assertIsNone(gradient) - - with self.subTest(msg="check loss works"): - self.assertGreater(loss(displacement), 0) - self.assertAlmostEqual(loss(np.zeros_like(theta)), 0) - - def test_invalid_num_timestep(self): - """Test raises if the num_timestep is not positive.""" - sampler = Sampler() - estimator = Estimator() - fidelity_primitive = ComputeUncompute(sampler) - pvqd = PVQD( - fidelity_primitive, - self.ansatz, - self.initial_parameters, - estimator, - optimizer=L_BFGS_B(), - num_timesteps=0, - ) - problem = TimeEvolutionProblem( - self.hamiltonian, time=0.01, aux_operators=[self.hamiltonian, self.observable] - ) - - with self.assertRaises(ValueError): - _ = pvqd.evolve(problem) - - def test_initial_guess_and_observables(self): - """Test doing no optimizations stays at initial guess.""" - initial_guess = np.zeros(self.ansatz.num_parameters) - sampler = Sampler() - estimator = Estimator() - fidelity_primitive = ComputeUncompute(sampler) - - pvqd = PVQD( - fidelity_primitive, - self.ansatz, - self.initial_parameters, - estimator, - optimizer=SPSA(maxiter=0, learning_rate=0.1, perturbation=0.01), - num_timesteps=10, - initial_guess=initial_guess, - ) - problem = TimeEvolutionProblem( - self.hamiltonian, time=0.1, aux_operators=[self.hamiltonian, self.observable] - ) - - result = pvqd.evolve(problem) - - observables = result.aux_ops_evaluated - self.assertEqual(observables[0], 0.1) # expected energy - self.assertEqual(observables[1], 1) # expected magnetization - - def test_zero_parameters(self): - """Test passing an ansatz with zero parameters raises an error.""" - problem = TimeEvolutionProblem(self.hamiltonian, time=0.02) - sampler = Sampler() - fidelity_primitive = ComputeUncompute(sampler) - - pvqd = PVQD( - fidelity_primitive, - QuantumCircuit(2), - np.array([]), - optimizer=SPSA(maxiter=10, learning_rate=0.1, perturbation=0.01), - ) - - with self.assertRaises(QiskitError): - _ = pvqd.evolve(problem) - - def test_initial_state_raises(self): - """Test passing an initial state raises an error for now.""" - initial_state = QuantumCircuit(2) - initial_state.x(0) - - problem = TimeEvolutionProblem( - self.hamiltonian, - time=0.02, - initial_state=initial_state, - ) - - sampler = Sampler() - fidelity_primitive = ComputeUncompute(sampler) - - pvqd = PVQD( - fidelity_primitive, - self.ansatz, - self.initial_parameters, - optimizer=SPSA(maxiter=0, learning_rate=0.1, perturbation=0.01), - ) - - with self.assertRaises(NotImplementedError): - _ = pvqd.evolve(problem) - - def test_aux_ops_raises(self): - """Test passing auxiliary operators with no estimator raises an error.""" - - problem = TimeEvolutionProblem( - self.hamiltonian, time=0.02, aux_operators=[self.hamiltonian, self.observable] - ) - - sampler = Sampler() - fidelity_primitive = ComputeUncompute(sampler) - - pvqd = PVQD( - fidelity_primitive, - self.ansatz, - self.initial_parameters, - optimizer=SPSA(maxiter=0, learning_rate=0.1, perturbation=0.01), - ) - - with self.assertRaises(ValueError): - _ = pvqd.evolve(problem) - - -class TestPVQDUtils(QiskitTestCase): - """Test some utility functions for PVQD.""" - - def setUp(self): - super().setUp() - self.hamiltonian = 0.1 * SparsePauliOp([Pauli("ZZ"), Pauli("IX"), Pauli("XI")]) - self.ansatz = EfficientSU2(2, reps=1) - - def test_gradient_supported(self): - """Test the gradient support is correctly determined.""" - # gradient supported here - wrapped = EfficientSU2(2) # a circuit wrapped into a big instruction - plain = wrapped.decompose() # a plain circuit with already supported instructions - - # gradients not supported on the following circuits - x = Parameter("x") - duplicated = QuantumCircuit(2) - duplicated.rx(x, 0) - duplicated.rx(x, 1) - - needs_chainrule = QuantumCircuit(2) - needs_chainrule.rx(2 * x, 0) - - custom_gate = WhatAmI(x) - unsupported = QuantumCircuit(2) - unsupported.append(custom_gate, [0, 1]) - - tests = [ - (wrapped, True), # tuple: (circuit, gradient support) - (plain, True), - (duplicated, False), - (needs_chainrule, False), - (unsupported, False), - ] - - # used to store the info if a gradient callable is passed into the - # optimizer of not - info = {"has_gradient": None} - optimizer = partial(gradient_supplied, info=info) - - sampler = Sampler() - estimator = Estimator() - fidelity_primitive = ComputeUncompute(sampler) - - pvqd = PVQD( - fidelity=fidelity_primitive, - ansatz=None, - initial_parameters=np.array([]), - estimator=estimator, - optimizer=optimizer, - ) - problem = TimeEvolutionProblem(self.hamiltonian, time=0.01) - for circuit, expected_support in tests: - with self.subTest(circuit=circuit, expected_support=expected_support): - pvqd.ansatz = circuit - pvqd.initial_parameters = np.zeros(circuit.num_parameters) - _ = pvqd.evolve(problem) - self.assertEqual(info["has_gradient"], expected_support) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/test_time_evolution_problem.py b/test/python/algorithms/time_evolvers/test_time_evolution_problem.py deleted file mode 100644 index 1982fb203749..000000000000 --- a/test/python/algorithms/time_evolvers/test_time_evolution_problem.py +++ /dev/null @@ -1,98 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test evolver problem class.""" -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import data, ddt -from numpy.testing import assert_raises -from qiskit import QuantumCircuit -from qiskit.algorithms import TimeEvolutionProblem -from qiskit.quantum_info import Pauli, SparsePauliOp, Statevector -from qiskit.circuit import Parameter -from qiskit.opflow import Y, Z, One, X, Zero, PauliSumOp - - -@ddt -class TestTimeEvolutionProblem(QiskitAlgorithmsTestCase): - """Test evolver problem class.""" - - def test_init_default(self): - """Tests that all default fields are initialized correctly.""" - hamiltonian = Y - time = 2.5 - initial_state = One - - evo_problem = TimeEvolutionProblem(hamiltonian, time, initial_state) - - expected_hamiltonian = Y - expected_time = 2.5 - expected_initial_state = One - expected_aux_operators = None - expected_t_param = None - expected_param_value_dict = None - - self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) - self.assertEqual(evo_problem.time, expected_time) - self.assertEqual(evo_problem.initial_state, expected_initial_state) - self.assertEqual(evo_problem.aux_operators, expected_aux_operators) - self.assertEqual(evo_problem.t_param, expected_t_param) - self.assertEqual(evo_problem.param_value_map, expected_param_value_dict) - - @data(QuantumCircuit(1), Statevector([1, 0])) - def test_init_all(self, initial_state): - """Tests that all fields are initialized correctly.""" - t_parameter = Parameter("t") - with self.assertWarns(DeprecationWarning): - hamiltonian = t_parameter * Z + Y - time = 2 - aux_operators = [X, Y] - param_value_dict = {t_parameter: 3.2} - - evo_problem = TimeEvolutionProblem( - hamiltonian, - time, - initial_state, - aux_operators, - t_param=t_parameter, - param_value_map=param_value_dict, - ) - - with self.assertWarns(DeprecationWarning): - expected_hamiltonian = Y + t_parameter * Z - expected_time = 2 - expected_type = QuantumCircuit - expected_aux_operators = [X, Y] - expected_t_param = t_parameter - expected_param_value_dict = {t_parameter: 3.2} - - with self.assertWarns(DeprecationWarning): - self.assertEqual(evo_problem.hamiltonian, expected_hamiltonian) - self.assertEqual(evo_problem.time, expected_time) - self.assertEqual(type(evo_problem.initial_state), expected_type) - self.assertEqual(evo_problem.aux_operators, expected_aux_operators) - self.assertEqual(evo_problem.t_param, expected_t_param) - self.assertEqual(evo_problem.param_value_map, expected_param_value_dict) - - def test_validate_params(self): - """Tests expected errors are thrown on parameters mismatch.""" - param_x = Parameter("x") - with self.subTest(msg="Parameter missing in dict."): - with self.assertWarns(DeprecationWarning): - hamiltonian = PauliSumOp(SparsePauliOp([Pauli("X"), Pauli("Y")]), param_x) - evolution_problem = TimeEvolutionProblem(hamiltonian, 2, Zero) - with assert_raises(ValueError): - evolution_problem.validate_params() - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/test_time_evolution_result.py b/test/python/algorithms/time_evolvers/test_time_evolution_result.py deleted file mode 100644 index 26f21ba93627..000000000000 --- a/test/python/algorithms/time_evolvers/test_time_evolution_result.py +++ /dev/null @@ -1,47 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Class for testing evolution result.""" -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -from qiskit.algorithms import TimeEvolutionResult -from qiskit.opflow import Zero - - -class TestTimeEvolutionResult(QiskitAlgorithmsTestCase): - """Class for testing evolution result and relevant metadata.""" - - def test_init_state(self): - """Tests that a class is initialized correctly with an evolved_state.""" - evolved_state = Zero - evo_result = TimeEvolutionResult(evolved_state=evolved_state) - - expected_state = Zero - expected_aux_ops_evaluated = None - - self.assertEqual(evo_result.evolved_state, expected_state) - self.assertEqual(evo_result.aux_ops_evaluated, expected_aux_ops_evaluated) - - def test_init_observable(self): - """Tests that a class is initialized correctly with an evolved_observable.""" - evolved_state = Zero - evolved_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] - evo_result = TimeEvolutionResult(evolved_state, evolved_aux_ops_evaluated) - - expected_state = Zero - expected_aux_ops_evaluated = [(5j, 5j), (1.0, 8j), (5 + 1j, 6 + 1j)] - - self.assertEqual(evo_result.evolved_state, expected_state) - self.assertEqual(evo_result.aux_ops_evaluated, expected_aux_ops_evaluated) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/test_trotter_qrte.py b/test/python/algorithms/time_evolvers/test_trotter_qrte.py deleted file mode 100644 index b8a14f0affeb..000000000000 --- a/test/python/algorithms/time_evolvers/test_trotter_qrte.py +++ /dev/null @@ -1,273 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test TrotterQRTE.""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import ddt, data, unpack -import numpy as np -from scipy.linalg import expm -from numpy.testing import assert_raises - -from qiskit.algorithms.time_evolvers import TimeEvolutionProblem, TrotterQRTE -from qiskit.primitives import Estimator -from qiskit import QuantumCircuit -from qiskit.circuit.library import ZGate -from qiskit.quantum_info import Statevector, Pauli, SparsePauliOp -from qiskit.utils import algorithm_globals -from qiskit.circuit import Parameter -from qiskit.opflow import PauliSumOp, X, MatrixOp -from qiskit.synthesis import SuzukiTrotter, QDrift - - -@ddt -class TestTrotterQRTE(QiskitAlgorithmsTestCase): - """TrotterQRTE tests.""" - - def setUp(self): - super().setUp() - self.seed = 50 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - - @data( - ( - None, - Statevector([0.29192658 - 0.45464871j, 0.70807342 - 0.45464871j]), - ), - ( - SuzukiTrotter(), - Statevector([0.29192658 - 0.84147098j, 0.0 - 0.45464871j]), - ), - ) - @unpack - def test_trotter_qrte_trotter_single_qubit(self, product_formula, expected_state): - """Test for default TrotterQRTE on a single qubit.""" - with self.assertWarns(DeprecationWarning): - operator = PauliSumOp(SparsePauliOp([Pauli("X"), Pauli("Z")])) - initial_state = QuantumCircuit(1) - time = 1 - evolution_problem = TimeEvolutionProblem(operator, time, initial_state) - - trotter_qrte = TrotterQRTE(product_formula=product_formula) - evolution_result_state_circuit = trotter_qrte.evolve(evolution_problem).evolved_state - - np.testing.assert_array_almost_equal( - Statevector.from_instruction(evolution_result_state_circuit).data, expected_state.data - ) - - @data((SparsePauliOp(["X", "Z"]), None), (SparsePauliOp(["X", "Z"]), Parameter("t"))) - @unpack - def test_trotter_qrte_trotter(self, operator, t_param): - """Test for default TrotterQRTE on a single qubit with auxiliary operators.""" - if not t_param is None: - operator = SparsePauliOp(operator.paulis, np.array([t_param, 1])) - - # LieTrotter with 1 rep - aux_ops = [Pauli("X"), Pauli("Y")] - - initial_state = QuantumCircuit(1) - time = 3 - num_timesteps = 2 - evolution_problem = TimeEvolutionProblem( - operator, time, initial_state, aux_ops, t_param=t_param - ) - estimator = Estimator() - - expected_psi, expected_observables_result = self._get_expected_trotter_qrte( - operator, - time, - num_timesteps, - initial_state, - aux_ops, - t_param, - ) - - expected_evolved_state = Statevector(expected_psi) - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - trotter_qrte = TrotterQRTE(estimator=estimator, num_timesteps=num_timesteps) - evolution_result = trotter_qrte.evolve(evolution_problem) - - np.testing.assert_array_almost_equal( - Statevector.from_instruction(evolution_result.evolved_state).data, - expected_evolved_state.data, - ) - - aux_ops_result = evolution_result.aux_ops_evaluated - expected_aux_ops_result = [ - (expected_observables_result[-1][0], {"variance": 0, "shots": 0}), - (expected_observables_result[-1][1], {"variance": 0, "shots": 0}), - ] - - means = [element[0] for element in aux_ops_result] - expected_means = [element[0] for element in expected_aux_ops_result] - np.testing.assert_array_almost_equal(means, expected_means) - - vars_and_shots = [element[1] for element in aux_ops_result] - expected_vars_and_shots = [element[1] for element in expected_aux_ops_result] - - observables_result = evolution_result.observables - expected_observables_result = [ - [(o, {"variance": 0, "shots": 0}) for o in eor] for eor in expected_observables_result - ] - - means = [sub_element[0] for element in observables_result for sub_element in element] - expected_means = [ - sub_element[0] for element in expected_observables_result for sub_element in element - ] - np.testing.assert_array_almost_equal(means, expected_means) - - for computed, expected in zip(vars_and_shots, expected_vars_and_shots): - self.assertAlmostEqual(computed.pop("variance", 0), expected["variance"], 2) - self.assertEqual(computed.pop("shots", 0), expected["shots"]) - - @data( - ( - PauliSumOp(SparsePauliOp([Pauli("XY"), Pauli("YX")])), - Statevector([-0.41614684 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.90929743 + 0.0j]), - ), - ( - PauliSumOp(SparsePauliOp([Pauli("ZZ"), Pauli("ZI"), Pauli("IZ")])), - Statevector([-0.9899925 - 0.14112001j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j]), - ), - ( - Pauli("YY"), - Statevector([0.54030231 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.84147098j]), - ), - ) - @unpack - def test_trotter_qrte_trotter_two_qubits(self, operator, expected_state): - """Test for TrotterQRTE on two qubits with various types of a Hamiltonian.""" - # LieTrotter with 1 rep - initial_state = QuantumCircuit(2) - evolution_problem = TimeEvolutionProblem(operator, 1, initial_state) - - trotter_qrte = TrotterQRTE() - evolution_result = trotter_qrte.evolve(evolution_problem) - - np.testing.assert_array_almost_equal( - Statevector.from_instruction(evolution_result.evolved_state).data, expected_state.data - ) - - @data( - (QuantumCircuit(1), Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j])), - ( - QuantumCircuit(1).compose(ZGate(), [0]), - Statevector([0.23071786 - 0.69436148j, 0.4646314 - 0.49874749j]), - ), - ) - @unpack - def test_trotter_qrte_qdrift(self, initial_state, expected_state): - """Test for TrotterQRTE with QDrift.""" - with self.assertWarns(DeprecationWarning): - operator = PauliSumOp(SparsePauliOp([Pauli("X"), Pauli("Z")])) - time = 1 - evolution_problem = TimeEvolutionProblem(operator, time, initial_state) - - trotter_qrte = TrotterQRTE(product_formula=QDrift(seed=0)) - evolution_result = trotter_qrte.evolve(evolution_problem) - - np.testing.assert_array_almost_equal( - Statevector.from_instruction(evolution_result.evolved_state).data, - expected_state.data, - ) - - @data((Parameter("t"), {}), (None, {Parameter("x"): 2}), (None, None)) - @unpack - def test_trotter_qrte_trotter_param_errors(self, t_param, param_value_dict): - """Test TrotterQRTE with raising errors for parameters.""" - with self.assertWarns(DeprecationWarning): - operator = Parameter("t") * PauliSumOp(SparsePauliOp([Pauli("X")])) + PauliSumOp( - SparsePauliOp([Pauli("Z")]) - ) - initial_state = QuantumCircuit(1) - self._run_error_test(initial_state, operator, None, None, t_param, param_value_dict) - - @data(([Pauli("X"), Pauli("Y")], None)) - @unpack - def test_trotter_qrte_trotter_aux_ops_errors(self, aux_ops, estimator): - """Test TrotterQRTE with raising errors.""" - with self.assertWarns(DeprecationWarning): - operator = PauliSumOp(SparsePauliOp([Pauli("X")])) + PauliSumOp( - SparsePauliOp([Pauli("Z")]) - ) - initial_state = QuantumCircuit(1) - self._run_error_test(initial_state, operator, aux_ops, estimator, None, None) - - @data( - (X, QuantumCircuit(1)), - (MatrixOp([[1, 1], [0, 1]]), QuantumCircuit(1)), - (PauliSumOp(SparsePauliOp([Pauli("X")])) + PauliSumOp(SparsePauliOp([Pauli("Z")])), None), - ( - SparsePauliOp([Pauli("X"), Pauli("Z")], np.array([Parameter("a"), Parameter("b")])), - QuantumCircuit(1), - ), - ) - @unpack - def test_trotter_qrte_trotter_hamiltonian_errors(self, operator, initial_state): - """Test TrotterQRTE with raising errors for evolution problem content.""" - self._run_error_test(initial_state, operator, None, None, None, None) - - @staticmethod - def _run_error_test(initial_state, operator, aux_ops, estimator, t_param, param_value_dict): - time = 1 - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - trotter_qrte = TrotterQRTE(estimator=estimator) - with assert_raises(ValueError): - evolution_problem = TimeEvolutionProblem( - operator, - time, - initial_state, - aux_ops, - t_param=t_param, - param_value_map=param_value_dict, - ) - _ = trotter_qrte.evolve(evolution_problem) - - @staticmethod - def _get_expected_trotter_qrte(operator, time, num_timesteps, init_state, observables, t_param): - """Compute reference values for Trotter evolution via exact matrix exponentiation.""" - dt = time / num_timesteps - observables = [obs.to_matrix() for obs in observables] - - psi = Statevector(init_state).data - if t_param is None: - ops = [Pauli(op).to_matrix() * np.real(coeff) for op, coeff in operator.to_list()] - - observable_results = [] - observable_results.append([np.real(np.conj(psi).dot(obs).dot(psi)) for obs in observables]) - - for n in range(num_timesteps): - if t_param is not None: - time_value = (n + 1) * dt - bound = operator.assign_parameters([time_value]) - ops = [Pauli(op).to_matrix() * np.real(coeff) for op, coeff in bound.to_list()] - for op in ops: - psi = expm(-1j * op * dt).dot(psi) - observable_results.append( - [np.real(np.conj(psi).dot(obs).dot(psi)) for obs in observables] - ) - - return psi, observable_results - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/__init__.py b/test/python/algorithms/time_evolvers/variational/__init__.py deleted file mode 100644 index 26f7536d3514..000000000000 --- a/test/python/algorithms/time_evolvers/variational/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/time_evolvers/variational/solvers/__init__.py b/test/python/algorithms/time_evolvers/variational/solvers/__init__.py deleted file mode 100644 index 26f7536d3514..000000000000 --- a/test/python/algorithms/time_evolvers/variational/solvers/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/time_evolvers/variational/solvers/expected_results/__init__.py b/test/python/algorithms/time_evolvers/variational/solvers/expected_results/__init__.py deleted file mode 100644 index 62ef1f76c6f4..000000000000 --- a/test/python/algorithms/time_evolvers/variational/solvers/expected_results/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Stores expected results that are lengthy.""" diff --git a/test/python/algorithms/time_evolvers/variational/solvers/expected_results/test_varqte_linear_solver_expected_1.py b/test/python/algorithms/time_evolvers/variational/solvers/expected_results/test_varqte_linear_solver_expected_1.py deleted file mode 100644 index aba6b4006f37..000000000000 --- a/test/python/algorithms/time_evolvers/variational/solvers/expected_results/test_varqte_linear_solver_expected_1.py +++ /dev/null @@ -1,182 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Stores expected results that are lengthy.""" -expected_metric_res_1 = [ - [ - 2.50000000e-01 + 0.0j, - -3.85185989e-33 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - -3.85185989e-33 + 0.0j, - -3.85185989e-33 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 2.50000000e-01 + 0.0j, - -2.77500000e-17 + 0.0j, - 4.85000000e-17 + 0.0j, - 4.77630626e-32 + 0.0j, - ], - [ - -3.85185989e-33 + 0.0j, - 2.50000000e-01 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - -3.85185989e-33 + 0.0j, - 2.50000000e-01 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - -3.85185989e-33 + 0.0j, - 2.50000000e-01 + 0.0j, - 4.85334346e-32 + 0.0j, - 4.17500000e-17 + 0.0j, - ], - [ - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 0.00000000e00 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 0.00000000e00 + 0.0j, - -1.38777878e-17 + 0.0j, - -7.00000000e-18 + 0.0j, - 1.38006319e-17 + 0.0j, - -1.39493681e-17 + 0.0j, - ], - [ - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 0.00000000e00 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 0.00000000e00 + 0.0j, - -1.38777878e-17 + 0.0j, - -7.00000000e-18 + 0.0j, - 1.38006319e-17 + 0.0j, - -1.39493681e-17 + 0.0j, - ], - [ - -3.85185989e-33 + 0.0j, - -3.85185989e-33 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 2.50000000e-01 + 0.0j, - -3.85185989e-33 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - -3.85185989e-33 + 0.0j, - 0.00000000e00 + 0.0j, - 4.85334346e-32 + 0.0j, - -7.00000000e-18 + 0.0j, - ], - [ - -3.85185989e-33 + 0.0j, - 2.50000000e-01 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - -3.85185989e-33 + 0.0j, - 2.50000000e-01 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - -3.85185989e-33 + 0.0j, - 2.50000000e-01 + 0.0j, - 4.85334346e-32 + 0.0j, - 4.17500000e-17 + 0.0j, - ], - [ - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 0.00000000e00 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 0.00000000e00 + 0.0j, - -1.38777878e-17 + 0.0j, - -7.00000000e-18 + 0.0j, - 1.38006319e-17 + 0.0j, - -1.39493681e-17 + 0.0j, - ], - [ - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 0.00000000e00 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 0.00000000e00 + 0.0j, - -1.38777878e-17 + 0.0j, - -7.00000000e-18 + 0.0j, - 1.38006319e-17 + 0.0j, - -1.39493681e-17 + 0.0j, - ], - [ - 2.50000000e-01 + 0.0j, - -3.85185989e-33 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - -3.85185989e-33 + 0.0j, - -3.85185989e-33 + 0.0j, - -1.38777878e-17 + 0.0j, - -1.38777878e-17 + 0.0j, - 2.50000000e-01 + 0.0j, - -2.77500000e-17 + 0.0j, - 4.85000000e-17 + 0.0j, - -7.00000000e-18 + 0.0j, - ], - [ - -2.77500000e-17 + 0.0j, - 2.50000000e-01 + 0.0j, - -7.00000000e-18 + 0.0j, - -7.00000000e-18 + 0.0j, - 0.00000000e00 + 0.0j, - 2.50000000e-01 + 0.0j, - -7.00000000e-18 + 0.0j, - -7.00000000e-18 + 0.0j, - -2.77500000e-17 + 0.0j, - 2.50000000e-01 + 0.0j, - 0.00000000e00 + 0.0j, - 4.17500000e-17 + 0.0j, - ], - [ - 4.85000000e-17 + 0.0j, - 4.85334346e-32 + 0.0j, - 1.38006319e-17 + 0.0j, - 1.38006319e-17 + 0.0j, - 4.85334346e-32 + 0.0j, - 4.85334346e-32 + 0.0j, - 1.38006319e-17 + 0.0j, - 1.38006319e-17 + 0.0j, - 4.85000000e-17 + 0.0j, - 0.00000000e00 + 0.0j, - 2.50000000e-01 + 0.0j, - -2.77500000e-17 + 0.0j, - ], - [ - 4.77630626e-32 + 0.0j, - 4.17500000e-17 + 0.0j, - -1.39493681e-17 + 0.0j, - -1.39493681e-17 + 0.0j, - -7.00000000e-18 + 0.0j, - 4.17500000e-17 + 0.0j, - -1.39493681e-17 + 0.0j, - -1.39493681e-17 + 0.0j, - -7.00000000e-18 + 0.0j, - 4.17500000e-17 + 0.0j, - -2.77500000e-17 + 0.0j, - 2.50000000e-01 + 0.0j, - ], -] diff --git a/test/python/algorithms/time_evolvers/variational/solvers/ode/__init__.py b/test/python/algorithms/time_evolvers/variational/solvers/ode/__init__.py deleted file mode 100644 index 26f7536d3514..000000000000 --- a/test/python/algorithms/time_evolvers/variational/solvers/ode/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/time_evolvers/variational/solvers/ode/test_forward_euler_solver.py b/test/python/algorithms/time_evolvers/variational/solvers/ode/test_forward_euler_solver.py deleted file mode 100644 index 802f930931d5..000000000000 --- a/test/python/algorithms/time_evolvers/variational/solvers/ode/test_forward_euler_solver.py +++ /dev/null @@ -1,47 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Forward Euler solver.""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -import numpy as np -from ddt import ddt, data, unpack -from scipy.integrate import solve_ivp - -from qiskit.algorithms.time_evolvers.variational.solvers.ode.forward_euler_solver import ( - ForwardEulerSolver, -) - - -@ddt -class TestForwardEulerSolver(QiskitAlgorithmsTestCase): - """Test Forward Euler solver.""" - - @unpack - @data((4, 16), (16, 35.52713678800501), (320, 53.261108839604795)) - def test_solve(self, timesteps, expected_result): - """Test Forward Euler solver for a simple ODE.""" - - y0 = [1] - - # pylint: disable=unused-argument - def func(time, y): - return y - - t_span = [0.0, 4.0] - sol1 = solve_ivp(func, t_span, y0, method=ForwardEulerSolver, num_t_steps=timesteps) - np.testing.assert_equal(sol1.y[-1][-1], expected_result) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/solvers/ode/test_ode_function.py b/test/python/algorithms/time_evolvers/variational/solvers/ode/test_ode_function.py deleted file mode 100644 index e680f45063e5..000000000000 --- a/test/python/algorithms/time_evolvers/variational/solvers/ode/test_ode_function.py +++ /dev/null @@ -1,147 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test ODE function generator.""" - -import unittest - -from test.python.algorithms import QiskitAlgorithmsTestCase -import numpy as np -from qiskit.quantum_info import SparsePauliOp -from qiskit.circuit import Parameter -from qiskit.algorithms.time_evolvers.variational.solvers.var_qte_linear_solver import ( - VarQTELinearSolver, -) -from qiskit.algorithms.time_evolvers.variational.solvers.ode.ode_function import ( - OdeFunction, -) -from qiskit.algorithms.time_evolvers.variational import ( - ImaginaryMcLachlanPrinciple, -) -from qiskit.circuit.library import EfficientSU2 - - -class TestOdeFunctionGenerator(QiskitAlgorithmsTestCase): - """Test ODE function generator.""" - - def test_var_qte_ode_function(self): - """Test ODE function generator.""" - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - # Define a set of initial parameters - parameters = list(ansatz.parameters) - - param_dict = {param: np.pi / 4 for param in parameters} - - var_principle = ImaginaryMcLachlanPrinciple() - - t_param = None - linear_solver = None - linear_solver = VarQTELinearSolver( - var_principle, - observable, - ansatz, - parameters, - t_param, - linear_solver, - ) - - time = 2 - ode_function_generator = OdeFunction(linear_solver, t_param=None, param_dict=param_dict) - - qte_ode_function = ode_function_generator.var_qte_ode_function(time, param_dict.values()) - - expected_qte_ode_function = [ - 0.442145, - -0.022081, - 0.106223, - -0.117468, - 0.251233, - 0.321256, - -0.062728, - -0.036209, - -0.509219, - -0.183459, - -0.050739, - -0.093163, - ] - - np.testing.assert_array_almost_equal(expected_qte_ode_function, qte_ode_function) - - def test_var_qte_ode_function_time_param(self): - """Test ODE function generator with time param.""" - t_param = Parameter("t") - - observable = SparsePauliOp( - ["II", "ZZ", "IZ", "ZI", "YY", "XX"], - np.array([t_param, 0.5716, 0.3435, -0.4347, 0.091, 0.091]), - ) - - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - # Define a set of initial parameters - parameters = list(ansatz.parameters) - - param_dict = {param: np.pi / 4 for param in parameters} - - var_principle = ImaginaryMcLachlanPrinciple() - - time = 2 - - linear_solver = None - varqte_linear_solver = VarQTELinearSolver( - var_principle, - observable, - ansatz, - parameters, - t_param, - linear_solver, - ) - ode_function_generator = OdeFunction( - varqte_linear_solver, t_param=t_param, param_dict=param_dict - ) - - qte_ode_function = ode_function_generator.var_qte_ode_function(time, param_dict.values()) - - expected_qte_ode_function = [ - 0.442145, - -0.022081, - 0.106223, - -0.117468, - 0.251233, - 0.321256, - -0.062728, - -0.036209, - -0.509219, - -0.183459, - -0.050739, - -0.093163, - ] - - np.testing.assert_array_almost_equal(expected_qte_ode_function, qte_ode_function, decimal=5) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/solvers/ode/test_var_qte_ode_solver.py b/test/python/algorithms/time_evolvers/variational/solvers/ode/test_var_qte_ode_solver.py deleted file mode 100644 index 4492b3cc9eaf..000000000000 --- a/test/python/algorithms/time_evolvers/variational/solvers/ode/test_var_qte_ode_solver.py +++ /dev/null @@ -1,127 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test solver of ODEs.""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import ddt, data, unpack -import numpy as np - -from qiskit.quantum_info import SparsePauliOp -from qiskit.algorithms.time_evolvers.variational.solvers.ode.forward_euler_solver import ( - ForwardEulerSolver, -) -from qiskit.algorithms.time_evolvers.variational.solvers.var_qte_linear_solver import ( - VarQTELinearSolver, -) -from qiskit.algorithms.time_evolvers.variational.solvers.ode.var_qte_ode_solver import ( - VarQTEOdeSolver, -) -from qiskit.algorithms.time_evolvers.variational.solvers.ode.ode_function import ( - OdeFunction, -) -from qiskit.algorithms.time_evolvers.variational import ( - ImaginaryMcLachlanPrinciple, -) -from qiskit.circuit.library import EfficientSU2 - - -@ddt -class TestVarQTEOdeSolver(QiskitAlgorithmsTestCase): - """Test solver of ODEs.""" - - @data( - ( - "RK45", - [ - -0.30076755873631345, - -0.8032811383782005, - 1.1674108371914734e-15, - 3.2293849116821145e-16, - 2.541585055586039, - 1.155475184255733, - -2.966331417968169e-16, - 9.604292449638343e-17, - ], - ), - ( - ForwardEulerSolver, - [ - -3.2707e-01, - -8.0960e-01, - 3.4323e-16, - 8.9034e-17, - 2.5290e00, - 1.1563e00, - 3.0227e-16, - -2.2769e-16, - ], - ), - ) - @unpack - def test_run_no_backend(self, ode_solver, expected_result): - """Test ODE solver with no backend.""" - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - d = 1 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - # Define a set of initial parameters - parameters = list(ansatz.parameters) - - init_param_values = np.zeros(len(parameters)) - for i in range(ansatz.num_qubits): - init_param_values[-(ansatz.num_qubits + i + 1)] = np.pi / 2 - - param_dict = dict(zip(parameters, init_param_values)) - - var_principle = ImaginaryMcLachlanPrinciple() - - time = 1 - - t_param = None - - linear_solver = None - linear_solver = VarQTELinearSolver( - var_principle, - observable, - ansatz, - parameters, - t_param, - linear_solver, - ) - ode_function_generator = OdeFunction(linear_solver, param_dict, t_param) - - var_qte_ode_solver = VarQTEOdeSolver( - list(param_dict.values()), - ode_function_generator, - ode_solver=ode_solver, - num_timesteps=25, - ) - - result, _, _ = var_qte_ode_solver.run(time) - - np.testing.assert_array_almost_equal(result, expected_result, decimal=4) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/solvers/test_varqte_linear_solver.py b/test/python/algorithms/time_evolvers/variational/solvers/test_varqte_linear_solver.py deleted file mode 100644 index 7b842b95cac0..000000000000 --- a/test/python/algorithms/time_evolvers/variational/solvers/test_varqte_linear_solver.py +++ /dev/null @@ -1,112 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test solver of linear equations.""" - -import unittest -from test.python.algorithms import QiskitAlgorithmsTestCase - -# fmt: off -from test.python.algorithms.time_evolvers.variational.solvers.expected_results.\ - test_varqte_linear_solver_expected_1 import expected_metric_res_1 -# fmt: on - -import numpy as np - -from qiskit.quantum_info import SparsePauliOp -from qiskit.algorithms.time_evolvers.variational import ( - ImaginaryMcLachlanPrinciple, -) -from qiskit.algorithms.time_evolvers.variational.solvers.var_qte_linear_solver import ( - VarQTELinearSolver, -) -from qiskit.circuit.library import EfficientSU2 - - -class TestVarQTELinearSolver(QiskitAlgorithmsTestCase): - """Test solver of linear equations.""" - - def test_solve_lse(self): - """Test SLE solver.""" - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - parameters = list(ansatz.parameters) - init_param_values = np.zeros(len(parameters)) - for i in range(ansatz.num_qubits): - init_param_values[-(ansatz.num_qubits + i + 1)] = np.pi / 2 - - param_dict = dict(zip(parameters, init_param_values)) - - var_principle = ImaginaryMcLachlanPrinciple() - t_param = None - linear_solver = None - linear_solver = VarQTELinearSolver( - var_principle, - observable, - ansatz, - parameters, - t_param, - linear_solver, - ) - - nat_grad_res, metric_res, grad_res = linear_solver.solve_lse(param_dict) - - expected_nat_grad_res = [ - 3.43500000e-01, - -2.89800000e-01, - 2.43575264e-16, - 1.31792695e-16, - -9.61200000e-01, - -2.89800000e-01, - 1.27493709e-17, - 1.12587456e-16, - 3.43500000e-01, - -2.89800000e-01, - 3.69914720e-17, - 1.95052083e-17, - ] - - expected_grad_res = [ - (0.17174999999999926 - 0j), - (-0.21735000000000085 + 0j), - (4.114902862895087e-17 - 0j), - (4.114902862895087e-17 - 0j), - (-0.24030000000000012 + 0j), - (-0.21735000000000085 + 0j), - (4.114902862895087e-17 - 0j), - (4.114902862895087e-17 - 0j), - (0.17174999999999918 - 0j), - (-0.21735000000000076 + 0j), - (1.7789936190837538e-17 - 0j), - (-8.319872568662832e-17 + 0j), - ] - - np.testing.assert_array_almost_equal(nat_grad_res, expected_nat_grad_res, decimal=4) - np.testing.assert_array_almost_equal(grad_res, expected_grad_res, decimal=4) - np.testing.assert_array_almost_equal(metric_res, expected_metric_res_1, decimal=4) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/test_var_qite.py b/test/python/algorithms/time_evolvers/variational/test_var_qite.py deleted file mode 100644 index b2e431f09f42..000000000000 --- a/test/python/algorithms/time_evolvers/variational/test_var_qite.py +++ /dev/null @@ -1,333 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Variational Quantum Imaginary Time Evolution algorithm.""" - -import unittest -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase -from ddt import ddt -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter -from qiskit.algorithms.gradients import LinCombQGT, LinCombEstimatorGradient -from qiskit.primitives import Estimator -from qiskit.quantum_info import SparsePauliOp, Pauli -from qiskit.utils import algorithm_globals -from qiskit.algorithms import TimeEvolutionProblem, VarQITE -from qiskit.algorithms.time_evolvers.variational import ( - ImaginaryMcLachlanPrinciple, -) -from qiskit.circuit.library import EfficientSU2 -from qiskit.quantum_info import Statevector - - -@ddt -class TestVarQITE(QiskitAlgorithmsTestCase): - """Test Variational Quantum Imaginary Time Evolution algorithm.""" - - def setUp(self): - super().setUp() - self.seed = 11 - np.random.seed(self.seed) - - def test_run_d_1_with_aux_ops(self): - """Test VarQITE for d = 1 and t = 1 with evaluating auxiliary operator and the Forward - Euler solver..""" - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - aux_ops = [Pauli("XX"), Pauli("YZ")] - d = 1 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - parameters = list(ansatz.parameters) - init_param_values = np.zeros(len(parameters)) - for i in range(len(parameters)): - init_param_values[i] = np.pi / 2 - init_param_values[0] = 1 - time = 1 - - evolution_problem = TimeEvolutionProblem(observable, time, aux_operators=aux_ops) - - thetas_expected = [ - 0.87984606025879, - 2.04681975664763, - 2.68980594039104, - 2.75915988512186, - 2.38796546567171, - 1.78144857115127, - 2.13109162826101, - 1.9259609596416, - ] - - thetas_expected_shots = [ - 0.9392668013702317, - 1.8756706968454864, - 2.6915067128662398, - 2.655420131540562, - 2.174687086978046, - 1.6997059390911056, - 1.8056912289547045, - 1.939353810908912, - ] - - with self.subTest(msg="Test exact backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - estimator = Estimator() - qgt = LinCombQGT(estimator) - gradient = LinCombEstimatorGradient(estimator) - var_principle = ImaginaryMcLachlanPrinciple(qgt, gradient) - - var_qite = VarQITE( - ansatz, init_param_values, var_principle, estimator, num_timesteps=25 - ) - evolution_result = var_qite.evolve(evolution_problem) - - aux_ops = evolution_result.aux_ops_evaluated - - parameter_values = evolution_result.parameter_values[-1] - - expected_aux_ops = (-0.2177982985749799, 0.2556790598588627) - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected[i], decimal=2 - ) - - np.testing.assert_array_almost_equal( - [result[0] for result in aux_ops], expected_aux_ops - ) - - with self.subTest(msg="Test shot-based backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - - estimator = Estimator(options={"shots": 4096, "seed": self.seed}) - qgt = LinCombQGT(estimator) - gradient = LinCombEstimatorGradient(estimator) - var_principle = ImaginaryMcLachlanPrinciple(qgt, gradient) - - var_qite = VarQITE( - ansatz, init_param_values, var_principle, estimator, num_timesteps=25 - ) - evolution_result = var_qite.evolve(evolution_problem) - - aux_ops = evolution_result.aux_ops_evaluated - - parameter_values = evolution_result.parameter_values[-1] - - expected_aux_ops = (-0.24629853310903974, 0.2518122871921184) - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected_shots[i], decimal=2 - ) - - np.testing.assert_array_almost_equal( - [result[0] for result in aux_ops], expected_aux_ops - ) - - def test_run_d_1_t_7(self): - """Test VarQITE for d = 1 and t = 7 with RK45 ODE solver.""" - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - d = 1 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - parameters = list(ansatz.parameters) - init_param_values = np.zeros(len(parameters)) - for i in range(len(parameters)): - init_param_values[i] = np.pi / 2 - init_param_values[0] = 1 - var_principle = ImaginaryMcLachlanPrinciple() - - time = 7 - var_qite = VarQITE( - ansatz, init_param_values, var_principle, ode_solver="RK45", num_timesteps=25 - ) - - thetas_expected = [ - 0.828917365718767, - 1.88481074798033, - 3.14111335991238, - 3.14125849601269, - 2.33768562678401, - 1.78670990729437, - 2.04214275514208, - 2.04009918594422, - ] - - self._test_helper(observable, thetas_expected, time, var_qite, 2) - - def test_run_d_2(self): - """Test VarQITE for d = 2 and t = 1 with RK45 ODE solver.""" - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - parameters = list(ansatz.parameters) - init_param_values = np.zeros(len(parameters)) - for i in range(len(parameters)): - init_param_values[i] = np.pi / 4 - - var_principle = ImaginaryMcLachlanPrinciple() - - time = 1 - var_qite = VarQITE( - ansatz, init_param_values, var_principle, ode_solver="RK45", num_timesteps=25 - ) - - thetas_expected = [ - 1.29495364023786, - 1.08970061333559, - 0.667488228710748, - 0.500122687902944, - 1.4377736672043, - 1.22881086103085, - 0.729773048146251, - 1.01698854755226, - 0.050807780587492, - 0.294828474947149, - 0.839305697704923, - 0.663689581255428, - ] - - self._test_helper(observable, thetas_expected, time, var_qite, 4) - - def test_run_d_1_time_dependent(self): - """Test VarQITE for d = 1 and a time-dependent Hamiltonian with the Forward Euler solver.""" - t_param = Parameter("t") - time = 1 - observable = SparsePauliOp(["I", "Z"], np.array([0, t_param])) - - x, y, z = [Parameter(s) for s in "xyz"] - ansatz = QuantumCircuit(1) - ansatz.rz(x, 0) - ansatz.ry(y, 0) - ansatz.rz(z, 0) - - parameters = list(ansatz.parameters) - init_param_values = np.zeros(len(parameters)) - x_val = 0 - y_val = np.pi / 2 - z_val = 0 - - init_param_values[0] = x_val - init_param_values[1] = y_val - init_param_values[2] = z_val - - evolution_problem = TimeEvolutionProblem(observable, time, t_param=t_param) - - thetas_expected = [1.83881002737137e-18, 2.43224994794434, -3.05311331771918e-18] - - thetas_expected_shots = [1.83881002737137e-18, 2.43224994794434, -3.05311331771918e-18] - - state_expected = Statevector([0.34849948 + 0.0j, 0.93730897 + 0.0j]).to_dict() - # the expected final state is Statevector([0.34849948+0.j, 0.93730897+0.j]) - - with self.subTest(msg="Test exact backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - estimator = Estimator() - var_principle = ImaginaryMcLachlanPrinciple() - - var_qite = VarQITE( - ansatz, init_param_values, var_principle, estimator, num_timesteps=100 - ) - evolution_result = var_qite.evolve(evolution_problem) - evolved_state = evolution_result.evolved_state - parameter_values = evolution_result.parameter_values[-1] - - for key, evolved_value in Statevector(evolved_state).to_dict().items(): - # np.allclose works with complex numbers - self.assertTrue(np.allclose(evolved_value, state_expected[key], 1e-02)) - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected[i], decimal=2 - ) - - with self.subTest(msg="Test shot-based backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - - estimator = Estimator(options={"shots": 4 * 4096, "seed": self.seed}) - var_principle = ImaginaryMcLachlanPrinciple() - - var_qite = VarQITE( - ansatz, init_param_values, var_principle, estimator, num_timesteps=100 - ) - - evolution_result = var_qite.evolve(evolution_problem) - - evolved_state = evolution_result.evolved_state - - parameter_values = evolution_result.parameter_values[-1] - - for key, evolved_value in Statevector(evolved_state).to_dict().items(): - # np.allclose works with complex numbers - self.assertTrue(np.allclose(evolved_value, state_expected[key], 1e-02)) - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected_shots[i], decimal=2 - ) - - def _test_helper(self, observable, thetas_expected, time, var_qite, decimal): - evolution_problem = TimeEvolutionProblem(observable, time) - evolution_result = var_qite.evolve(evolution_problem) - parameter_values = evolution_result.parameter_values[-1] - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected[i], decimal=decimal - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/test_var_qrte.py b/test/python/algorithms/time_evolvers/variational/test_var_qrte.py deleted file mode 100644 index e652125728b0..000000000000 --- a/test/python/algorithms/time_evolvers/variational/test_var_qrte.py +++ /dev/null @@ -1,319 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Variational Quantum Real Time Evolution algorithm.""" - -import unittest -import warnings -from test.python.algorithms import QiskitAlgorithmsTestCase - -from ddt import ddt -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.circuit import Parameter, ParameterVector -from qiskit.algorithms.gradients import LinCombQGT, DerivativeType, LinCombEstimatorGradient -from qiskit.primitives import Estimator -from qiskit.utils import algorithm_globals -from qiskit.quantum_info import SparsePauliOp, Pauli, Statevector -from qiskit.algorithms import TimeEvolutionProblem, VarQRTE -from qiskit.algorithms.time_evolvers.variational import ( - RealMcLachlanPrinciple, -) -from qiskit.circuit.library import EfficientSU2 - - -@ddt -class TestVarQRTE(QiskitAlgorithmsTestCase): - """Test Variational Quantum Real Time Evolution algorithm.""" - - def setUp(self): - super().setUp() - self.seed = 11 - np.random.seed(self.seed) - - def test_time_dependent_hamiltonian(self): - """Simple test case with a time dependent Hamiltonian.""" - t_param = Parameter("t") - hamiltonian = SparsePauliOp(["Z"], np.array(t_param)) - - x = ParameterVector("x", 3) - circuit = QuantumCircuit(1) - circuit.rz(x[0], 0) - circuit.ry(x[1], 0) - circuit.rz(x[2], 0) - - initial_parameters = np.array([0, np.pi / 2, 0]) - - def expected_state(time): - # possible with pen and paper as the Hamiltonian is diagonal - return 1 / np.sqrt(2) * np.array([np.exp(-0.5j * time**2), np.exp(0.5j * time**2)]) - - final_time = 0.75 - evolution_problem = TimeEvolutionProblem(hamiltonian, t_param=t_param, time=final_time) - estimator = Estimator() - varqrte = VarQRTE(circuit, initial_parameters, estimator=estimator) - - result = varqrte.evolve(evolution_problem) - - final_parameters = result.parameter_values[-1] - final_state = Statevector(circuit.assign_parameters(final_parameters)).to_dict() - final_expected_state = expected_state(final_time) - - for key, expected_value in final_state.items(): - self.assertTrue(np.allclose(final_expected_state[int(key)], expected_value, 1e-02)) - - def test_run_d_1_with_aux_ops(self): - """Test VarQRTE for d = 1 and t = 0.1 with evaluating auxiliary operators and the Forward - Euler solver.""" - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - aux_ops = [Pauli("XX"), Pauli("YZ")] - d = 1 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - parameters = list(ansatz.parameters) - init_param_values = np.zeros(len(parameters)) - for i in range(len(parameters)): - init_param_values[i] = np.pi / 2 - init_param_values[0] = 1 - - time = 0.1 - - evolution_problem = TimeEvolutionProblem(observable, time, aux_operators=aux_ops) - - thetas_expected = [ - 0.886841151529636, - 1.53852629218265, - 1.57099556659882, - 1.5889216657174, - 1.5996487153364, - 1.57018939515742, - 1.63950719260698, - 1.53853696496673, - ] - - thetas_expected_shots = [ - 0.886975892820015, - 1.53822607733397, - 1.57058096749141, - 1.59023223608564, - 1.60105707043745, - 1.57018042397236, - 1.64010900210835, - 1.53959523034133, - ] - - with self.subTest(msg="Test exact backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - estimator = Estimator() - qgt = LinCombQGT(estimator) - gradient = LinCombEstimatorGradient(estimator, derivative_type=DerivativeType.IMAG) - var_principle = RealMcLachlanPrinciple(qgt, gradient) - - var_qrte = VarQRTE( - ansatz, init_param_values, var_principle, estimator, num_timesteps=25 - ) - evolution_result = var_qrte.evolve(evolution_problem) - - aux_ops = evolution_result.aux_ops_evaluated - - parameter_values = evolution_result.parameter_values[-1] - - expected_aux_ops = [0.06836996703935797, 0.7711574493422457] - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected[i], decimal=2 - ) - - np.testing.assert_array_almost_equal( - [result[0] for result in aux_ops], expected_aux_ops - ) - - with self.subTest(msg="Test shot-based backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - - estimator = Estimator(options={"shots": 4 * 4096, "seed": self.seed}) - qgt = LinCombQGT(estimator) - gradient = LinCombEstimatorGradient(estimator, derivative_type=DerivativeType.IMAG) - var_principle = RealMcLachlanPrinciple(qgt, gradient) - - var_qrte = VarQRTE( - ansatz, init_param_values, var_principle, estimator, num_timesteps=25 - ) - evolution_result = var_qrte.evolve(evolution_problem) - - aux_ops = evolution_result.aux_ops_evaluated - - parameter_values = evolution_result.parameter_values[-1] - - expected_aux_ops = [ - 0.070436, - 0.777938, - ] - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected_shots[i], decimal=2 - ) - - np.testing.assert_array_almost_equal( - [result[0] for result in aux_ops], expected_aux_ops, decimal=2 - ) - - def test_run_d_2(self): - """Test VarQRTE for d = 2 and t = 1 with RK45 ODE solver.""" - - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - parameters = list(ansatz.parameters) - init_param_values = np.zeros(len(parameters)) - for i in range(len(parameters)): - init_param_values[i] = np.pi / 4 - estimator = Estimator() - qgt = LinCombQGT(estimator) - gradient = LinCombEstimatorGradient(estimator, derivative_type=DerivativeType.IMAG) - - var_principle = RealMcLachlanPrinciple(qgt, gradient) - - param_dict = dict(zip(parameters, init_param_values)) - - time = 1 - var_qrte = VarQRTE(ansatz, param_dict, var_principle, ode_solver="RK45", num_timesteps=25) - - thetas_expected = [ - 0.348407744196573, - 0.919404626262464, - 1.18189219371626, - 0.771011177789998, - 0.734384256533924, - 0.965289520781899, - 1.14441687204195, - 1.17231927568571, - 1.03014771379412, - 0.867266309056347, - 0.699606368428206, - 0.610788576398685, - ] - - self._test_helper(observable, thetas_expected, time, var_qrte) - - def test_run_d_1_time_dependent(self): - """Test VarQRTE for d = 1 and a time-dependent Hamiltonian with the Forward Euler solver.""" - t_param = Parameter("t") - time = 1 - observable = SparsePauliOp(["I", "Z"], np.array([0, t_param])) - - x, y, z = [Parameter(s) for s in "xyz"] - ansatz = QuantumCircuit(1) - ansatz.rz(x, 0) - ansatz.ry(y, 0) - ansatz.rz(z, 0) - - parameters = list(ansatz.parameters) - init_param_values = np.zeros(len(parameters)) - x_val = 0 - y_val = np.pi / 2 - z_val = 0 - - init_param_values[0] = x_val - init_param_values[1] = y_val - init_param_values[2] = z_val - - evolution_problem = TimeEvolutionProblem(observable, time, t_param=t_param) - - thetas_expected = [1.27675647831902e-18, 1.5707963267949, 0.990000000000001] - - thetas_expected_shots = [0.00534345821469238, 1.56260960200375, 0.990017403734316] - - # the expected final state is Statevector([0.62289306-0.33467034j, 0.62289306+0.33467034j]) - - with self.subTest(msg="Test exact backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - estimator = Estimator() - qgt = LinCombQGT(estimator) - gradient = LinCombEstimatorGradient(estimator, derivative_type=DerivativeType.IMAG) - var_principle = RealMcLachlanPrinciple(qgt, gradient) - - var_qrte = VarQRTE( - ansatz, init_param_values, var_principle, estimator, num_timesteps=100 - ) - evolution_result = var_qrte.evolve(evolution_problem) - - parameter_values = evolution_result.parameter_values[-1] - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected[i], decimal=2 - ) - - with self.subTest(msg="Test shot-based backend."): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = self.seed - - estimator = Estimator(options={"shots": 4 * 4096, "seed": self.seed}) - qgt = LinCombQGT(estimator) - gradient = LinCombEstimatorGradient(estimator, derivative_type=DerivativeType.IMAG) - var_principle = RealMcLachlanPrinciple(qgt, gradient) - - var_qrte = VarQRTE( - ansatz, init_param_values, var_principle, estimator, num_timesteps=100 - ) - - evolution_result = var_qrte.evolve(evolution_problem) - - parameter_values = evolution_result.parameter_values[-1] - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal( - float(parameter_value), thetas_expected_shots[i], decimal=2 - ) - - def _test_helper(self, observable, thetas_expected, time, var_qrte): - evolution_problem = TimeEvolutionProblem(observable, time) - evolution_result = var_qrte.evolve(evolution_problem) - - parameter_values = evolution_result.parameter_values[-1] - - for i, parameter_value in enumerate(parameter_values): - np.testing.assert_almost_equal(float(parameter_value), thetas_expected[i], decimal=4) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/test_var_qte.py b/test/python/algorithms/time_evolvers/variational/test_var_qte.py deleted file mode 100644 index 4b92e4e460d0..000000000000 --- a/test/python/algorithms/time_evolvers/variational/test_var_qte.py +++ /dev/null @@ -1,84 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Test Variational Quantum Real Time Evolution algorithm.""" - -import unittest - -from test.python.algorithms import QiskitAlgorithmsTestCase -from numpy.testing import assert_raises -from ddt import data, ddt -import numpy as np - -from qiskit.algorithms.time_evolvers.variational.var_qte import VarQTE -from qiskit.circuit import Parameter - - -@ddt -class TestVarQTE(QiskitAlgorithmsTestCase): - """Test Variational Quantum Time Evolution class methods.""" - - def setUp(self): - super().setUp() - self._parameters1 = [Parameter("a"), Parameter("b"), Parameter("c")] - - @data([1.4, 2, 3], np.asarray([1.4, 2, 3])) - def test_create_init_state_param_dict(self, param_values): - """Tests if a correct dictionary is created.""" - expected = dict(zip(self._parameters1, param_values)) - with self.subTest("Parameters values given as a list test."): - result = VarQTE._create_init_state_param_dict(param_values, self._parameters1) - np.testing.assert_equal(result, expected) - with self.subTest("Parameters values given as a dictionary test."): - result = VarQTE._create_init_state_param_dict( - dict(zip(self._parameters1, param_values)), self._parameters1 - ) - np.testing.assert_equal(result, expected) - with self.subTest("Parameters values given as a superset dictionary test."): - expected = dict( - zip( - [self._parameters1[0], self._parameters1[2]], [param_values[0], param_values[2]] - ) - ) - result = VarQTE._create_init_state_param_dict( - dict(zip(self._parameters1, param_values)), - [self._parameters1[0], self._parameters1[2]], - ) - np.testing.assert_equal(result, expected) - - @data([1.4, 2], np.asarray([1.4, 3]), {}, []) - def test_create_init_state_param_dict_errors_list(self, param_values): - """Tests if an error is raised.""" - with assert_raises(ValueError): - _ = VarQTE._create_init_state_param_dict(param_values, self._parameters1) - - @data([1.4, 2], np.asarray([1.4, 3])) - def test_create_init_state_param_dict_errors_subset(self, param_values): - """Tests if an error is raised if subset of parameters provided.""" - param_values_dict = dict(zip([self._parameters1[0], self._parameters1[2]], param_values)) - with assert_raises(ValueError): - _ = VarQTE._create_init_state_param_dict(param_values_dict, self._parameters1) - - @data("s") - def test_create_init_state_param_dict_errors_value(self, param_values): - """Tests if an error is raised if wrong input.""" - with assert_raises(ValueError): - _ = VarQTE._create_init_state_param_dict(param_values, self._parameters1) - - @data(Parameter("x"), 5) - def test_create_init_state_param_dict_errors_type(self, param_values): - """Tests if an error is raised if wrong input type.""" - with assert_raises(TypeError): - _ = VarQTE._create_init_state_param_dict(param_values, self._parameters1) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/__init__.py b/test/python/algorithms/time_evolvers/variational/variational_principles/__init__.py deleted file mode 100644 index 26f7536d3514..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/__init__.py b/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/__init__.py deleted file mode 100644 index 62ef1f76c6f4..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Stores expected results that are lengthy.""" diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected1.py b/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected1.py deleted file mode 100644 index a26cb1b8726b..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected1.py +++ /dev/null @@ -1,182 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Stores expected results that are lengthy.""" -expected_bound_metric_tensor_1 = [ - [ - 2.50000000e-01 + 0.0j, - 1.59600000e-33 + 0.0j, - 5.90075760e-18 + 0.0j, - -8.49242405e-19 + 0.0j, - 8.83883476e-02 + 0.0j, - 1.33253788e-17 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.40000000e-17 + 0.0j, - -1.41735435e-01 + 0.0j, - 3.12500000e-02 + 0.0j, - 1.00222087e-01 + 0.0j, - -3.12500000e-02 + 0.0j, - ], - [ - 1.59600000e-33 + 0.0j, - 2.50000000e-01 + 0.0j, - 1.34350288e-17 + 0.0j, - 6.43502884e-18 + 0.0j, - -8.83883476e-02 + 0.0j, - 1.25000000e-01 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.25000000e-01 + 0.0j, - -8.45970869e-02 + 0.0j, - 7.54441738e-02 + 0.0j, - 1.48207521e-01 + 0.0j, - 2.00444174e-01 + 0.0j, - ], - [ - 5.90075760e-18 + 0.0j, - 1.34350288e-17 + 0.0j, - 1.25000000e-01 + 0.0j, - -1.38777878e-17 + 0.0j, - -4.41941738e-02 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.19638348e-01 + 0.0j, - 6.25000000e-02 + 0.0j, - -5.14514565e-02 + 0.0j, - 6.89720869e-02 + 0.0j, - 1.04933262e-02 + 0.0j, - -6.89720869e-02 + 0.0j, - ], - [ - -8.49242405e-19 + 0.0j, - 6.43502884e-18 + 0.0j, - -1.38777878e-17 + 0.0j, - 1.25000000e-01 + 0.0j, - -4.41941738e-02 + 0.0j, - -6.25000000e-02 + 0.0j, - 3.12500000e-02 + 0.0j, - 1.25000000e-01 + 0.0j, - 5.14514565e-02 + 0.0j, - -6.89720869e-02 + 0.0j, - 7.81250000e-03 + 0.0j, - 1.94162607e-02 + 0.0j, - ], - [ - 8.83883476e-02 + 0.0j, - -8.83883476e-02 + 0.0j, - -4.41941738e-02 + 0.0j, - -4.41941738e-02 + 0.0j, - 2.34375000e-01 + 0.0j, - -1.10485435e-01 + 0.0j, - -2.02014565e-02 + 0.0j, - -4.41941738e-02 + 0.0j, - 1.49547935e-02 + 0.0j, - -2.24896848e-02 + 0.0j, - -1.42172278e-03 + 0.0j, - -1.23822206e-01 + 0.0j, - ], - [ - 1.33253788e-17 + 0.0j, - 1.25000000e-01 + 0.0j, - 6.25000000e-02 + 0.0j, - -6.25000000e-02 + 0.0j, - -1.10485435e-01 + 0.0j, - 2.18750000e-01 + 0.0j, - -2.68082618e-03 + 0.0j, - -1.59099026e-17 + 0.0j, - -1.57197815e-01 + 0.0j, - 2.53331304e-02 + 0.0j, - 9.82311963e-03 + 0.0j, - 1.06138957e-01 + 0.0j, - ], - [ - 6.25000000e-02 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.19638348e-01 + 0.0j, - 3.12500000e-02 + 0.0j, - -2.02014565e-02 + 0.0j, - -2.68082618e-03 + 0.0j, - 2.23881674e-01 + 0.0j, - 1.37944174e-01 + 0.0j, - -3.78033966e-02 + 0.0j, - 1.58423239e-01 + 0.0j, - 1.34535646e-01 + 0.0j, - -5.49651086e-02 + 0.0j, - ], - [ - 1.40000000e-17 + 0.0j, - 1.25000000e-01 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.25000000e-01 + 0.0j, - -4.41941738e-02 + 0.0j, - -1.59099026e-17 + 0.0j, - 1.37944174e-01 + 0.0j, - 2.50000000e-01 + 0.0j, - -2.10523539e-17 + 0.0j, - 1.15574269e-17 + 0.0j, - 9.75412607e-02 + 0.0j, - 5.71383476e-02 + 0.0j, - ], - [ - -1.41735435e-01 + 0.0j, - -8.45970869e-02 + 0.0j, - -5.14514565e-02 + 0.0j, - 5.14514565e-02 + 0.0j, - 1.49547935e-02 + 0.0j, - -1.57197815e-01 + 0.0j, - -3.78033966e-02 + 0.0j, - -2.10523539e-17 + 0.0j, - 1.95283753e-01 + 0.0j, - -3.82941440e-02 + 0.0j, - -6.11392595e-02 + 0.0j, - -4.51588288e-02 + 0.0j, - ], - [ - 3.12500000e-02 + 0.0j, - 7.54441738e-02 + 0.0j, - 6.89720869e-02 + 0.0j, - -6.89720869e-02 + 0.0j, - -2.24896848e-02 + 0.0j, - 2.53331304e-02 + 0.0j, - 1.58423239e-01 + 0.0j, - 1.15574269e-17 + 0.0j, - -3.82941440e-02 + 0.0j, - 2.17629701e-01 + 0.0j, - 1.32431810e-01 + 0.0j, - -1.91961467e-02 + 0.0j, - ], - [ - 1.00222087e-01 + 0.0j, - 1.48207521e-01 + 0.0j, - 1.04933262e-02 + 0.0j, - 7.81250000e-03 + 0.0j, - -1.42172278e-03 + 0.0j, - 9.82311963e-03 + 0.0j, - 1.34535646e-01 + 0.0j, - 9.75412607e-02 + 0.0j, - -6.11392595e-02 + 0.0j, - 1.32431810e-01 + 0.0j, - 1.81683746e-01 + 0.0j, - 7.28902444e-02 + 0.0j, - ], - [ - -3.12500000e-02 + 0.0j, - 2.00444174e-01 + 0.0j, - -6.89720869e-02 + 0.0j, - 1.94162607e-02 + 0.0j, - -1.23822206e-01 + 0.0j, - 1.06138957e-01 + 0.0j, - -5.49651086e-02 + 0.0j, - 5.71383476e-02 + 0.0j, - -4.51588288e-02 + 0.0j, - -1.91961467e-02 + 0.0j, - 7.28902444e-02 + 0.0j, - 2.38616353e-01 + 0.0j, - ], -] diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected2.py b/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected2.py deleted file mode 100644 index 3a46f371787c..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected2.py +++ /dev/null @@ -1,182 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Stores expected results that are lengthy.""" -expected_bound_metric_tensor_2 = [ - [ - 2.50000000e-01 + 0.0j, - 1.59600000e-33 + 0.0j, - 5.90075760e-18 + 0.0j, - -8.49242405e-19 + 0.0j, - 8.83883476e-02 + 0.0j, - 1.33253788e-17 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.40000000e-17 + 0.0j, - -1.41735435e-01 + 0.0j, - 3.12500000e-02 + 0.0j, - 1.00222087e-01 + 0.0j, - -3.12500000e-02 + 0.0j, - ], - [ - 1.59600000e-33 + 0.0j, - 2.50000000e-01 + 0.0j, - 1.34350288e-17 + 0.0j, - 6.43502884e-18 + 0.0j, - -8.83883476e-02 + 0.0j, - 1.25000000e-01 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.25000000e-01 + 0.0j, - -8.45970869e-02 + 0.0j, - 7.54441738e-02 + 0.0j, - 1.48207521e-01 + 0.0j, - 2.00444174e-01 + 0.0j, - ], - [ - 5.90075760e-18 + 0.0j, - 1.34350288e-17 + 0.0j, - 1.25000000e-01 + 0.0j, - -1.38777878e-17 + 0.0j, - -4.41941738e-02 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.19638348e-01 + 0.0j, - 6.25000000e-02 + 0.0j, - -5.14514565e-02 + 0.0j, - 6.89720869e-02 + 0.0j, - 1.04933262e-02 + 0.0j, - -6.89720869e-02 + 0.0j, - ], - [ - -8.49242405e-19 + 0.0j, - 6.43502884e-18 + 0.0j, - -1.38777878e-17 + 0.0j, - 1.25000000e-01 + 0.0j, - -4.41941738e-02 + 0.0j, - -6.25000000e-02 + 0.0j, - 3.12500000e-02 + 0.0j, - 1.25000000e-01 + 0.0j, - 5.14514565e-02 + 0.0j, - -6.89720869e-02 + 0.0j, - 7.81250000e-03 + 0.0j, - 1.94162607e-02 + 0.0j, - ], - [ - 8.83883476e-02 + 0.0j, - -8.83883476e-02 + 0.0j, - -4.41941738e-02 + 0.0j, - -4.41941738e-02 + 0.0j, - 2.34375000e-01 + 0.0j, - -1.10485435e-01 + 0.0j, - -2.02014565e-02 + 0.0j, - -4.41941738e-02 + 0.0j, - 1.49547935e-02 + 0.0j, - -2.24896848e-02 + 0.0j, - -1.42172278e-03 + 0.0j, - -1.23822206e-01 + 0.0j, - ], - [ - 1.33253788e-17 + 0.0j, - 1.25000000e-01 + 0.0j, - 6.25000000e-02 + 0.0j, - -6.25000000e-02 + 0.0j, - -1.10485435e-01 + 0.0j, - 2.18750000e-01 + 0.0j, - -2.68082618e-03 + 0.0j, - -1.59099026e-17 + 0.0j, - -1.57197815e-01 + 0.0j, - 2.53331304e-02 + 0.0j, - 9.82311963e-03 + 0.0j, - 1.06138957e-01 + 0.0j, - ], - [ - 6.25000000e-02 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.19638348e-01 + 0.0j, - 3.12500000e-02 + 0.0j, - -2.02014565e-02 + 0.0j, - -2.68082618e-03 + 0.0j, - 2.23881674e-01 + 0.0j, - 1.37944174e-01 + 0.0j, - -3.78033966e-02 + 0.0j, - 1.58423239e-01 + 0.0j, - 1.34535646e-01 + 0.0j, - -5.49651086e-02 + 0.0j, - ], - [ - 1.40000000e-17 + 0.0j, - 1.25000000e-01 + 0.0j, - 6.25000000e-02 + 0.0j, - 1.25000000e-01 + 0.0j, - -4.41941738e-02 + 0.0j, - -1.59099026e-17 + 0.0j, - 1.37944174e-01 + 0.0j, - 2.50000000e-01 + 0.0j, - -2.10523539e-17 + 0.0j, - 1.15574269e-17 + 0.0j, - 9.75412607e-02 + 0.0j, - 5.71383476e-02 + 0.0j, - ], - [ - -1.41735435e-01 + 0.0j, - -8.45970869e-02 + 0.0j, - -5.14514565e-02 + 0.0j, - 5.14514565e-02 + 0.0j, - 1.49547935e-02 + 0.0j, - -1.57197815e-01 + 0.0j, - -3.78033966e-02 + 0.0j, - -2.10523539e-17 + 0.0j, - 1.95283753e-01 + 0.0j, - -3.82941440e-02 + 0.0j, - -6.11392595e-02 + 0.0j, - -4.51588288e-02 + 0.0j, - ], - [ - 3.12500000e-02 + 0.0j, - 7.54441738e-02 + 0.0j, - 6.89720869e-02 + 0.0j, - -6.89720869e-02 + 0.0j, - -2.24896848e-02 + 0.0j, - 2.53331304e-02 + 0.0j, - 1.58423239e-01 + 0.0j, - 1.15574269e-17 + 0.0j, - -3.82941440e-02 + 0.0j, - 2.17629701e-01 + 0.0j, - 1.32431810e-01 + 0.0j, - -1.91961467e-02 + 0.0j, - ], - [ - 1.00222087e-01 + 0.0j, - 1.48207521e-01 + 0.0j, - 1.04933262e-02 + 0.0j, - 7.81250000e-03 + 0.0j, - -1.42172278e-03 + 0.0j, - 9.82311963e-03 + 0.0j, - 1.34535646e-01 + 0.0j, - 9.75412607e-02 + 0.0j, - -6.11392595e-02 + 0.0j, - 1.32431810e-01 + 0.0j, - 1.81683746e-01 + 0.0j, - 7.28902444e-02 + 0.0j, - ], - [ - -3.12500000e-02 + 0.0j, - 2.00444174e-01 + 0.0j, - -6.89720869e-02 + 0.0j, - 1.94162607e-02 + 0.0j, - -1.23822206e-01 + 0.0j, - 1.06138957e-01 + 0.0j, - -5.49651086e-02 + 0.0j, - 5.71383476e-02 + 0.0j, - -4.51588288e-02 + 0.0j, - -1.91961467e-02 + 0.0j, - 7.28902444e-02 + 0.0j, - 2.38616353e-01 + 0.0j, - ], -] diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected3.py b/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected3.py deleted file mode 100644 index 4790482e0db9..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/expected_results/test_imaginary_mc_lachlan_variational_principle_expected3.py +++ /dev/null @@ -1,182 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -"""Stores expected results that are lengthy.""" -expected_bound_metric_tensor_3 = [ - [ - -1.21000000e-34 + 0.00e00j, - 1.21000000e-34 + 2.50e-19j, - 1.76776695e-01 - 1.00e-18j, - -1.40000000e-17 + 0.00e00j, - -6.25000000e-02 + 0.00e00j, - 8.83883476e-02 - 1.25e-18j, - 1.69194174e-01 + 2.25e-18j, - 8.83883476e-02 - 2.50e-19j, - -7.27633476e-02 + 0.00e00j, - 9.75412607e-02 + 7.50e-19j, - 1.48398042e-02 - 1.75e-18j, - -9.75412607e-02 + 3.75e-18j, - ], - [ - 1.21000000e-34 + 2.50e-19j, - -1.21000000e-34 + 0.00e00j, - 1.10000000e-34 + 2.75e-18j, - 1.76776695e-01 - 2.25e-18j, - -6.25000000e-02 + 0.00e00j, - -8.83883476e-02 + 4.00e-18j, - 4.41941738e-02 - 1.25e-18j, - 1.76776695e-01 - 2.50e-19j, - 7.27633476e-02 - 7.50e-19j, - -9.75412607e-02 - 7.50e-19j, - 1.10485435e-02 - 7.50e-19j, - 2.74587393e-02 + 2.50e-19j, - ], - [ - 1.76776695e-01 - 1.00e-18j, - 1.10000000e-34 + 2.75e-18j, - -1.25000000e-01 + 0.00e00j, - -1.25000000e-01 + 0.00e00j, - -1.06694174e-01 + 1.25e-18j, - -6.25000000e-02 + 1.75e-18j, - -1.01332521e-01 + 7.50e-19j, - 4.67500000e-17 - 7.50e-19j, - 1.75206304e-02 + 5.00e-19j, - -8.57075215e-02 - 1.00e-18j, - -1.63277304e-01 + 1.00e-18j, - -1.56250000e-02 + 0.00e00j, - ], - [ - -1.40000000e-17 + 0.00e00j, - 1.76776695e-01 - 2.25e-18j, - -1.25000000e-01 + 0.00e00j, - -1.25000000e-01 + 0.00e00j, - 1.83058262e-02 - 1.50e-18j, - -1.50888348e-01 - 1.50e-18j, - -1.01332521e-01 + 2.50e-19j, - -8.83883476e-02 - 1.00e-18j, - -2.28822827e-02 - 1.00e-18j, - -1.16957521e-01 + 1.00e-18j, - -1.97208130e-01 + 0.00e00j, - -1.79457521e-01 + 1.25e-18j, - ], - [ - -6.25000000e-02 + 0.00e00j, - -6.25000000e-02 + 0.00e00j, - -1.06694174e-01 + 1.25e-18j, - 1.83058262e-02 - 1.50e-18j, - -1.56250000e-02 + 0.00e00j, - -2.20970869e-02 - 2.00e-18j, - 1.48992717e-01 - 1.00e-18j, - 2.60000000e-17 - 1.50e-18j, - -6.69614673e-02 - 5.00e-19j, - 2.00051576e-01 + 5.00e-19j, - 1.13640168e-01 + 1.25e-18j, - -4.83780325e-02 - 1.00e-18j, - ], - [ - 8.83883476e-02 - 1.25e-18j, - -8.83883476e-02 + 4.00e-18j, - -6.25000000e-02 + 1.75e-18j, - -1.50888348e-01 - 1.50e-18j, - -2.20970869e-02 - 2.00e-18j, - -3.12500000e-02 + 0.00e00j, - -2.85691738e-02 + 4.25e-18j, - 1.76776695e-01 + 0.00e00j, - 5.52427173e-03 + 1.00e-18j, - -1.29346478e-01 + 5.00e-19j, - -4.81004238e-02 + 4.25e-18j, - 5.27918696e-02 + 2.50e-19j, - ], - [ - 1.69194174e-01 + 2.25e-18j, - 4.41941738e-02 - 1.25e-18j, - -1.01332521e-01 + 7.50e-19j, - -1.01332521e-01 + 2.50e-19j, - 1.48992717e-01 - 1.00e-18j, - -2.85691738e-02 + 4.25e-18j, - -2.61183262e-02 + 0.00e00j, - -6.88900000e-33 + 0.00e00j, - 6.62099510e-02 - 1.00e-18j, - -2.90767610e-02 + 1.75e-18j, - -1.24942505e-01 + 0.00e00j, - -1.72430217e-02 + 2.50e-19j, - ], - [ - 8.83883476e-02 - 2.50e-19j, - 1.76776695e-01 - 2.50e-19j, - 4.67500000e-17 - 7.50e-19j, - -8.83883476e-02 - 1.00e-18j, - 2.60000000e-17 - 1.50e-18j, - 1.76776695e-01 + 0.00e00j, - -6.88900000e-33 + 0.00e00j, - -6.88900000e-33 + 0.00e00j, - 1.79457521e-01 - 1.75e-18j, - -5.33470869e-02 + 2.00e-18j, - -9.56456304e-02 + 3.00e-18j, - -1.32582521e-01 + 2.50e-19j, - ], - [ - -7.27633476e-02 + 0.00e00j, - 7.27633476e-02 - 7.50e-19j, - 1.75206304e-02 + 5.00e-19j, - -2.28822827e-02 - 1.00e-18j, - -6.69614673e-02 - 5.00e-19j, - 5.52427173e-03 + 1.00e-18j, - 6.62099510e-02 - 1.00e-18j, - 1.79457521e-01 - 1.75e-18j, - -5.47162473e-02 + 0.00e00j, - -4.20854047e-02 + 4.00e-18j, - -7.75494553e-02 - 2.50e-18j, - -2.49573723e-02 + 7.50e-19j, - ], - [ - 9.75412607e-02 + 7.50e-19j, - -9.75412607e-02 - 7.50e-19j, - -8.57075215e-02 - 1.00e-18j, - -1.16957521e-01 + 1.00e-18j, - 2.00051576e-01 + 5.00e-19j, - -1.29346478e-01 + 5.00e-19j, - -2.90767610e-02 + 1.75e-18j, - -5.33470869e-02 + 2.00e-18j, - -4.20854047e-02 + 4.00e-18j, - -3.23702991e-02 + 0.00e00j, - -4.70257118e-02 + 0.00e00j, - 1.22539288e-01 - 2.25e-18j, - ], - [ - 1.48398042e-02 - 1.75e-18j, - 1.10485435e-02 - 7.50e-19j, - -1.63277304e-01 + 1.00e-18j, - -1.97208130e-01 + 0.00e00j, - 1.13640168e-01 + 1.25e-18j, - -4.81004238e-02 + 4.25e-18j, - -1.24942505e-01 + 0.00e00j, - -9.56456304e-02 + 3.00e-18j, - -7.75494553e-02 - 2.50e-18j, - -4.70257118e-02 + 0.00e00j, - -6.83162540e-02 + 0.00e00j, - -2.78870598e-02 + 0.00e00j, - ], - [ - -9.75412607e-02 + 3.75e-18j, - 2.74587393e-02 + 2.50e-19j, - -1.56250000e-02 + 0.00e00j, - -1.79457521e-01 + 1.25e-18j, - -4.83780325e-02 - 1.00e-18j, - 5.27918696e-02 + 2.50e-19j, - -1.72430217e-02 + 2.50e-19j, - -1.32582521e-01 + 2.50e-19j, - -2.49573723e-02 + 7.50e-19j, - 1.22539288e-01 - 2.25e-18j, - -2.78870598e-02 + 0.00e00j, - -1.13836467e-02 + 0.00e00j, - ], -] diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/imaginary/__init__.py b/test/python/algorithms/time_evolvers/variational/variational_principles/imaginary/__init__.py deleted file mode 100644 index 26f7536d3514..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/imaginary/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/imaginary/test_imaginary_mc_lachlan_principle.py b/test/python/algorithms/time_evolvers/variational/variational_principles/imaginary/test_imaginary_mc_lachlan_principle.py deleted file mode 100644 index 8bb0f0d7c20d..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/imaginary/test_imaginary_mc_lachlan_principle.py +++ /dev/null @@ -1,115 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test imaginary McLachlan's variational principle.""" - -import unittest - -# fmt: off -from test.python.algorithms.time_evolvers.variational.variational_principles.expected_results.\ - test_imaginary_mc_lachlan_variational_principle_expected1 import expected_bound_metric_tensor_1 -# fmt: on -from test.python.algorithms import QiskitAlgorithmsTestCase -import numpy as np - -from qiskit.quantum_info import SparsePauliOp -from qiskit.algorithms.time_evolvers.variational import ( - ImaginaryMcLachlanPrinciple, -) -from qiskit.circuit.library import EfficientSU2 -from qiskit.algorithms.gradients import LinCombEstimatorGradient, DerivativeType -from qiskit.primitives import Estimator - - -class TestImaginaryMcLachlanPrinciple(QiskitAlgorithmsTestCase): - """Test imaginary McLachlan's variational principle.""" - - def test_calc_metric_tensor(self): - """Test calculating a metric tensor.""" - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - # Define a set of initial parameters - parameters = list(ansatz.parameters) - param_dict = {param: np.pi / 4 for param in parameters} - var_principle = ImaginaryMcLachlanPrinciple() - - bound_metric_tensor = var_principle.metric_tensor(ansatz, list(param_dict.values())) - - np.testing.assert_almost_equal(bound_metric_tensor, expected_bound_metric_tensor_1) - - def test_calc_calc_evolution_gradient(self): - """Test calculating evolution gradient.""" - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - # Define a set of initial parameters - parameters = list(ansatz.parameters) - param_dict = {param: np.pi / 4 for param in parameters} - var_principle = ImaginaryMcLachlanPrinciple() - - bound_evolution_gradient = var_principle.evolution_gradient( - observable, ansatz, list(param_dict.values()), parameters - ) - - expected_evolution_gradient = [ - (0.19308934095957098 - 1.4e-17j), - (0.007027674650099142 - 0j), - (0.03192524520091862 - 0j), - (-0.06810314606309673 - 1e-18j), - (0.07590371669521798 - 7e-18j), - (0.11891968269385343 + 1.5e-18j), - (-0.0012030273438232639 + 0j), - (-0.049885258804562266 + 1.8500000000000002e-17j), - (-0.20178860797540302 - 5e-19j), - (-0.0052269232310933195 + 1e-18j), - (0.022892905637005266 - 3e-18j), - (-0.022892905637005294 + 3.5e-18j), - ] - - np.testing.assert_almost_equal(bound_evolution_gradient, expected_evolution_gradient) - - def test_gradient_setting(self): - """Test reactions to wrong gradient settings..""" - estimator = Estimator() - gradient = LinCombEstimatorGradient(estimator, derivative_type=DerivativeType.IMAG) - - with self.assertWarns(Warning): - var_principle = ImaginaryMcLachlanPrinciple(gradient=gradient) - - np.testing.assert_equal(var_principle.gradient._derivative_type, DerivativeType.REAL) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/real/__init__.py b/test/python/algorithms/time_evolvers/variational/variational_principles/real/__init__.py deleted file mode 100644 index 26f7536d3514..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/real/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/time_evolvers/variational/variational_principles/real/test_real_mc_lachlan_principle.py b/test/python/algorithms/time_evolvers/variational/variational_principles/real/test_real_mc_lachlan_principle.py deleted file mode 100644 index 5a314979d5de..000000000000 --- a/test/python/algorithms/time_evolvers/variational/variational_principles/real/test_real_mc_lachlan_principle.py +++ /dev/null @@ -1,120 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test real McLachlan's variational principle.""" - -import unittest - -from test.python.algorithms import QiskitAlgorithmsTestCase - -# fmt: off -from test.python.algorithms.time_evolvers.variational.variational_principles.expected_results.\ - test_imaginary_mc_lachlan_variational_principle_expected2 import expected_bound_metric_tensor_2 -# fmt: on -import numpy as np - -from qiskit.quantum_info import SparsePauliOp -from qiskit.algorithms.time_evolvers.variational import ( - RealMcLachlanPrinciple, -) -from qiskit.circuit.library import EfficientSU2 -from qiskit.algorithms.gradients import LinCombEstimatorGradient, DerivativeType -from qiskit.primitives import Estimator - - -class TestRealMcLachlanPrinciple(QiskitAlgorithmsTestCase): - """Test real McLachlan's variational principle.""" - - def test_calc_calc_metric_tensor(self): - """Test calculating a metric tensor.""" - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - # Define a set of initial parameters - parameters = list(ansatz.parameters) - param_dict = {param: np.pi / 4 for param in parameters} - var_principle = RealMcLachlanPrinciple() - - bound_metric_tensor = var_principle.metric_tensor(ansatz, list(param_dict.values())) - - np.testing.assert_almost_equal( - bound_metric_tensor, expected_bound_metric_tensor_2, decimal=5 - ) - - def test_calc_evolution_gradient(self): - """Test calculating evolution gradient.""" - observable = SparsePauliOp.from_list( - [ - ("II", 0.2252), - ("ZZ", 0.5716), - ("IZ", 0.3435), - ("ZI", -0.4347), - ("YY", 0.091), - ("XX", 0.091), - ] - ) - - d = 2 - ansatz = EfficientSU2(observable.num_qubits, reps=d) - - # Define a set of initial parameters - parameters = list(ansatz.parameters) - param_dict = {param: np.pi / 4 for param in parameters} - var_principle = RealMcLachlanPrinciple() - - bound_evolution_gradient = var_principle.evolution_gradient( - observable, ansatz, list(param_dict.values()), parameters - ) - - expected_evolution_gradient = [ - (-0.04514911474522546 + 4e-18j), - (0.0963123928027075 - 1.5e-18j), - (0.1365347823673539 - 7e-18j), - (0.004969316401057883 - 4.9999999999999996e-18j), - (-0.003843833929692342 - 4.999999999999998e-19j), - (0.07036988622493834 - 7e-18j), - (0.16560609099860682 - 3.5e-18j), - (0.16674183768051887 + 1e-18j), - (-0.03843296670360974 - 6e-18j), - (0.08891074158680243 - 6e-18j), - (0.06425681697616654 + 7e-18j), - (-0.03172376682078948 - 7e-18j), - ] - - np.testing.assert_almost_equal( - bound_evolution_gradient, expected_evolution_gradient, decimal=5 - ) - - def test_gradient_setting(self): - """Test reactions to wrong gradient settings..""" - estimator = Estimator() - gradient = LinCombEstimatorGradient(estimator, derivative_type=DerivativeType.REAL) - - with self.assertWarns(Warning): - var_principle = RealMcLachlanPrinciple(gradient=gradient) - - np.testing.assert_equal(var_principle.gradient._derivative_type, DerivativeType.IMAG) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/algorithms/utils/__init__.py b/test/python/algorithms/utils/__init__.py deleted file mode 100644 index fdb172d367f0..000000000000 --- a/test/python/algorithms/utils/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. diff --git a/test/python/algorithms/utils/test_validate_bounds.py b/test/python/algorithms/utils/test_validate_bounds.py deleted file mode 100644 index e4cc42ad154f..000000000000 --- a/test/python/algorithms/utils/test_validate_bounds.py +++ /dev/null @@ -1,57 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test validate bounds.""" - -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -from unittest.mock import Mock - -import numpy as np - -from qiskit.algorithms.utils import validate_bounds -from qiskit.utils import algorithm_globals - - -class TestValidateBounds(QiskitAlgorithmsTestCase): - """Test the ``validate_bounds`` utility function.""" - - def setUp(self): - super().setUp() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - self.bounds = [(-np.pi / 2, np.pi / 2)] - self.ansatz = Mock() - - def test_with_no_ansatz_bounds(self): - """Test with no ansatz bounds.""" - self.ansatz.num_parameters = 1 - self.ansatz.parameter_bounds = None - bounds = validate_bounds(self.ansatz) - self.assertEqual(bounds, [(None, None)]) - - def test_with_ansatz_bounds(self): - """Test with ansatz bounds.""" - self.ansatz.num_parameters = 1 - self.ansatz.parameter_bounds = self.bounds - bounds = validate_bounds(self.ansatz) - self.assertEqual(bounds, self.bounds) - - def test_with_mismatched_num_params(self): - """Test with a mismatched number of parameters and bounds""" - self.ansatz.num_parameters = 2 - self.ansatz.parameter_bounds = self.bounds - with self.assertRaises(ValueError): - _ = validate_bounds(self.ansatz) diff --git a/test/python/algorithms/utils/test_validate_initial_point.py b/test/python/algorithms/utils/test_validate_initial_point.py deleted file mode 100644 index 28854b485fee..000000000000 --- a/test/python/algorithms/utils/test_validate_initial_point.py +++ /dev/null @@ -1,54 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test validate initial point.""" - -import warnings - -from test.python.algorithms import QiskitAlgorithmsTestCase - -from unittest.mock import Mock - -import numpy as np - -from qiskit.algorithms.utils import validate_initial_point -from qiskit.utils import algorithm_globals - - -class TestValidateInitialPoint(QiskitAlgorithmsTestCase): - """Test the ``validate_initial_point`` utility function.""" - - def setUp(self): - super().setUp() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 0 - self.ansatz = Mock() - self.ansatz.num_parameters = 1 - - def test_with_no_initial_point_or_bounds(self): - """Test with no user-defined initial point and no ansatz bounds.""" - self.ansatz.parameter_bounds = None - initial_point = validate_initial_point(None, self.ansatz) - np.testing.assert_array_almost_equal(initial_point, [1.721111]) - - def test_with_no_initial_point(self): - """Test with no user-defined initial point with ansatz bounds.""" - self.ansatz.parameter_bounds = [(-np.pi / 2, np.pi / 2)] - initial_point = validate_initial_point(None, self.ansatz) - np.testing.assert_array_almost_equal(initial_point, [0.430278]) - - def test_with_mismatched_params(self): - """Test with mistmatched parameters and bounds..""" - self.ansatz.parameter_bounds = None - with self.assertRaises(ValueError): - _ = validate_initial_point([1.0, 2.0], self.ansatz) diff --git a/test/python/basicaer/test_qasm_simulator.py b/test/python/basicaer/test_qasm_simulator.py index 90bb10149943..6deb874d9c82 100644 --- a/test/python/basicaer/test_qasm_simulator.py +++ b/test/python/basicaer/test_qasm_simulator.py @@ -25,6 +25,7 @@ from qiskit.compiler import transpile, assemble from qiskit.providers.basicaer import QasmSimulatorPy from qiskit.test import providers +from qiskit.qasm2 import dumps class StreamHandlerRaiseException(StreamHandler): @@ -306,7 +307,7 @@ def test_teleport(self): "1": data["1 0 0"] + data["1 1 0"] + data["1 0 1"] + data["1 1 1"], } self.log.info("test_teleport: circuit:") - self.log.info(circuit.qasm()) + self.log.info(dumps(circuit)) self.log.info("test_teleport: data %s", data) self.log.info("test_teleport: alice %s", alice) self.log.info("test_teleport: bob %s", bob) diff --git a/test/python/circuit/classical/test_expr_helpers.py b/test/python/circuit/classical/test_expr_helpers.py index f7b420c07144..31b4d7028a8b 100644 --- a/test/python/circuit/classical/test_expr_helpers.py +++ b/test/python/circuit/classical/test_expr_helpers.py @@ -115,3 +115,30 @@ def always_equal(_): # ``True`` instead. self.assertFalse(expr.structurally_equivalent(left, right, not_handled, not_handled)) self.assertTrue(expr.structurally_equivalent(left, right, always_equal, always_equal)) + + +@ddt.ddt +class TestIsLValue(QiskitTestCase): + @ddt.data( + expr.Var.new("a", types.Bool()), + expr.Var.new("b", types.Uint(8)), + expr.Var(Clbit(), types.Bool()), + expr.Var(ClassicalRegister(8, "cr"), types.Uint(8)), + ) + def test_happy_cases(self, lvalue): + self.assertTrue(expr.is_lvalue(lvalue)) + + @ddt.data( + expr.Value(3, types.Uint(2)), + expr.Value(False, types.Bool()), + expr.Cast(expr.Var.new("a", types.Uint(2)), types.Uint(8)), + expr.Unary(expr.Unary.Op.LOGIC_NOT, expr.Var.new("a", types.Bool()), types.Bool()), + expr.Binary( + expr.Binary.Op.LOGIC_AND, + expr.Var.new("a", types.Bool()), + expr.Var.new("b", types.Bool()), + types.Bool(), + ), + ) + def test_bad_cases(self, not_an_lvalue): + self.assertFalse(expr.is_lvalue(not_an_lvalue)) diff --git a/test/python/circuit/classical/test_expr_properties.py b/test/python/circuit/classical/test_expr_properties.py index a6873153c0eb..f8c1277cd0f4 100644 --- a/test/python/circuit/classical/test_expr_properties.py +++ b/test/python/circuit/classical/test_expr_properties.py @@ -14,11 +14,12 @@ import copy import pickle +import uuid import ddt from qiskit.test import QiskitTestCase -from qiskit.circuit import ClassicalRegister +from qiskit.circuit import ClassicalRegister, Clbit from qiskit.circuit.classical import expr, types @@ -56,3 +57,78 @@ def test_expr_can_be_cloned(self, obj): self.assertEqual(obj, copy.copy(obj)) self.assertEqual(obj, copy.deepcopy(obj)) self.assertEqual(obj, pickle.loads(pickle.dumps(obj))) + + def test_var_equality(self): + """Test that various types of :class:`.expr.Var` equality work as expected both in equal and + unequal cases.""" + var_a_bool = expr.Var.new("a", types.Bool()) + self.assertEqual(var_a_bool, var_a_bool) + + # Allocating a new variable should not compare equal, despite the name match. A semantic + # equality checker can choose to key these variables on only their names and types, if it + # knows that that check is valid within the semantic context. + self.assertNotEqual(var_a_bool, expr.Var.new("a", types.Bool())) + + # Manually constructing the same object with the same UUID should cause it compare equal, + # though, for serialisation ease. + self.assertEqual(var_a_bool, expr.Var(var_a_bool.var, types.Bool(), name="a")) + + # This is a badly constructed variable because it's using a different type to refer to the + # same storage location (the UUID) as another variable. It is an IR error to generate this + # sort of thing, but we can't fully be responsible for that and a pass would need to go out + # of its way to do this incorrectly, but we can still ensure that the direct equality check + # would spot the error. + self.assertNotEqual( + var_a_bool, expr.Var(var_a_bool.var, types.Uint(8), name=var_a_bool.name) + ) + + # This is also badly constructed because it uses a different name to refer to the "same" + # storage location. + self.assertNotEqual(var_a_bool, expr.Var(var_a_bool.var, types.Bool(), name="b")) + + # Obviously, two variables of different types and names should compare unequal. + self.assertNotEqual(expr.Var.new("a", types.Bool()), expr.Var.new("b", types.Uint(8))) + # As should two variables of the same name but different storage locations and types. + self.assertNotEqual(expr.Var.new("a", types.Bool()), expr.Var.new("a", types.Uint(8))) + + def test_var_uuid_clone(self): + """Test that :class:`.expr.Var` instances that have an associated UUID and name roundtrip + through pickle and copy operations to produce values that compare equal.""" + var_a_u8 = expr.Var.new("a", types.Uint(8)) + + self.assertEqual(var_a_u8, pickle.loads(pickle.dumps(var_a_u8))) + self.assertEqual(var_a_u8, copy.copy(var_a_u8)) + self.assertEqual(var_a_u8, copy.deepcopy(var_a_u8)) + + def test_var_standalone(self): + """Test that the ``Var.standalone`` property is set correctly.""" + self.assertTrue(expr.Var.new("a", types.Bool()).standalone) + self.assertTrue(expr.Var.new("a", types.Uint(8)).standalone) + self.assertFalse(expr.Var(Clbit(), types.Bool()).standalone) + self.assertFalse(expr.Var(ClassicalRegister(8, "cr"), types.Uint(8)).standalone) + + def test_var_hashable(self): + clbits = [Clbit(), Clbit()] + cregs = [ClassicalRegister(2, "cr1"), ClassicalRegister(2, "cr2")] + + vars_ = [ + expr.Var.new("a", types.Bool()), + expr.Var.new("b", types.Uint(16)), + expr.Var(clbits[0], types.Bool()), + expr.Var(clbits[1], types.Bool()), + expr.Var(cregs[0], types.Uint(2)), + expr.Var(cregs[1], types.Uint(2)), + ] + duplicates = [ + expr.Var(uuid.UUID(bytes=vars_[0].var.bytes), types.Bool(), name=vars_[0].name), + expr.Var(uuid.UUID(bytes=vars_[1].var.bytes), types.Uint(16), name=vars_[1].name), + expr.Var(clbits[0], types.Bool()), + expr.Var(clbits[1], types.Bool()), + expr.Var(cregs[0], types.Uint(2)), + expr.Var(cregs[1], types.Uint(2)), + ] + + # Smoke test. + self.assertEqual(vars_, duplicates) + # Actual test of hashability properties. + self.assertEqual(set(vars_ + duplicates), set(vars_)) diff --git a/test/python/circuit/classical/test_types_ordering.py b/test/python/circuit/classical/test_types_ordering.py index 374e1ecff1b1..58417fb17c03 100644 --- a/test/python/circuit/classical/test_types_ordering.py +++ b/test/python/circuit/classical/test_types_ordering.py @@ -58,3 +58,13 @@ def test_greater(self): self.assertEqual(types.greater(types.Bool(), types.Bool()), types.Bool()) with self.assertRaisesRegex(TypeError, "no ordering"): types.greater(types.Bool(), types.Uint(8)) + + +class TestTypesCastKind(QiskitTestCase): + def test_basic_examples(self): + """This is used extensively throughout the expression construction functions, but since it + is public API, it should have some direct unit tests as well.""" + self.assertIs(types.cast_kind(types.Bool(), types.Bool()), types.CastKind.EQUAL) + self.assertIs(types.cast_kind(types.Uint(8), types.Bool()), types.CastKind.IMPLICIT) + self.assertIs(types.cast_kind(types.Bool(), types.Uint(8)), types.CastKind.LOSSLESS) + self.assertIs(types.cast_kind(types.Uint(16), types.Uint(8)), types.CastKind.DANGEROUS) diff --git a/test/python/circuit/library/test_blueprintcircuit.py b/test/python/circuit/library/test_blueprintcircuit.py index 974e4c1ead32..0ccd5c40ebbc 100644 --- a/test/python/circuit/library/test_blueprintcircuit.py +++ b/test/python/circuit/library/test_blueprintcircuit.py @@ -16,8 +16,15 @@ from ddt import ddt, data from qiskit.test.base import QiskitTestCase -from qiskit.circuit import QuantumRegister, Parameter, QuantumCircuit, Gate, Instruction -from qiskit.circuit.library import BlueprintCircuit +from qiskit.circuit import ( + QuantumRegister, + Parameter, + QuantumCircuit, + Gate, + Instruction, + CircuitInstruction, +) +from qiskit.circuit.library import BlueprintCircuit, XGate class MockBlueprint(BlueprintCircuit): @@ -139,6 +146,34 @@ def test_to_gate_and_instruction(self, method): gate = circuit.to_instruction() self.assertIsInstance(gate, Instruction) + def test_build_before_appends(self): + """Test that both forms of direct append (public and semi-public) function correctly.""" + + class DummyBlueprint(BlueprintCircuit): + """Dummy circuit.""" + + def _check_configuration(self, raise_on_failure=True): + return True + + def _build(self): + super()._build() + self.z(0) + + expected = QuantumCircuit(2) + expected.z(0) + expected.x(0) + + qr = QuantumRegister(2, "q") + mock = DummyBlueprint() + mock.add_register(qr) + mock.append(XGate(), [qr[0]], []) + self.assertEqual(expected, mock) + + mock = DummyBlueprint() + mock.add_register(qr) + mock._append(CircuitInstruction(XGate(), (qr[0],), ())) + self.assertEqual(expected, mock) + if __name__ == "__main__": unittest.main() diff --git a/test/python/circuit/library/test_evolution_gate.py b/test/python/circuit/library/test_evolution_gate.py index adf0e189db32..fb13f5de88ae 100644 --- a/test/python/circuit/library/test_evolution_gate.py +++ b/test/python/circuit/library/test_evolution_gate.py @@ -22,9 +22,13 @@ from qiskit.synthesis import LieTrotter, SuzukiTrotter, MatrixExponential, QDrift from qiskit.converters import circuit_to_dag from qiskit.test import QiskitTestCase -from qiskit.opflow import I, X, Y, Z, PauliSumOp from qiskit.quantum_info import Operator, SparsePauliOp, Pauli, Statevector +X = SparsePauliOp("X") +Y = SparsePauliOp("Y") +Z = SparsePauliOp("Z") +I = SparsePauliOp("I") + @ddt class TestEvolutionGate(QiskitTestCase): @@ -37,8 +41,7 @@ def setUp(self): def test_matrix_decomposition(self): """Test the default decomposition.""" - with self.assertWarns(DeprecationWarning): - op = (X ^ 3) + (Y ^ 3) + (Z ^ 3) + op = (X ^ X ^ X) + (Y ^ Y ^ Y) + (Z ^ Z ^ Z) time = 0.123 matrix = op.to_matrix() @@ -50,8 +53,7 @@ def test_matrix_decomposition(self): def test_lie_trotter(self): """Test constructing the circuit with Lie Trotter decomposition.""" - with self.assertWarns(DeprecationWarning): - op = (X ^ 3) + (Y ^ 3) + (Z ^ 3) + op = (X ^ X ^ X) + (Y ^ Y ^ Y) + (Z ^ Z ^ Z) time = 0.123 reps = 4 evo_gate = PauliEvolutionGate(op, time, synthesis=LieTrotter(reps=reps)) @@ -60,32 +62,30 @@ def test_lie_trotter(self): def test_rzx_order(self): """Test ZX and XZ is mapped onto the correct qubits.""" - with self.assertWarns(DeprecationWarning): - op = (X ^ 3) + (Y ^ 3) + (Z ^ 3) - for op, indices in zip([X ^ Z, Z ^ X], [(0, 1), (1, 0)]): - with self.subTest(op=op, indices=indices): - evo_gate = PauliEvolutionGate(op) - decomposed = evo_gate.definition.decompose() - - # ┌───┐┌───────┐┌───┐ - # q_0: ─────┤ X ├┤ Rz(2) ├┤ X ├───── - # ┌───┐└─┬─┘└───────┘└─┬─┘┌───┐ - # q_1: ┤ H ├──■─────────────■──┤ H ├ - # └───┘ └───┘ - ref = QuantumCircuit(2) - ref.h(indices[1]) - ref.cx(indices[1], indices[0]) - ref.rz(2.0, indices[0]) - ref.cx(indices[1], indices[0]) - ref.h(indices[1]) - - # don't use circuit equality since RZX here decomposes with RZ on the bottom - self.assertTrue(Operator(decomposed).equiv(ref)) + + for op, indices in zip([X ^ Z, Z ^ X], [(0, 1), (1, 0)]): + with self.subTest(op=op, indices=indices): + evo_gate = PauliEvolutionGate(op) + decomposed = evo_gate.definition.decompose() + + # ┌───┐┌───────┐┌───┐ + # q_0: ─────┤ X ├┤ Rz(2) ├┤ X ├───── + # ┌───┐└─┬─┘└───────┘└─┬─┘┌───┐ + # q_1: ┤ H ├──■─────────────■──┤ H ├ + # └───┘ └───┘ + ref = QuantumCircuit(2) + ref.h(indices[1]) + ref.cx(indices[1], indices[0]) + ref.rz(2.0, indices[0]) + ref.cx(indices[1], indices[0]) + ref.h(indices[1]) + + # don't use circuit equality since RZX here decomposes with RZ on the bottom + self.assertTrue(Operator(decomposed).equiv(ref)) def test_suzuki_trotter(self): """Test constructing the circuit with Lie Trotter decomposition.""" - with self.assertWarns(DeprecationWarning): - op = (X ^ 3) + (Y ^ 3) + (Z ^ 3) + op = (X ^ X ^ X) + (Y ^ Y ^ Y) + (Z ^ Z ^ Z) time = 0.123 reps = 4 for order in [2, 4, 6]: @@ -107,8 +107,7 @@ def test_suzuki_trotter(self): def test_suzuki_trotter_manual(self): """Test the evolution circuit of Suzuki Trotter against a manually constructed circuit.""" - with self.assertWarns(DeprecationWarning): - op = X + Y + op = X + Y time = 0.1 reps = 1 evo_gate = PauliEvolutionGate(op, time, synthesis=SuzukiTrotter(order=4, reps=reps)) @@ -156,8 +155,7 @@ def test_qdrift_manual(self, op, time, reps, sampled_ops): def test_qdrift_evolution(self): """Test QDrift on an example.""" - with self.assertWarns(DeprecationWarning): - op = 0.1 * (Z ^ Z) + (X ^ I) + (I ^ X) + 0.2 * (X ^ X) + op = 0.1 * (Z ^ Z) + (X ^ I) + (I ^ X) + 0.2 * (X ^ X) reps = 20 qdrift = PauliEvolutionGate( op, time=0.5 / reps, synthesis=QDrift(reps=reps, seed=self.seed) @@ -171,8 +169,7 @@ def energy(evo): def test_passing_grouped_paulis(self): """Test passing a list of already grouped Paulis.""" - with self.assertWarns(DeprecationWarning): - grouped_ops = [(X ^ Y) + (Y ^ X), (Z ^ I) + (Z ^ Z) + (I ^ Z), (X ^ X)] + grouped_ops = [(X ^ Y) + (Y ^ X), (Z ^ I) + (Z ^ Z) + (I ^ Z), (X ^ X)] evo_gate = PauliEvolutionGate(grouped_ops, time=0.12, synthesis=LieTrotter()) decomposed = evo_gate.definition.decompose() self.assertEqual(decomposed.count_ops()["rz"], 4) @@ -181,8 +178,7 @@ def test_passing_grouped_paulis(self): def test_list_from_grouped_paulis(self): """Test getting a string representation from grouped Paulis.""" - with self.assertWarns(DeprecationWarning): - grouped_ops = [(X ^ Y) + (Y ^ X), (Z ^ I) + (Z ^ Z) + (I ^ Z), (X ^ X)] + grouped_ops = [(X ^ Y) + (Y ^ X), (Z ^ I) + (Z ^ Z) + (I ^ Z), (X ^ X)] evo_gate = PauliEvolutionGate(grouped_ops, time=0.12, synthesis=LieTrotter()) pauli_strings = [] @@ -202,8 +198,7 @@ def test_list_from_grouped_paulis(self): def test_dag_conversion(self): """Test constructing a circuit with evolutions yields a DAG with evolution blocks.""" time = Parameter("t") - with self.assertWarns(DeprecationWarning): - evo = PauliEvolutionGate((Z ^ 2) + (X ^ 2), time=time) + evo = PauliEvolutionGate((Z ^ Z) + (X ^ X), time=time) circuit = QuantumCircuit(2) circuit.h(circuit.qubits) @@ -221,8 +216,7 @@ def test_dag_conversion(self): def test_cnot_chain_options(self, option): """Test selecting different kinds of CNOT chains.""" - with self.assertWarns(DeprecationWarning): - op = Z ^ Z ^ Z + op = Z ^ Z ^ Z synthesis = LieTrotter(reps=1, cx_structure=option) evo = PauliEvolutionGate(op, synthesis=synthesis) @@ -247,9 +241,7 @@ def test_cnot_chain_options(self, option): @data( Pauli("XI"), - X ^ I, # PauliOp SparsePauliOp(Pauli("XI")), - PauliSumOp(SparsePauliOp("XI")), ) def test_different_input_types(self, op): """Test all different supported input types and that they yield the same.""" @@ -266,16 +258,14 @@ def test_different_input_types(self, op): def test_pauliop_coefficients_respected(self): """Test that global ``PauliOp`` coefficients are being taken care of.""" - with self.assertWarns(DeprecationWarning): - evo = PauliEvolutionGate(5 * (Z ^ I), time=1, synthesis=LieTrotter()) + evo = PauliEvolutionGate(5 * (Z ^ I), time=1, synthesis=LieTrotter()) circuit = evo.definition.decompose() rz_angle = circuit.data[0].operation.params[0] self.assertEqual(rz_angle, 10) def test_paulisumop_coefficients_respected(self): """Test that global ``PauliSumOp`` coefficients are being taken care of.""" - with self.assertWarns(DeprecationWarning): - evo = PauliEvolutionGate(5 * (2 * X + 3 * Y - Z), time=1, synthesis=LieTrotter()) + evo = PauliEvolutionGate(5 * (2 * X + 3 * Y - Z), time=1, synthesis=LieTrotter()) circuit = evo.definition.decompose() rz_angles = [ circuit.data[0].operation.params[0], # X @@ -289,8 +279,7 @@ def test_lie_trotter_two_qubit_correct_order(self): Regression test of Qiskit/qiskit-terra#7544. """ - with self.assertWarns(DeprecationWarning): - operator = I ^ Z ^ Z + operator = I ^ Z ^ Z time = 0.5 exact = scipy.linalg.expm(-1j * time * operator.to_matrix()) lie_trotter = PauliEvolutionGate(operator, time, synthesis=LieTrotter()) @@ -310,8 +299,7 @@ def test_paramtrized_op_raises(self): @data(LieTrotter, MatrixExponential) def test_inverse(self, synth_cls): """Test calculating the inverse is correct.""" - with self.assertWarns(DeprecationWarning): - evo = PauliEvolutionGate(X + Y, time=0.12, synthesis=synth_cls()) + evo = PauliEvolutionGate(X + Y, time=0.12, synthesis=synth_cls()) circuit = QuantumCircuit(1) circuit.append(evo, circuit.qubits) @@ -321,8 +309,7 @@ def test_inverse(self, synth_cls): def test_labels_and_name(self): """Test the name and labels are correct.""" - with self.assertWarns(DeprecationWarning): - operators = [X, (X + Y), ((I ^ Z) + (Z ^ I) - 0.2 * (X ^ X))] + operators = [X, (X + Y), ((I ^ Z) + (Z ^ I) - 0.2 * (X ^ X))] # note: the labels do not show coefficients! expected_labels = ["X", "(X + Y)", "(IZ + ZI + XX)"] diff --git a/test/python/circuit/library/test_evolved_op_ansatz.py b/test/python/circuit/library/test_evolved_op_ansatz.py index 06bcc206d8a4..000eb8cfb9df 100644 --- a/test/python/circuit/library/test_evolved_op_ansatz.py +++ b/test/python/circuit/library/test_evolved_op_ansatz.py @@ -12,11 +12,9 @@ """Test the evolved operator ansatz.""" -from ddt import ddt, data import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.opflow import X, Y, Z, I, MatrixEvolution from qiskit.quantum_info import SparsePauliOp, Operator, Pauli from qiskit.circuit.library import EvolvedOperatorAnsatz, HamiltonianGate @@ -24,24 +22,15 @@ from qiskit.test import QiskitTestCase -@ddt class TestEvolvedOperatorAnsatz(QiskitTestCase): """Test the evolved operator ansatz.""" - @data(True, False) - def test_evolved_op_ansatz(self, use_opflow): + def test_evolved_op_ansatz(self): """Test the default evolution.""" num_qubits = 3 - if use_opflow: - with self.assertWarns(DeprecationWarning): - ops = [Z ^ num_qubits, Y ^ num_qubits, X ^ num_qubits] - evo = EvolvedOperatorAnsatz(ops, 2) - parameters = evo.parameters - - else: - ops = [Pauli("Z" * num_qubits), Pauli("Y" * num_qubits), Pauli("X" * num_qubits)] - evo = EvolvedOperatorAnsatz(ops, 2) - parameters = evo.parameters + ops = [Pauli("Z" * num_qubits), Pauli("Y" * num_qubits), Pauli("X" * num_qubits)] + evo = EvolvedOperatorAnsatz(ops, 2) + parameters = evo.parameters reference = QuantumCircuit(num_qubits) strings = ["z" * num_qubits, "y" * num_qubits, "x" * num_qubits] * 2 @@ -50,62 +39,47 @@ def test_evolved_op_ansatz(self, use_opflow): self.assertEqual(evo.decompose().decompose(), reference) - @data(True, False) - def test_custom_evolution(self, use_opflow): + def test_custom_evolution(self): """Test using another evolution than the default (e.g. matrix evolution).""" - if use_opflow: - with self.assertWarns(DeprecationWarning): - op = X ^ I ^ Z - matrix = op.to_matrix() - evolution = MatrixEvolution() - evo = EvolvedOperatorAnsatz(op, evolution=evolution) - parameters = evo.parameters - - else: - op = SparsePauliOp(["ZIX"]) - matrix = np.array(op) - evolution = MatrixExponential() - evo = EvolvedOperatorAnsatz(op, evolution=evolution) - parameters = evo.parameters + op = SparsePauliOp(["ZIX"]) + matrix = np.array(op) + evolution = MatrixExponential() + evo = EvolvedOperatorAnsatz(op, evolution=evolution) + parameters = evo.parameters reference = QuantumCircuit(3) reference.append(HamiltonianGate(matrix, parameters[0]), [0, 1, 2]) - decomposed = evo.decompose() - if not use_opflow: - decomposed = decomposed.decompose() - + decomposed = evo.decompose().decompose() self.assertEqual(decomposed, reference) def test_changing_operators(self): """Test rebuilding after the operators changed.""" - ops = [X, Y, Z] - with self.assertWarns(DeprecationWarning): - evo = EvolvedOperatorAnsatz(ops) - evo.operators = [X, Y] - parameters = evo.parameters + ops = [Pauli("X"), Pauli("Y"), Pauli("Z")] + evo = EvolvedOperatorAnsatz(ops) + evo.operators = [Pauli("X"), Pauli("Y")] + parameters = evo.parameters reference = QuantumCircuit(1) reference.rx(2 * parameters[0], 0) reference.ry(2 * parameters[1], 0) - self.assertEqual(evo.decompose(), reference) + self.assertEqual(evo.decompose(reps=2), reference) def test_invalid_reps(self): """Test setting an invalid number of reps.""" with self.assertRaises(ValueError): - _ = EvolvedOperatorAnsatz(X, reps=-1) + _ = EvolvedOperatorAnsatz(Pauli("X"), reps=-1) def test_insert_barriers(self): """Test using insert_barriers.""" - with self.assertWarns(DeprecationWarning): - evo = EvolvedOperatorAnsatz(Z, reps=4, insert_barriers=True) - ref = QuantumCircuit(1) - for parameter in evo.parameters: - ref.rz(2.0 * parameter, 0) - ref.barrier() - self.assertEqual(evo.decompose(), ref) + evo = EvolvedOperatorAnsatz(Pauli("Z"), reps=4, insert_barriers=True) + ref = QuantumCircuit(1) + for parameter in evo.parameters: + ref.rz(2.0 * parameter, 0) + ref.barrier() + self.assertEqual(evo.decompose(reps=2), ref) def test_empty_build_fails(self): """Test setting no operators to evolve raises the appropriate error.""" diff --git a/test/python/circuit/library/test_functional_pauli_rotations.py b/test/python/circuit/library/test_functional_pauli_rotations.py index 38f6568f117a..294a2b3282f0 100644 --- a/test/python/circuit/library/test_functional_pauli_rotations.py +++ b/test/python/circuit/library/test_functional_pauli_rotations.py @@ -84,7 +84,7 @@ def test_polynomial_rotations_mutability(self): with self.subTest(msg="missing number of state qubits"): with self.assertRaises(AttributeError): # no state qubits set - print(polynomial_rotations.draw()) + _ = str(polynomial_rotations.draw()) with self.subTest(msg="default setup, just setting number of state qubits"): polynomial_rotations.num_state_qubits = 2 @@ -121,7 +121,7 @@ def test_linear_rotations_mutability(self): with self.subTest(msg="missing number of state qubits"): with self.assertRaises(AttributeError): # no state qubits set - print(linear_rotation.draw()) + _ = str(linear_rotation.draw()) with self.subTest(msg="default setup, just setting number of state qubits"): linear_rotation.num_state_qubits = 2 @@ -171,7 +171,7 @@ def test_piecewise_linear_rotations_mutability(self): with self.subTest(msg="missing number of state qubits"): with self.assertRaises(AttributeError): # no state qubits set - print(pw_linear_rotations.draw()) + _ = str(pw_linear_rotations.draw()) with self.subTest(msg="default setup, just setting number of state qubits"): pw_linear_rotations.num_state_qubits = 2 diff --git a/test/python/circuit/library/test_integer_comparator.py b/test/python/circuit/library/test_integer_comparator.py index 2c6459cf38e3..55bd2b9071af 100644 --- a/test/python/circuit/library/test_integer_comparator.py +++ b/test/python/circuit/library/test_integer_comparator.py @@ -71,13 +71,13 @@ def test_mutability(self): with self.subTest(msg="missing num state qubits and value"): with self.assertRaises(AttributeError): - print(comp.draw()) + _ = str(comp.draw()) comp.num_state_qubits = 2 with self.subTest(msg="missing value"): with self.assertRaises(AttributeError): - print(comp.draw()) + _ = str(comp.draw()) comp.value = 0 comp.geq = True diff --git a/test/python/circuit/library/test_nlocal.py b/test/python/circuit/library/test_nlocal.py index f20377368ed4..62e7912606b8 100644 --- a/test/python/circuit/library/test_nlocal.py +++ b/test/python/circuit/library/test_nlocal.py @@ -378,7 +378,7 @@ def test_pairwise_entanglement_raises(self): # pairwise entanglement is only defined if the entangling gate has 2 qubits with self.assertRaises(ValueError): - print(nlocal.draw()) + _ = str(nlocal.draw()) def test_entanglement_by_list(self): """Test setting the entanglement by list. diff --git a/test/python/circuit/library/test_overlap.py b/test/python/circuit/library/test_overlap.py index a9a5f0ae1e2a..fa5e4af05c0d 100644 --- a/test/python/circuit/library/test_overlap.py +++ b/test/python/circuit/library/test_overlap.py @@ -82,6 +82,15 @@ def test_partial_parameterized_inputs2(self): overlap = UnitaryOverlap(unitary1, unitary2) self.assertEqual(overlap.num_parameters, unitary1.num_parameters) + def test_barrier(self): + """Test that barriers on input circuits are well handled""" + unitary1 = EfficientSU2(1, reps=0) + unitary1.barrier() + unitary2 = EfficientSU2(1, reps=1) + unitary2.barrier() + overlap = UnitaryOverlap(unitary1, unitary2) + self.assertEqual(overlap.num_parameters, unitary1.num_parameters + unitary2.num_parameters) + def test_measurements(self): """Test that exception is thrown for measurements""" unitary1 = EfficientSU2(2) diff --git a/test/python/circuit/library/test_permutation.py b/test/python/circuit/library/test_permutation.py index bf4da582b6ad..aacc5425d74b 100644 --- a/test/python/circuit/library/test_permutation.py +++ b/test/python/circuit/library/test_permutation.py @@ -25,6 +25,7 @@ from qiskit.circuit.library import Permutation, PermutationGate from qiskit.quantum_info import Operator from qiskit.qpy import dump, load +from qiskit.qasm2 import dumps class TestPermutationLibrary(QiskitTestCase): @@ -160,9 +161,9 @@ def test_qasm(self): "gate permutation__2_4_3_0_1_ q0,q1,q2,q3,q4 { swap q2,q3; swap q1,q4; swap q0,q3; }\n" "qreg q0[5];\n" "permutation__2_4_3_0_1_ q0[0],q0[1],q0[2],q0[3],q0[4];\n" - "h q0[0];\n" + "h q0[0];" ) - self.assertEqual(expected_qasm, circuit.qasm()) + self.assertEqual(expected_qasm, dumps(circuit)) def test_qpy(self): """Test qpy for circuits with permutations.""" @@ -170,7 +171,6 @@ def test_qpy(self): circuit.cx(0, 1) circuit.append(PermutationGate([1, 2, 0]), [2, 4, 5]) circuit.h(4) - print(circuit) qpy_file = io.BytesIO() dump(circuit, qpy_file) diff --git a/test/python/circuit/library/test_piecewise_chebyshev.py b/test/python/circuit/library/test_piecewise_chebyshev.py index 4b8cb6054e1d..c721fe47ee67 100644 --- a/test/python/circuit/library/test_piecewise_chebyshev.py +++ b/test/python/circuit/library/test_piecewise_chebyshev.py @@ -103,7 +103,7 @@ def f_x_1(x): with self.subTest(msg="missing number of state qubits"): with self.assertRaises(AttributeError): # no state qubits set - print(pw_approximation.draw()) + _ = str(pw_approximation.draw()) with self.subTest(msg="default setup, just setting number of state qubits"): pw_approximation.num_state_qubits = 2 diff --git a/test/python/circuit/library/test_qaoa_ansatz.py b/test/python/circuit/library/test_qaoa_ansatz.py index dfbf25da69d4..fcd062152bfa 100644 --- a/test/python/circuit/library/test_qaoa_ansatz.py +++ b/test/python/circuit/library/test_qaoa_ansatz.py @@ -18,7 +18,6 @@ from qiskit.circuit import QuantumCircuit, Parameter from qiskit.circuit.library import HGate, RXGate, YGate, RYGate, RZGate from qiskit.circuit.library.n_local.qaoa_ansatz import QAOAAnsatz -from qiskit.opflow import I from qiskit.quantum_info import Pauli, SparsePauliOp from qiskit.test import QiskitTestCase @@ -29,8 +28,7 @@ class TestQAOAAnsatz(QiskitTestCase): def test_default_qaoa(self): """Test construction of the default circuit.""" - # To be changed once QAOAAnsatz drops support for opflow - circuit = QAOAAnsatz(I, 1) + circuit = QAOAAnsatz(Pauli("I"), 1) parameters = circuit.parameters circuit = circuit.decompose() diff --git a/test/python/circuit/library/test_weighted_adder.py b/test/python/circuit/library/test_weighted_adder.py index 7bfe614e0bd6..db33156aa0a2 100644 --- a/test/python/circuit/library/test_weighted_adder.py +++ b/test/python/circuit/library/test_weighted_adder.py @@ -67,7 +67,7 @@ def test_mutability(self): with self.subTest(msg="missing number of state qubits"): with self.assertRaises(AttributeError): - print(adder.draw()) + _ = str(adder.draw()) with self.subTest(msg="default weights"): adder.num_state_qubits = 3 @@ -81,7 +81,7 @@ def test_mutability(self): with self.subTest(msg="mismatching number of state qubits and weights"): with self.assertRaises(ValueError): adder.weights = [0, 1, 2, 3] - print(adder.draw()) + _ = str(adder.draw()) with self.subTest(msg="change all attributes"): adder.num_state_qubits = 4 diff --git a/test/python/circuit/test_circuit_data.py b/test/python/circuit/test_circuit_data.py index d90d953902f7..45f2c1639c40 100644 --- a/test/python/circuit/test_circuit_data.py +++ b/test/python/circuit/test_circuit_data.py @@ -11,14 +11,178 @@ # that they have been altered from the originals. """Test operations on circuit.data.""" - -from qiskit.circuit import QuantumCircuit, QuantumRegister, Parameter, CircuitInstruction, Operation +import ddt +from qiskit._accelerate.quantum_circuit import CircuitData + +from qiskit.circuit import ( + ClassicalRegister, + QuantumCircuit, + QuantumRegister, + Parameter, + CircuitInstruction, + Operation, + Qubit, +) from qiskit.circuit.library import HGate, XGate, CXGate, RXGate from qiskit.test import QiskitTestCase from qiskit.circuit.exceptions import CircuitError +@ddt.ddt +class TestQuantumCircuitData(QiskitTestCase): + """CircuitData (Rust) operation tests.""" + + @ddt.data( + slice(0, 5, 1), # Get everything. + slice(-1, -6, -1), # Get everything, reversed. + slice(0, 4, 1), # Get subslice. + slice(0, 5, 2), # Get every other. + slice(-1, -6, -2), # Get every other, reversed. + slice(2, 2, 1), # Get nothing. + slice(2, 3, 1), # Get at index 2. + slice(4, 10, 1), # Get index 4 to end, using excessive upper bound. + slice(5, 0, -2), # Get every other, reversed, excluding index 0. + slice(-10, -5, 1), # Get nothing. + slice(0, 10, 1), # Get everything. + ) + def test_getitem_slice(self, sli): + """Test that __getitem__ with slice is equivalent to that of list.""" + qr = QuantumRegister(5) + data_list = [ + CircuitInstruction(XGate(), [qr[0]], []), + CircuitInstruction(XGate(), [qr[1]], []), + CircuitInstruction(XGate(), [qr[2]], []), + CircuitInstruction(XGate(), [qr[3]], []), + CircuitInstruction(XGate(), [qr[4]], []), + ] + data = CircuitData(qubits=list(qr), data=data_list) + self.assertEqual(data[sli], data_list[sli]) + + @ddt.data( + slice(0, 5, 1), # Delete everything. + slice(-1, -6, -1), # Delete everything, reversed. + slice(0, 4, 1), # Delete subslice. + slice(0, 5, 2), # Delete every other. + slice(-1, -6, -2), # Delete every other, reversed. + slice(2, 2, 1), # Delete nothing. + slice(2, 3, 1), # Delete at index 2. + slice(4, 10, 1), # Delete index 4 to end, excessive upper bound. + slice(5, 0, -2), # Delete every other, reversed, excluding index 0. + slice(-10, -5, 1), # Delete nothing. + slice(0, 10, 1), # Delete everything, excessive upper bound. + ) + def test_delitem_slice(self, sli): + """Test that __delitem__ with slice is equivalent to that of list.""" + qr = QuantumRegister(5) + data_list = [ + CircuitInstruction(XGate(), [qr[0]], []), + CircuitInstruction(XGate(), [qr[1]], []), + CircuitInstruction(XGate(), [qr[2]], []), + CircuitInstruction(XGate(), [qr[3]], []), + CircuitInstruction(XGate(), [qr[4]], []), + ] + data = CircuitData(qubits=list(qr), data=data_list) + + del data_list[sli] + del data[sli] + if data_list[sli] != data[sli]: + print(f"data_list: {data_list}") + print(f"data: {list(data)}") + + self.assertEqual(data[sli], data_list[sli]) + + @ddt.data( + (slice(0, 5, 1), 5), # Replace entire slice. + (slice(-1, -6, -1), 5), # Replace entire slice, reversed. + (slice(0, 4, 1), 4), # Replace subslice. + (slice(0, 4, 1), 10), # Replace subslice with bigger sequence. + (slice(0, 5, 2), 3), # Replace every other. + (slice(-1, -6, -2), 3), # Replace every other, reversed. + (slice(2, 2, 1), 1), # Insert at index 2. + (slice(2, 3, 1), 1), # Replace at index 2. + (slice(2, 3, 1), 10), # Replace at index 2 with bigger sequence. + (slice(4, 10, 1), 2), # Replace index 4 with bigger sequence, excessive upper bound. + (slice(5, 10, 1), 10), # Append sequence. + (slice(4, 0, -1), 4), # Replace subslice at end, reversed. + ) + @ddt.unpack + def test_setitem_slice(self, sli, value_length): + """Test that __setitem__ with slice is equivalent to that of list.""" + reg_size = 20 + assert value_length <= reg_size + qr = QuantumRegister(reg_size) + default_bit = Qubit() + data_list = [ + CircuitInstruction(XGate(), [default_bit], []), + CircuitInstruction(XGate(), [default_bit], []), + CircuitInstruction(XGate(), [default_bit], []), + CircuitInstruction(XGate(), [default_bit], []), + CircuitInstruction(XGate(), [default_bit], []), + ] + data = CircuitData(qubits=list(qr) + [default_bit], data=data_list) + + value = [CircuitInstruction(XGate(), [qr[i]]) for i in range(value_length)] + data_list[sli] = value + data[sli] = value + self.assertEqual(data, data_list) + + @ddt.data( + (slice(0, 5, 2), 2), # Replace smaller, with gaps. + (slice(0, 5, 2), 4), # Replace larger, with gaps. + (slice(4, 0, -1), 10), # Replace larger, reversed. + (slice(-1, -6, -1), 6), # Replace larger, reversed, negative notation. + (slice(4, 3, -1), 10), # Replace at index 4 with bigger sequence, reversed. + ) + @ddt.unpack + def test_setitem_slice_negative(self, sli, value_length): + """Test that __setitem__ with slice is equivalent to that of list.""" + reg_size = 20 + assert value_length <= reg_size + qr = QuantumRegister(reg_size) + default_bit = Qubit() + data_list = [ + CircuitInstruction(XGate(), [default_bit], []), + CircuitInstruction(XGate(), [default_bit], []), + CircuitInstruction(XGate(), [default_bit], []), + CircuitInstruction(XGate(), [default_bit], []), + CircuitInstruction(XGate(), [default_bit], []), + ] + data = CircuitData(qubits=list(qr) + [default_bit], data=data_list) + + value = [CircuitInstruction(XGate(), [qr[i]]) for i in range(value_length)] + with self.assertRaises(ValueError): + data_list[sli] = value + with self.assertRaises(ValueError): + data[sli] = value + self.assertEqual(data, data_list) + + def test_unregistered_bit_error_new(self): + """Test using foreign bits is not allowed.""" + qr = QuantumRegister(1) + cr = ClassicalRegister(1) + with self.assertRaisesRegex(KeyError, "not been added to this circuit"): + CircuitData(qr, cr, [CircuitInstruction(XGate(), [Qubit()], [])]) + + def test_unregistered_bit_error_append(self): + """Test using foreign bits is not allowed.""" + qr = QuantumRegister(1) + cr = ClassicalRegister(1) + data = CircuitData(qr, cr) + with self.assertRaisesRegex(KeyError, "not been added to this circuit"): + qr_foreign = QuantumRegister(1) + data.append(CircuitInstruction(XGate(), [qr_foreign[0]], [])) + + def test_unregistered_bit_error_set(self): + """Test using foreign bits is not allowed.""" + qr = QuantumRegister(1) + cr = ClassicalRegister(1) + data = CircuitData(qr, cr, [CircuitInstruction(XGate(), [qr[0]], [])]) + with self.assertRaisesRegex(KeyError, "not been added to this circuit"): + qr_foreign = QuantumRegister(1) + data[0] = CircuitInstruction(XGate(), [qr_foreign[0]], []) + + class TestQuantumCircuitInstructionData(QiskitTestCase): """QuantumCircuit.data operation tests.""" diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index f8ba8c260df9..5695a298f782 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -55,7 +55,6 @@ from qiskit.quantum_info.random import random_unitary from qiskit.circuit.controlledgate import ControlledGate from qiskit.utils import optionals -from qiskit.exceptions import MissingOptionalLibraryError @ddt.ddt @@ -1685,6 +1684,18 @@ def test_qpy_deprecation(self): # pylint: disable=no-name-in-module, unused-import, redefined-outer-name, reimported from qiskit.circuit.qpy_serialization import dump, load + @ddt.data(0, "01", [1, 0, 0, 0]) + def test_valid_circuit_with_initialize_instruction(self, param): + """Tests that circuit that has initialize instruction can be saved and correctly retrieved""" + qc = QuantumCircuit(2) + qc.initialize(param, qc.qubits) + with io.BytesIO() as fptr: + dump(qc, fptr) + fptr.seek(0) + new_circuit = load(fptr)[0] + self.assertEqual(qc, new_circuit) + self.assertDeprecatedBitProperties(qc, new_circuit) + class TestSymengineLoadFromQPY(QiskitTestCase): """Test use of symengine in qpy set of methods.""" @@ -1735,22 +1746,3 @@ def test_symengine_full_path(self): new_circ = load(qpy_file)[0] self.assertEqual(self.qc, new_circ) self.assertDeprecatedBitProperties(self.qc, new_circ) - - @unittest.skipIf(not optionals.HAS_SYMENGINE, "Install symengine to run this test.") - def test_dump_no_symengine(self): - """Test dump fails if symengine is not installed and use_symengine==True.""" - qpy_file = io.BytesIO() - with optionals.HAS_SYMENGINE.disable_locally(): - with self.assertRaises(MissingOptionalLibraryError): - dump(self.qc, qpy_file, use_symengine=True) - - @unittest.skipIf(not optionals.HAS_SYMENGINE, "Install symengine to run this test.") - def test_load_no_symengine(self): - """Test that load fails if symengine is not installed and the - file was created with use_symengine==True.""" - qpy_file = io.BytesIO() - dump(self.qc, qpy_file, use_symengine=True) - qpy_file.seek(0) - with optionals.HAS_SYMENGINE.disable_locally(): - with self.assertRaises(MissingOptionalLibraryError): - _ = load(qpy_file)[0] diff --git a/test/python/circuit/test_circuit_operations.py b/test/python/circuit/test_circuit_operations.py index 11770d896bc6..af94a1a290e1 100644 --- a/test/python/circuit/test_circuit_operations.py +++ b/test/python/circuit/test_circuit_operations.py @@ -19,6 +19,7 @@ from qiskit import BasicAer, ClassicalRegister, QuantumCircuit, QuantumRegister, execute from qiskit.circuit import Gate, Instruction, Measure, Parameter, Barrier from qiskit.circuit.bit import Bit +from qiskit.circuit.classical import expr, types from qiskit.circuit.classicalregister import Clbit from qiskit.circuit.exceptions import CircuitError from qiskit.circuit.controlflow import IfElseOp @@ -391,6 +392,77 @@ def test_copy_empty_like_circuit(self): copied = qc.copy_empty_like("copy") self.assertEqual(copied.name, "copy") + def test_copy_variables(self): + """Test that a full copy of circuits including variables copies them across.""" + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Uint(8)) + c = expr.Var.new("c", types.Bool()) + d = expr.Var.new("d", types.Uint(8)) + + qc = QuantumCircuit(inputs=[a], declarations=[(c, expr.lift(False))]) + copied = qc.copy() + self.assertEqual({a}, set(copied.iter_input_vars())) + self.assertEqual({c}, set(copied.iter_declared_vars())) + self.assertEqual( + [instruction.operation for instruction in qc], + [instruction.operation for instruction in copied.data], + ) + + # Check that the original circuit is not mutated. + copied.add_input(b) + copied.add_var(d, 0xFF) + self.assertEqual({a, b}, set(copied.iter_input_vars())) + self.assertEqual({c, d}, set(copied.iter_declared_vars())) + self.assertEqual({a}, set(qc.iter_input_vars())) + self.assertEqual({c}, set(qc.iter_declared_vars())) + + qc = QuantumCircuit(captures=[b], declarations=[(a, expr.lift(False)), (c, a)]) + copied = qc.copy() + self.assertEqual({b}, set(copied.iter_captured_vars())) + self.assertEqual({a, c}, set(copied.iter_declared_vars())) + self.assertEqual( + [instruction.operation for instruction in qc], + [instruction.operation for instruction in copied.data], + ) + + # Check that the original circuit is not mutated. + copied.add_capture(d) + self.assertEqual({b, d}, set(copied.iter_captured_vars())) + self.assertEqual({b}, set(qc.iter_captured_vars())) + + def test_copy_empty_variables(self): + """Test that an empty copy of circuits including variables copies them across, but does not + initialise them.""" + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Uint(8)) + c = expr.Var.new("c", types.Bool()) + d = expr.Var.new("d", types.Uint(8)) + + qc = QuantumCircuit(inputs=[a], declarations=[(c, expr.lift(False))]) + copied = qc.copy_empty_like() + self.assertEqual({a}, set(copied.iter_input_vars())) + self.assertEqual({c}, set(copied.iter_declared_vars())) + self.assertEqual([], list(copied.data)) + + # Check that the original circuit is not mutated. + copied.add_input(b) + copied.add_var(d, 0xFF) + self.assertEqual({a, b}, set(copied.iter_input_vars())) + self.assertEqual({c, d}, set(copied.iter_declared_vars())) + self.assertEqual({a}, set(qc.iter_input_vars())) + self.assertEqual({c}, set(qc.iter_declared_vars())) + + qc = QuantumCircuit(captures=[b], declarations=[(a, expr.lift(False)), (c, a)]) + copied = qc.copy_empty_like() + self.assertEqual({b}, set(copied.iter_captured_vars())) + self.assertEqual({a, c}, set(copied.iter_declared_vars())) + self.assertEqual([], list(copied.data)) + + # Check that the original circuit is not mutated. + copied.add_capture(d) + self.assertEqual({b, d}, set(copied.iter_captured_vars())) + self.assertEqual({b}, set(qc.iter_captured_vars())) + def test_circuit_copy_rejects_invalid_types(self): """Test copy method rejects argument with type other than 'string' and 'None' type.""" qc = QuantumCircuit(1, 1) @@ -420,6 +492,27 @@ def test_clear_circuit(self): self.assertEqual(len(qc.data), 0) self.assertEqual(len(qc._parameter_table), 0) + def test_barrier(self): + """Test multiple argument forms of barrier.""" + qr1, qr2 = QuantumRegister(3, "qr1"), QuantumRegister(4, "qr2") + qc = QuantumCircuit(qr1, qr2) + qc.barrier() # All qubits. + qc.barrier(0, 1) + qc.barrier([4, 2]) + qc.barrier(qr1) + qc.barrier(slice(3, 5)) + qc.barrier({1, 4, 2}, range(5, 7)) + + expected = QuantumCircuit(qr1, qr2) + expected.append(Barrier(expected.num_qubits), expected.qubits.copy(), []) + expected.append(Barrier(2), [expected.qubits[0], expected.qubits[1]], []) + expected.append(Barrier(2), [expected.qubits[2], expected.qubits[4]], []) + expected.append(Barrier(3), expected.qubits[0:3], []) + expected.append(Barrier(2), [expected.qubits[3], expected.qubits[4]], []) + expected.append(Barrier(5), [expected.qubits[x] for x in [1, 2, 4, 5, 6]], []) + + self.assertEqual(qc, expected) + def test_measure_active(self): """Test measure_active Applies measurements only to non-idle qubits. Creates a ClassicalRegister of size equal to @@ -1287,11 +1380,11 @@ def test_pop_previous_instruction_removes_parameters(self): x, y = Parameter("x"), Parameter("y") test = QuantumCircuit(1, 1) test.rx(y, 0) - last_instructions = test.u(x, y, 0, 0) + last_instructions = list(test.u(x, y, 0, 0)) self.assertEqual({x, y}, set(test.parameters)) instruction = test._pop_previous_instruction_in_scope() - self.assertEqual(list(last_instructions), [instruction]) + self.assertEqual(last_instructions, [instruction]) self.assertEqual({y}, set(test.parameters)) def test_decompose_gate_type(self): diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index 5b27f968d021..7102df18bf68 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -20,7 +20,8 @@ from qiskit.test import QiskitTestCase from qiskit.circuit import Parameter, Qubit, Clbit, Gate from qiskit.circuit.library import C3SXGate, CCZGate, CSGate, CSdgGate, PermutationGate -from qiskit.qasm.exceptions import QasmError +from qiskit.qasm2.exceptions import QASM2Error as QasmError +from qiskit.qasm2 import dumps # Regex pattern to match valid OpenQASM identifiers VALID_QASM2_IDENTIFIER = re.compile("[a-z][a-zA-Z_0-9]*") @@ -69,8 +70,8 @@ def test_circuit_qasm(self): barrier qr1[0],qr2[0],qr2[1]; measure qr1[0] -> cr[0]; measure qr2[0] -> cr[1]; -measure qr2[1] -> cr[2];\n""" - self.assertEqual(qc.qasm(), expected_qasm) +measure qr2[1] -> cr[2];""" + self.assertEqual(dumps(qc), expected_qasm) def test_circuit_qasm_with_composite_circuit(self): """Test circuit qasm() method when a composite circuit instruction @@ -103,8 +104,8 @@ def test_circuit_qasm_with_composite_circuit(self): barrier qr[0],qr[1]; composite_circ qr[0],qr[1]; measure qr[0] -> cr[0]; -measure qr[1] -> cr[1];\n""" - self.assertEqual(qc.qasm(), expected_qasm) +measure qr[1] -> cr[1];""" + self.assertEqual(dumps(qc), expected_qasm) def test_circuit_qasm_with_multiple_same_composite_circuits(self): """Test circuit qasm() method when a composite circuit is added @@ -139,8 +140,8 @@ def test_circuit_qasm_with_multiple_same_composite_circuits(self): composite_circ qr[0],qr[1]; composite_circ qr[0],qr[1]; measure qr[0] -> cr[0]; -measure qr[1] -> cr[1];\n""" - self.assertEqual(qc.qasm(), expected_qasm) +measure qr[1] -> cr[1];""" + self.assertEqual(dumps(qc), expected_qasm) def test_circuit_qasm_with_multiple_composite_circuits_with_same_name(self): """Test circuit qasm() method when multiple composite circuit instructions @@ -175,10 +176,10 @@ def test_circuit_qasm_with_multiple_composite_circuits_with_same_name(self): qreg qr[1]; my_gate qr[0]; my_gate_{1} qr[0]; -my_gate_{0} qr[0];\n""".format( +my_gate_{0} qr[0];""".format( my_gate_inst3_id, my_gate_inst2_id ) - self.assertEqual(circuit.qasm(), expected_qasm) + self.assertEqual(dumps(circuit), expected_qasm) def test_circuit_qasm_with_composite_circuit_with_children_composite_circuit(self): """Test circuit qasm() method when composite circuits with children @@ -205,16 +206,16 @@ def test_circuit_qasm_with_composite_circuit_with_children_composite_circuit(sel gate parent_circ q0,q1,q2 { child_circ q0,q1; h q2; } gate grandparent_circ q0,q1,q2,q3 { parent_circ q0,q1,q2; x q3; } qreg q[4]; -grandparent_circ q[0],q[1],q[2],q[3];\n""" +grandparent_circ q[0],q[1],q[2],q[3];""" - self.assertEqual(qc.qasm(), expected_qasm) + self.assertEqual(dumps(qc), expected_qasm) def test_circuit_qasm_pi(self): """Test circuit qasm() method with pi params.""" circuit = QuantumCircuit(2) circuit.cz(0, 1) circuit.u(2 * pi, 3 * pi, -5 * pi, 0) - qasm_str = circuit.qasm() + qasm_str = dumps(circuit) circuit2 = QuantumCircuit.from_qasm_str(qasm_str) self.assertEqual(circuit, circuit2) @@ -227,10 +228,10 @@ def test_circuit_qasm_with_composite_circuit_with_one_param(self): gate nG0(param0) q0 { h q0; } qreg q[3]; creg c[3]; -nG0(pi) q[0];\n""" +nG0(pi) q[0];""" qc = QuantumCircuit.from_qasm_str(original_str) - self.assertEqual(original_str, qc.qasm()) + self.assertEqual(original_str, dumps(qc)) def test_circuit_qasm_with_composite_circuit_with_many_params_and_qubits(self): """Test circuit qasm() method when a composite circuit instruction @@ -243,10 +244,10 @@ def test_circuit_qasm_with_composite_circuit_with_many_params_and_qubits(self): qreg r[3]; creg c[3]; creg d[3]; -nG0(pi,pi/2) q[0],r[0];\n""" +nG0(pi,pi/2) q[0],r[0];""" qc = QuantumCircuit.from_qasm_str(original_str) - self.assertEqual(original_str, qc.qasm()) + self.assertEqual(original_str, dumps(qc)) def test_c3sxgate_roundtrips(self): """Test that C3SXGate correctly round trips. @@ -256,12 +257,11 @@ def test_c3sxgate_roundtrips(self): resolution issues.""" qc = QuantumCircuit(4) qc.append(C3SXGate(), qc.qubits, []) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; qreg q[4]; -c3sqrtx q[0],q[1],q[2],q[3]; -""" +c3sqrtx q[0],q[1],q[2],q[3];""" self.assertEqual(qasm, expected) parsed = QuantumCircuit.from_qasm_str(qasm) self.assertIsInstance(parsed.data[0].operation, C3SXGate) @@ -275,39 +275,36 @@ def test_cczgate_qasm(self): """Test that CCZ dumps definition as a non-qelib1 gate.""" qc = QuantumCircuit(3) qc.append(CCZGate(), qc.qubits, []) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; gate ccz q0,q1,q2 { h q2; ccx q0,q1,q2; h q2; } qreg q[3]; -ccz q[0],q[1],q[2]; -""" +ccz q[0],q[1],q[2];""" self.assertEqual(qasm, expected) def test_csgate_qasm(self): """Test that CS dumps definition as a non-qelib1 gate.""" qc = QuantumCircuit(2) qc.append(CSGate(), qc.qubits, []) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; gate cs q0,q1 { p(pi/4) q0; cx q0,q1; p(-pi/4) q1; cx q0,q1; p(pi/4) q1; } qreg q[2]; -cs q[0],q[1]; -""" +cs q[0],q[1];""" self.assertEqual(qasm, expected) def test_csdggate_qasm(self): """Test that CSdg dumps definition as a non-qelib1 gate.""" qc = QuantumCircuit(2) qc.append(CSdgGate(), qc.qubits, []) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; gate csdg q0,q1 { p(-pi/4) q0; cx q0,q1; p(pi/4) q1; cx q0,q1; p(-pi/4) q1; } qreg q[2]; -csdg q[0],q[1]; -""" +csdg q[0],q[1];""" self.assertEqual(qasm, expected) def test_rzxgate_qasm(self): @@ -315,14 +312,13 @@ def test_rzxgate_qasm(self): qc = QuantumCircuit(2) qc.rzx(0, 0, 1) qc.rzx(pi / 2, 1, 0) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; gate rzx(param0) q0,q1 { h q1; cx q0,q1; rz(param0) q1; cx q0,q1; h q1; } qreg q[2]; rzx(0) q[0],q[1]; -rzx(pi/2) q[1],q[0]; -""" +rzx(pi/2) q[1],q[0];""" self.assertEqual(qasm, expected) def test_ecrgate_qasm(self): @@ -330,28 +326,26 @@ def test_ecrgate_qasm(self): qc = QuantumCircuit(2) qc.ecr(0, 1) qc.ecr(1, 0) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; gate rzx(param0) q0,q1 { h q1; cx q0,q1; rz(param0) q1; cx q0,q1; h q1; } gate ecr q0,q1 { rzx(pi/4) q0,q1; x q0; rzx(-pi/4) q0,q1; } qreg q[2]; ecr q[0],q[1]; -ecr q[1],q[0]; -""" +ecr q[1],q[0];""" self.assertEqual(qasm, expected) def test_unitary_qasm(self): """Test that UnitaryGate can be dumped to OQ2 correctly.""" qc = QuantumCircuit(1) qc.unitary([[1, 0], [0, 1]], 0) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; gate unitary q0 { u(0,0,0) q0; } qreg q[1]; -unitary q[0]; -""" +unitary q[0];""" self.assertEqual(qasm, expected) def test_multiple_unitary_qasm(self): @@ -363,7 +357,7 @@ def test_multiple_unitary_qasm(self): qc.unitary([[1, 0], [0, 1]], 0) qc.unitary([[0, 1], [1, 0]], 1) qc.append(custom.to_gate(), [0], []) - qasm = qc.qasm() + qasm = dumps(qc) expected = re.compile( r"""OPENQASM 2.0; include "qelib1.inc"; @@ -374,8 +368,7 @@ def test_multiple_unitary_qasm(self): qreg q\[2\]; unitary q\[0\]; (?P=u1) q\[1\]; -custom q\[0\]; -""", +custom q\[0\];""", re.MULTILINE, ) self.assertRegex(qasm, expected) @@ -386,7 +379,7 @@ def test_unbound_circuit_raises(self): theta = Parameter("θ") qc.rz(theta, 0) with self.assertRaises(QasmError): - qc.qasm() + dumps(qc) def test_gate_qasm_with_ctrl_state(self): """Test gate qasm() with controlled gate that has ctrl_state setting.""" @@ -394,7 +387,7 @@ def test_gate_qasm_with_ctrl_state(self): qc = QuantumCircuit(2) qc.ch(0, 1, ctrl_state=0) - qasm_str = qc.qasm() + qasm_str = dumps(qc) self.assertEqual(Operator(qc), Operator(QuantumCircuit.from_qasm_str(qasm_str))) def test_circuit_qasm_with_mcx_gate(self): @@ -410,8 +403,8 @@ def test_circuit_qasm_with_mcx_gate(self): include "qelib1.inc"; gate mcx q0,q1,q2,q3 { h q3; p(pi/8) q0; p(pi/8) q1; p(pi/8) q2; p(pi/8) q3; cx q0,q1; p(-pi/8) q1; cx q0,q1; cx q1,q2; p(-pi/8) q2; cx q0,q2; p(pi/8) q2; cx q1,q2; p(-pi/8) q2; cx q0,q2; cx q2,q3; p(-pi/8) q3; cx q1,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q0,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q1,q3; p(pi/8) q3; cx q2,q3; p(-pi/8) q3; cx q0,q3; h q3; } qreg q[4]; -mcx q[0],q[1],q[2],q[3];\n""" - self.assertEqual(qc.qasm(), expected_qasm) +mcx q[0],q[1],q[2],q[3];""" + self.assertEqual(dumps(qc), expected_qasm) def test_circuit_qasm_with_mcx_gate_variants(self): """Test circuit qasm() method with MCXGrayCode, MCXRecursive, MCXVChain""" @@ -435,9 +428,9 @@ def test_circuit_qasm_with_mcx_gate_variants(self): qreg q[9]; mcx_gray q[0],q[1],q[2],q[3],q[4],q[5]; mcx_recursive q[0],q[1],q[2],q[3],q[4],q[5],q[6]; -mcx_vchain q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7],q[8];\n""" +mcx_vchain q[0],q[1],q[2],q[3],q[4],q[5],q[6],q[7],q[8];""" - self.assertEqual(qc.qasm(), expected_qasm) + self.assertEqual(dumps(qc), expected_qasm) def test_circuit_qasm_with_registerless_bits(self): """Test that registerless bits do not have naming collisions in their registers.""" @@ -446,7 +439,7 @@ def test_circuit_qasm_with_registerless_bits(self): # Match a 'qreg identifier[3];'-like QASM register declaration. register_regex = re.compile(r"\s*[cq]reg\s+(\w+)\s*\[\d+\]\s*", re.M) qasm_register_names = set() - for statement in qc.qasm().split(";"): + for statement in dumps(qc).split(";"): match = register_regex.match(statement) if match: qasm_register_names.add(match.group(1)) @@ -462,7 +455,7 @@ def test_circuit_qasm_with_registerless_bits(self): for generated_name in generated_names: qc.add_register(QuantumRegister(1, name=generated_name)) qasm_register_names = set() - for statement in qc.qasm().split(";"): + for statement in dumps(qc).split(";"): match = register_regex.match(statement) if match: qasm_register_names.add(match.group(1)) @@ -498,9 +491,9 @@ def test_circuit_qasm_with_repeated_instruction_names(self): h q[0]; x q[1]; custom q[0]; -custom_{id(gate2)} q[1],q[0];\n""" +custom_{id(gate2)} q[1],q[0];""" # Check qasm() produced the correct string - self.assertEqual(expected_qasm, qc.qasm()) + self.assertEqual(expected_qasm, dumps(qc)) # Check instruction names were not changed by qasm() names = ["h", "x", "custom", "custom"] for idx, instruction in enumerate(qc._data): @@ -539,12 +532,11 @@ def test_circuit_qasm_with_invalid_identifiers(self): "qreg q[2];", "gate_A___ q[0];", "invalid_name_ q[1],q[0];", - "", ] ) # Check qasm() produces the correct string - self.assertEqual(expected_qasm, qc.qasm()) + self.assertEqual(expected_qasm, dumps(qc)) # Check instruction names were not changed by qasm() names = ["A[$]", "invalid[name]"] @@ -568,7 +560,7 @@ def test_circuit_qasm_with_duplicate_invalid_identifiers(self): # Check qasm is correctly produced names = set() - for match in re.findall(r"gate (\S+)", base.qasm()): + for match in re.findall(r"gate (\S+)", dumps(base)): self.assertTrue(VALID_QASM2_IDENTIFIER.fullmatch(match)) names.add(match) self.assertEqual(len(names), 2) @@ -584,15 +576,14 @@ def test_circuit_qasm_escapes_register_names(self): qc = QuantumCircuit(QuantumRegister(2, "?invalid"), QuantumRegister(2, "!invalid")) qc.cx(0, 1) qc.cx(2, 3) - qasm = qc.qasm() + qasm = dumps(qc) match = re.fullmatch( rf"""OPENQASM 2.0; include "qelib1.inc"; qreg ({VALID_QASM2_IDENTIFIER.pattern})\[2\]; qreg ({VALID_QASM2_IDENTIFIER.pattern})\[2\]; cx \1\[0\],\1\[1\]; -cx \2\[0\],\2\[1\]; -""", +cx \2\[0\],\2\[1\];""", qasm, ) self.assertTrue(match) @@ -604,14 +595,13 @@ def test_circuit_qasm_escapes_reserved(self): gate = Gate("gate", 1, []) gate.definition = QuantumCircuit(1) qc.append(gate, [qc.qubits[0]]) - qasm = qc.qasm() + qasm = dumps(qc) match = re.fullmatch( rf"""OPENQASM 2.0; include "qelib1.inc"; gate ({VALID_QASM2_IDENTIFIER.pattern}) q0 {{ }} qreg ({VALID_QASM2_IDENTIFIER.pattern})\[1\]; -\1 \2\[0\]; -""", +\1 \2\[0\];""", qasm, ) self.assertTrue(match) @@ -632,8 +622,8 @@ def test_circuit_qasm_with_double_precision_rotation_angle(self): qreg q[1]; p(0.123456789) q[0]; p(9.869604401089358) q[0]; -p(51.26548245743669) q[0];\n""" - self.assertEqual(qc.qasm(), expected_qasm) +p(51.26548245743669) q[0];""" + self.assertEqual(dumps(qc), expected_qasm) def test_circuit_qasm_with_rotation_angles_close_to_pi(self): """Test that qasm() properly rounds values closer than 1e-12 to pi.""" @@ -645,8 +635,8 @@ def test_circuit_qasm_with_rotation_angles_close_to_pi(self): include "qelib1.inc"; qreg q[1]; p(3.141592653599793) q[0]; -p(pi) q[0];\n""" - self.assertEqual(qc.qasm(), expected_qasm) +p(pi) q[0];""" + self.assertEqual(dumps(qc), expected_qasm) def test_circuit_raises_on_single_bit_condition(self): """OpenQASM 2 can't represent single-bit conditions, so test that a suitable error is @@ -655,7 +645,7 @@ def test_circuit_raises_on_single_bit_condition(self): qc.x(0).c_if(0, True) with self.assertRaisesRegex(QasmError, "OpenQASM 2 can only condition on registers"): - qc.qasm() + dumps(qc) def test_circuit_raises_invalid_custom_gate_no_qubits(self): """OpenQASM 2 exporter of custom gates with no qubits. @@ -665,7 +655,7 @@ def test_circuit_raises_invalid_custom_gate_no_qubits(self): legit_circuit.append(empty_circuit) with self.assertRaisesRegex(QasmError, "acts on zero qubits"): - legit_circuit.qasm() + dumps(legit_circuit) def test_circuit_raises_invalid_custom_gate_clbits(self): """OpenQASM 2 exporter of custom instruction. @@ -679,7 +669,7 @@ def test_circuit_raises_invalid_custom_gate_clbits(self): qc.append(custom_instruction, [0, 1], [0, 1]) with self.assertRaisesRegex(QasmError, "acts on 2 classical bits"): - qc.qasm() + dumps(qc) def test_circuit_qasm_with_permutations(self): """Test circuit qasm() method with Permutation gates.""" @@ -691,8 +681,8 @@ def test_circuit_qasm_with_permutations(self): include "qelib1.inc"; gate permutation__2_1_0_ q0,q1,q2 { swap q0,q2; } qreg q[4]; -permutation__2_1_0_ q[0],q[1],q[2];\n""" - self.assertEqual(qc.qasm(), expected_qasm) +permutation__2_1_0_ q[0],q[1],q[2];""" + self.assertEqual(dumps(qc), expected_qasm) def test_multiple_permutation(self): """Test that multiple PermutationGates can be added to a circuit.""" @@ -704,7 +694,7 @@ def test_multiple_permutation(self): qc.append(PermutationGate([2, 1, 0]), [0, 1, 2], []) qc.append(PermutationGate([1, 2, 0]), [0, 1, 2], []) qc.append(custom.to_gate(), [1, 3, 2], []) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; gate permutation__2_1_0_ q0,q1,q2 { swap q0,q2; } @@ -714,8 +704,7 @@ def test_multiple_permutation(self): qreg q[4]; permutation__2_1_0_ q[0],q[1],q[2]; permutation__1_2_0_ q[0],q[1],q[2]; -custom q[1],q[3],q[2]; -""" +custom q[1],q[3],q[2];""" self.assertEqual(qasm, expected) def test_circuit_qasm_with_reset(self): @@ -727,8 +716,8 @@ def test_circuit_qasm_with_reset(self): include "qelib1.inc"; qreg q[2]; reset q[0]; -reset q[1];\n""" - self.assertEqual(qc.qasm(), expected_qasm) +reset q[1];""" + self.assertEqual(dumps(qc), expected_qasm) def test_nested_gate_naming_clashes(self): """Test that gates that have naming clashes but only appear in the body of another gate @@ -755,7 +744,7 @@ def _define(self): qc = QuantumCircuit(1) qc.append(Outer(1.0), [0], []) qc.append(Outer(2.0), [0], []) - qasm = qc.qasm() + qasm = dumps(qc) expected = re.compile( r"""OPENQASM 2\.0; @@ -766,8 +755,7 @@ def _define(self): gate (?Pouter_[0-9]*)\(param0\) q0 { (?P=inner1)\(2\.0\) q0; } qreg q\[1\]; outer\(1\.0\) q\[0\]; -(?P=outer1)\(2\.0\) q\[0\]; -""", +(?P=outer1)\(2\.0\) q\[0\];""", re.MULTILINE, ) self.assertRegex(qasm, expected) @@ -782,7 +770,7 @@ def test_opaque_output(self): qc.append(Gate("my_a", 1, []), [1]) qc.append(Gate("my_b", 2, [1.0]), [1, 0]) qc.append(custom.to_gate(), [0], []) - qasm = qc.qasm() + qasm = dumps(qc) expected = """OPENQASM 2.0; include "qelib1.inc"; opaque my_a q0; @@ -793,8 +781,7 @@ def test_opaque_output(self): my_a q[0]; my_a q[1]; my_b(1.0) q[1],q[0]; -custom q[0]; -""" +custom q[0];""" self.assertEqual(qasm, expected) def test_sequencial_inner_gates_with_same_name(self): @@ -824,10 +811,9 @@ def test_sequencial_inner_gates_with_same_name(self): a_{gate_a_id} q[0],q[1],q[2]; z q[0]; z q[1]; -z q[2]; -""" +z q[2];""" - self.assertEqual(qc.qasm(), expected_output) + self.assertEqual(dumps(qc), expected_output) def test_empty_barrier(self): """Test that a blank barrier statement in _Qiskit_ acts over all qubits, while an explicitly @@ -842,9 +828,8 @@ def test_empty_barrier(self): include "qelib1.inc"; qreg qr1[2]; qreg qr2[3]; -barrier qr1[0],qr1[1],qr2[0],qr2[1],qr2[2]; -""" - self.assertEqual(qc.qasm(), expected) +barrier qr1[0],qr1[1],qr2[0],qr2[1],qr2[2];""" + self.assertEqual(dumps(qc), expected) def test_small_angle_valid(self): """Test that small angles do not get converted to invalid OQ2 floating-point values.""" @@ -856,9 +841,8 @@ def test_small_angle_valid(self): OPENQASM 2.0; include "qelib1.inc"; qreg q[1]; -rx(1.e-06) q[0]; -""" - self.assertEqual(qc.qasm(), expected) +rx(1.e-06) q[0];""" + self.assertEqual(dumps(qc), expected) if __name__ == "__main__": diff --git a/test/python/circuit/test_circuit_registers.py b/test/python/circuit/test_circuit_registers.py index d6a1a63cb469..ca0ce6d0bed3 100644 --- a/test/python/circuit/test_circuit_registers.py +++ b/test/python/circuit/test_circuit_registers.py @@ -26,6 +26,7 @@ ) from qiskit.circuit.exceptions import CircuitError from qiskit.test import QiskitTestCase +from qiskit.qasm2 import dumps class TestCircuitRegisters(QiskitTestCase): @@ -284,7 +285,7 @@ def test_apply_barrier_to_slice(self): num_qubits = 2 qc = QuantumCircuit(qr, cr) qc.barrier(qr[0:num_qubits]) - self.log.info(qc.qasm()) + self.log.info(dumps(qc)) self.assertEqual(len(qc.data), 1) self.assertEqual(qc.data[0].operation.name, "barrier") self.assertEqual(len(qc.data[0].qubits), num_qubits) diff --git a/test/python/circuit/test_circuit_vars.py b/test/python/circuit/test_circuit_vars.py new file mode 100644 index 000000000000..c09e4717af3f --- /dev/null +++ b/test/python/circuit/test_circuit_vars.py @@ -0,0 +1,393 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +from qiskit.test import QiskitTestCase +from qiskit.circuit import QuantumCircuit, CircuitError, Clbit, ClassicalRegister +from qiskit.circuit.classical import expr, types + + +class TestCircuitVars(QiskitTestCase): + """Tests for variable-manipulation routines on circuits. More specific functionality is likely + tested in the suites of the specific methods.""" + + def test_initialise_inputs(self): + vars_ = [expr.Var.new("a", types.Bool()), expr.Var.new("b", types.Uint(16))] + qc = QuantumCircuit(inputs=vars_) + self.assertEqual(set(vars_), set(qc.iter_vars())) + self.assertEqual(qc.num_vars, len(vars_)) + self.assertEqual(qc.num_input_vars, len(vars_)) + self.assertEqual(qc.num_captured_vars, 0) + self.assertEqual(qc.num_declared_vars, 0) + + def test_initialise_captures(self): + vars_ = [expr.Var.new("a", types.Bool()), expr.Var.new("b", types.Uint(16))] + qc = QuantumCircuit(captures=vars_) + self.assertEqual(set(vars_), set(qc.iter_vars())) + self.assertEqual(qc.num_vars, len(vars_)) + self.assertEqual(qc.num_input_vars, 0) + self.assertEqual(qc.num_captured_vars, len(vars_)) + self.assertEqual(qc.num_declared_vars, 0) + + def test_initialise_declarations_iterable(self): + vars_ = [ + (expr.Var.new("a", types.Bool()), expr.lift(True)), + (expr.Var.new("b", types.Uint(16)), expr.lift(0xFFFF)), + ] + qc = QuantumCircuit(declarations=vars_) + + self.assertEqual({var for var, _initialiser in vars_}, set(qc.iter_vars())) + self.assertEqual(qc.num_vars, len(vars_)) + self.assertEqual(qc.num_input_vars, 0) + self.assertEqual(qc.num_captured_vars, 0) + self.assertEqual(qc.num_declared_vars, len(vars_)) + operations = [ + (instruction.operation.name, instruction.operation.lvalue, instruction.operation.rvalue) + for instruction in qc.data + ] + self.assertEqual(operations, [("store", lvalue, rvalue) for lvalue, rvalue in vars_]) + + def test_initialise_declarations_mapping(self): + # Dictionary iteration order is guaranteed to be insertion order. + vars_ = { + expr.Var.new("a", types.Bool()): expr.lift(True), + expr.Var.new("b", types.Uint(16)): expr.lift(0xFFFF), + } + qc = QuantumCircuit(declarations=vars_) + + self.assertEqual(set(vars_), set(qc.iter_vars())) + operations = [ + (instruction.operation.name, instruction.operation.lvalue, instruction.operation.rvalue) + for instruction in qc.data + ] + self.assertEqual( + operations, [("store", lvalue, rvalue) for lvalue, rvalue in vars_.items()] + ) + + def test_initialise_declarations_dependencies(self): + """Test that the cirucit initialiser can take in declarations with dependencies between + them, provided they're specified in a suitable order.""" + a = expr.Var.new("a", types.Bool()) + vars_ = [ + (a, expr.lift(True)), + (expr.Var.new("b", types.Bool()), a), + ] + qc = QuantumCircuit(declarations=vars_) + + self.assertEqual({var for var, _initialiser in vars_}, set(qc.iter_vars())) + operations = [ + (instruction.operation.name, instruction.operation.lvalue, instruction.operation.rvalue) + for instruction in qc.data + ] + self.assertEqual(operations, [("store", lvalue, rvalue) for lvalue, rvalue in vars_]) + + def test_initialise_inputs_declarations(self): + a = expr.Var.new("a", types.Uint(16)) + b = expr.Var.new("b", types.Uint(16)) + b_init = expr.bit_and(a, 0xFFFF) + qc = QuantumCircuit(inputs=[a], declarations={b: b_init}) + + self.assertEqual({a}, set(qc.iter_input_vars())) + self.assertEqual({b}, set(qc.iter_declared_vars())) + self.assertEqual({a, b}, set(qc.iter_vars())) + self.assertEqual(qc.num_vars, 2) + self.assertEqual(qc.num_input_vars, 1) + self.assertEqual(qc.num_captured_vars, 0) + self.assertEqual(qc.num_declared_vars, 1) + operations = [ + (instruction.operation.name, instruction.operation.lvalue, instruction.operation.rvalue) + for instruction in qc.data + ] + self.assertEqual(operations, [("store", b, b_init)]) + + def test_initialise_captures_declarations(self): + a = expr.Var.new("a", types.Uint(16)) + b = expr.Var.new("b", types.Uint(16)) + b_init = expr.bit_and(a, 0xFFFF) + qc = QuantumCircuit(captures=[a], declarations={b: b_init}) + + self.assertEqual({a}, set(qc.iter_captured_vars())) + self.assertEqual({b}, set(qc.iter_declared_vars())) + self.assertEqual({a, b}, set(qc.iter_vars())) + self.assertEqual(qc.num_vars, 2) + self.assertEqual(qc.num_input_vars, 0) + self.assertEqual(qc.num_captured_vars, 1) + self.assertEqual(qc.num_declared_vars, 1) + operations = [ + (instruction.operation.name, instruction.operation.lvalue, instruction.operation.rvalue) + for instruction in qc.data + ] + self.assertEqual(operations, [("store", b, b_init)]) + + def test_add_uninitialized_var(self): + a = expr.Var.new("a", types.Bool()) + qc = QuantumCircuit() + qc.add_uninitialized_var(a) + self.assertEqual({a}, set(qc.iter_vars())) + self.assertEqual([], list(qc.data)) + + def test_add_var_returns_good_var(self): + qc = QuantumCircuit() + a = qc.add_var("a", expr.lift(True)) + self.assertEqual(a.name, "a") + self.assertEqual(a.type, types.Bool()) + + b = qc.add_var("b", expr.Value(0xFF, types.Uint(8))) + self.assertEqual(b.name, "b") + self.assertEqual(b.type, types.Uint(8)) + + def test_add_var_returns_input(self): + """Test that the `Var` returned by `add_var` is the same as the input if `Var`.""" + a = expr.Var.new("a", types.Bool()) + qc = QuantumCircuit() + a_other = qc.add_var(a, expr.lift(True)) + self.assertIs(a, a_other) + + def test_add_input_returns_good_var(self): + qc = QuantumCircuit() + a = qc.add_input("a", types.Bool()) + self.assertEqual(a.name, "a") + self.assertEqual(a.type, types.Bool()) + + b = qc.add_input("b", types.Uint(8)) + self.assertEqual(b.name, "b") + self.assertEqual(b.type, types.Uint(8)) + + def test_add_input_returns_input(self): + """Test that the `Var` returned by `add_input` is the same as the input if `Var`.""" + a = expr.Var.new("a", types.Bool()) + qc = QuantumCircuit() + a_other = qc.add_input(a) + self.assertIs(a, a_other) + + def test_cannot_have_both_inputs_and_captures(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + + with self.assertRaisesRegex(CircuitError, "circuits with input.*cannot be closures"): + QuantumCircuit(inputs=[a], captures=[b]) + + qc = QuantumCircuit(inputs=[a]) + with self.assertRaisesRegex(CircuitError, "circuits with input.*cannot be closures"): + qc.add_capture(b) + + qc = QuantumCircuit(captures=[a]) + with self.assertRaisesRegex(CircuitError, "circuits to be enclosed.*cannot have input"): + qc.add_input(b) + + def test_cannot_add_cyclic_declaration(self): + a = expr.Var.new("a", types.Bool()) + with self.assertRaisesRegex(CircuitError, "not present in this circuit"): + QuantumCircuit(declarations=[(a, a)]) + + qc = QuantumCircuit() + with self.assertRaisesRegex(CircuitError, "not present in this circuit"): + qc.add_var(a, a) + + def test_initialise_inputs_equal_to_add_input(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Uint(16)) + + qc_init = QuantumCircuit(inputs=[a, b]) + qc_manual = QuantumCircuit() + qc_manual.add_input(a) + qc_manual.add_input(b) + self.assertEqual(list(qc_init.iter_vars()), list(qc_manual.iter_vars())) + + qc_manual = QuantumCircuit() + a = qc_manual.add_input("a", types.Bool()) + b = qc_manual.add_input("b", types.Uint(16)) + qc_init = QuantumCircuit(inputs=[a, b]) + self.assertEqual(list(qc_init.iter_vars()), list(qc_manual.iter_vars())) + + def test_initialise_captures_equal_to_add_capture(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Uint(16)) + + qc_init = QuantumCircuit(captures=[a, b]) + qc_manual = QuantumCircuit() + qc_manual.add_capture(a) + qc_manual.add_capture(b) + self.assertEqual(list(qc_init.iter_vars()), list(qc_manual.iter_vars())) + + def test_initialise_declarations_equal_to_add_var(self): + a = expr.Var.new("a", types.Bool()) + a_init = expr.lift(False) + b = expr.Var.new("b", types.Uint(16)) + b_init = expr.lift(0xFFFF) + + qc_init = QuantumCircuit(declarations=[(a, a_init), (b, b_init)]) + qc_manual = QuantumCircuit() + qc_manual.add_var(a, a_init) + qc_manual.add_var(b, b_init) + self.assertEqual(list(qc_init.iter_vars()), list(qc_manual.iter_vars())) + self.assertEqual(qc_init.data, qc_manual.data) + + qc_manual = QuantumCircuit() + a = qc_manual.add_var("a", a_init) + b = qc_manual.add_var("b", b_init) + qc_init = QuantumCircuit(declarations=[(a, a_init), (b, b_init)]) + self.assertEqual(list(qc_init.iter_vars()), list(qc_manual.iter_vars())) + self.assertEqual(qc_init.data, qc_manual.data) + + def test_cannot_shadow_vars(self): + """Test that exact duplicate ``Var`` nodes within different combinations of the inputs are + detected and rejected.""" + a = expr.Var.new("a", types.Bool()) + a_init = expr.lift(True) + with self.assertRaisesRegex(CircuitError, "already present"): + QuantumCircuit(inputs=[a, a]) + with self.assertRaisesRegex(CircuitError, "already present"): + QuantumCircuit(captures=[a, a]) + with self.assertRaisesRegex(CircuitError, "already present"): + QuantumCircuit(declarations=[(a, a_init), (a, a_init)]) + with self.assertRaisesRegex(CircuitError, "already present"): + QuantumCircuit(inputs=[a], declarations=[(a, a_init)]) + with self.assertRaisesRegex(CircuitError, "already present"): + QuantumCircuit(captures=[a], declarations=[(a, a_init)]) + + def test_cannot_shadow_names(self): + """Test that exact duplicate ``Var`` nodes within different combinations of the inputs are + detected and rejected.""" + a_bool1 = expr.Var.new("a", types.Bool()) + a_bool2 = expr.Var.new("a", types.Bool()) + a_uint = expr.Var.new("a", types.Uint(16)) + a_bool_init = expr.lift(True) + a_uint_init = expr.lift(0xFFFF) + + tests = [ + ((a_bool1, a_bool_init), (a_bool2, a_bool_init)), + ((a_bool1, a_bool_init), (a_uint, a_uint_init)), + ] + for (left, left_init), (right, right_init) in tests: + with self.assertRaisesRegex(CircuitError, "its name shadows"): + QuantumCircuit(inputs=(left, right)) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + QuantumCircuit(captures=(left, right)) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + QuantumCircuit(declarations=[(left, left_init), (right, right_init)]) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + QuantumCircuit(inputs=[left], declarations=[(right, right_init)]) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + QuantumCircuit(captures=[left], declarations=[(right, right_init)]) + + qc = QuantumCircuit(inputs=[left]) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + qc.add_input(right) + qc = QuantumCircuit(inputs=[left]) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + qc.add_var(right, right_init) + + qc = QuantumCircuit(captures=[left]) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + qc.add_capture(right) + qc = QuantumCircuit(captures=[left]) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + qc.add_var(right, right_init) + + qc = QuantumCircuit(inputs=[left]) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + qc.add_var(right, right_init) + + qc = QuantumCircuit() + qc.add_var("a", expr.lift(True)) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + qc.add_var("a", expr.lift(True)) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + qc.add_var("a", expr.lift(0xFF)) + + def test_cannot_add_vars_wrapping_clbits(self): + a = expr.Var(Clbit(), types.Bool()) + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + QuantumCircuit(inputs=[a]) + qc = QuantumCircuit() + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + qc.add_input(a) + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + QuantumCircuit(captures=[a]) + qc = QuantumCircuit() + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + qc.add_capture(a) + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + QuantumCircuit(declarations=[(a, expr.lift(True))]) + qc = QuantumCircuit() + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + qc.add_var(a, expr.lift(True)) + + def test_cannot_add_vars_wrapping_cregs(self): + a = expr.Var(ClassicalRegister(8, "cr"), types.Uint(8)) + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + QuantumCircuit(inputs=[a]) + qc = QuantumCircuit() + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + qc.add_input(a) + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + QuantumCircuit(captures=[a]) + qc = QuantumCircuit() + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + qc.add_capture(a) + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + QuantumCircuit(declarations=[(a, expr.lift(0xFF))]) + qc = QuantumCircuit() + with self.assertRaisesRegex(CircuitError, "cannot add variables that wrap"): + qc.add_var(a, expr.lift(0xFF)) + + def test_get_var_success(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Uint(8)) + + qc = QuantumCircuit(inputs=[a], declarations={b: expr.Value(0xFF, types.Uint(8))}) + self.assertIs(qc.get_var("a"), a) + self.assertIs(qc.get_var("b"), b) + + qc = QuantumCircuit(captures=[a, b]) + self.assertIs(qc.get_var("a"), a) + self.assertIs(qc.get_var("b"), b) + + qc = QuantumCircuit(declarations={a: expr.lift(True), b: expr.Value(0xFF, types.Uint(8))}) + self.assertIs(qc.get_var("a"), a) + self.assertIs(qc.get_var("b"), b) + + def test_get_var_missing(self): + qc = QuantumCircuit() + with self.assertRaises(KeyError): + qc.get_var("a") + + a = expr.Var.new("a", types.Bool()) + qc.add_input(a) + with self.assertRaises(KeyError): + qc.get_var("b") + + def test_get_var_default(self): + qc = QuantumCircuit() + self.assertIs(qc.get_var("a", None), None) + + missing = "default" + a = expr.Var.new("a", types.Bool()) + qc.add_input(a) + self.assertIs(qc.get_var("b", missing), missing) + self.assertIs(qc.get_var("b", a), a) + + def test_has_var(self): + a = expr.Var.new("a", types.Bool()) + self.assertFalse(QuantumCircuit().has_var("a")) + self.assertTrue(QuantumCircuit(inputs=[a]).has_var("a")) + self.assertTrue(QuantumCircuit(captures=[a]).has_var("a")) + self.assertTrue(QuantumCircuit(declarations={a: expr.lift(True)}).has_var("a")) + self.assertTrue(QuantumCircuit(inputs=[a]).has_var(a)) + self.assertTrue(QuantumCircuit(captures=[a]).has_var(a)) + self.assertTrue(QuantumCircuit(declarations={a: expr.lift(True)}).has_var(a)) + + # When giving an `Var`, the match must be exact, not just the name. + self.assertFalse(QuantumCircuit(inputs=[a]).has_var(expr.Var.new("a", types.Uint(8)))) + self.assertFalse(QuantumCircuit(inputs=[a]).has_var(expr.Var.new("a", types.Bool()))) diff --git a/test/python/circuit/test_control_flow.py b/test/python/circuit/test_control_flow.py index e827b679a4a2..ecb5de96f793 100644 --- a/test/python/circuit/test_control_flow.py +++ b/test/python/circuit/test_control_flow.py @@ -510,6 +510,49 @@ def test_switch_rejects_cases_after_default(self): with self.assertRaisesRegex(CircuitError, "cases after the default are unreachable"): SwitchCaseOp(creg, [(CASE_DEFAULT, case1), (1, case2)]) + def test_if_else_rejects_input_vars(self): + """Bodies must not contain input variables.""" + cond = (Clbit(), False) + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + bad_body = QuantumCircuit(inputs=[a]) + good_body = QuantumCircuit(captures=[a], declarations=[(b, expr.lift(False))]) + + with self.assertRaisesRegex(CircuitError, "cannot contain input variables"): + IfElseOp(cond, bad_body, None) + with self.assertRaisesRegex(CircuitError, "cannot contain input variables"): + IfElseOp(cond, bad_body, good_body) + with self.assertRaisesRegex(CircuitError, "cannot contain input variables"): + IfElseOp(cond, good_body, bad_body) + + def test_while_rejects_input_vars(self): + """Bodies must not contain input variables.""" + cond = (Clbit(), False) + a = expr.Var.new("a", types.Bool()) + bad_body = QuantumCircuit(inputs=[a]) + with self.assertRaisesRegex(CircuitError, "cannot contain input variables"): + WhileLoopOp(cond, bad_body) + + def test_for_rejects_input_vars(self): + """Bodies must not contain input variables.""" + a = expr.Var.new("a", types.Bool()) + bad_body = QuantumCircuit(inputs=[a]) + with self.assertRaisesRegex(CircuitError, "cannot contain input variables"): + ForLoopOp(range(3), None, bad_body) + + def test_switch_rejects_input_vars(self): + """Bodies must not contain input variables.""" + target = ClassicalRegister(3, "cr") + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + bad_body = QuantumCircuit(inputs=[a]) + good_body = QuantumCircuit(captures=[a], declarations=[(b, expr.lift(False))]) + + with self.assertRaisesRegex(CircuitError, "cannot contain input variables"): + SwitchCaseOp(target, [(0, bad_body)]) + with self.assertRaisesRegex(CircuitError, "cannot contain input variables"): + SwitchCaseOp(target, [(0, good_body), (1, bad_body)]) + @ddt class TestAddingControlFlowOperations(QiskitTestCase): @@ -874,3 +917,148 @@ def test_nested_parameters_can_be_assigned(self): ) self.assertEqual(assigned, expected) + + def test_can_add_op_with_captures_of_inputs(self): + """Test circuit methods can capture input variables.""" + outer = QuantumCircuit(1, 1) + a = outer.add_input("a", types.Bool()) + + inner = QuantumCircuit(1, 1, captures=[a]) + + outer.if_test((outer.clbits[0], False), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "if_else") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.if_else((outer.clbits[0], False), inner.copy(), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "if_else") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + self.assertEqual(set(added.blocks[1].iter_captured_vars()), {a}) + + outer.while_loop((outer.clbits[0], False), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "while_loop") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.for_loop(range(3), None, inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "for_loop") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.switch(outer.clbits[0], [(False, inner.copy()), (True, inner.copy())], [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "switch_case") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + self.assertEqual(set(added.blocks[1].iter_captured_vars()), {a}) + + def test_can_add_op_with_captures_of_captures(self): + """Test circuit methods can capture captured variables.""" + outer = QuantumCircuit(1, 1) + a = expr.Var.new("a", types.Bool()) + outer.add_capture(a) + + inner = QuantumCircuit(1, 1, captures=[a]) + + outer.if_test((outer.clbits[0], False), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "if_else") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.if_else((outer.clbits[0], False), inner.copy(), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "if_else") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + self.assertEqual(set(added.blocks[1].iter_captured_vars()), {a}) + + outer.while_loop((outer.clbits[0], False), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "while_loop") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.for_loop(range(3), None, inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "for_loop") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.switch(outer.clbits[0], [(False, inner.copy()), (True, inner.copy())], [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "switch_case") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + self.assertEqual(set(added.blocks[1].iter_captured_vars()), {a}) + + def test_can_add_op_with_captures_of_locals(self): + """Test circuit methods can capture declared variables.""" + outer = QuantumCircuit(1, 1) + a = outer.add_var("a", expr.lift(True)) + + inner = QuantumCircuit(1, 1, captures=[a]) + + outer.if_test((outer.clbits[0], False), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "if_else") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.if_else((outer.clbits[0], False), inner.copy(), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "if_else") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + self.assertEqual(set(added.blocks[1].iter_captured_vars()), {a}) + + outer.while_loop((outer.clbits[0], False), inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "while_loop") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.for_loop(range(3), None, inner.copy(), [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "for_loop") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + + outer.switch(outer.clbits[0], [(False, inner.copy()), (True, inner.copy())], [0], [0]) + added = outer.data[-1].operation + self.assertEqual(added.name, "switch_case") + self.assertEqual(set(added.blocks[0].iter_captured_vars()), {a}) + self.assertEqual(set(added.blocks[1].iter_captured_vars()), {a}) + + def test_cannot_capture_unknown_variables_methods(self): + """Control-flow operations should not be able to capture variables that don't exist in the + outer circuit.""" + outer = QuantumCircuit(1, 1) + + a = expr.Var.new("a", types.Bool()) + inner = QuantumCircuit(1, 1, captures=[a]) + + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.if_test((outer.clbits[0], False), inner.copy(), [0], [0]) + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.if_else((outer.clbits[0], False), inner.copy(), inner.copy(), [0], [0]) + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.while_loop((outer.clbits[0], False), inner.copy(), [0], [0]) + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.for_loop(range(3), None, inner.copy(), [0], [0]) + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.switch(outer.clbits[0], [(False, inner.copy()), (True, inner.copy())], [0], [0]) + + def test_cannot_capture_unknown_variables_append(self): + """Control-flow operations should not be able to capture variables that don't exist in the + outer circuit.""" + outer = QuantumCircuit(1, 1) + + a = expr.Var.new("a", types.Bool()) + inner = QuantumCircuit(1, 1, captures=[a]) + + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.append(IfElseOp((outer.clbits[0], False), inner.copy(), None), [0], [0]) + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.append(IfElseOp((outer.clbits[0], False), inner.copy(), inner.copy()), [0], [0]) + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.append(WhileLoopOp((outer.clbits[0], False), inner.copy()), [0], [0]) + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.append(ForLoopOp(range(3), None, inner.copy()), [0], [0]) + with self.assertRaisesRegex(CircuitError, "not in this circuit"): + outer.append( + SwitchCaseOp(outer.clbits[0], [(False, inner.copy()), (True, inner.copy())]), + [0], + [0], + ) diff --git a/test/python/circuit/test_control_flow_builders.py b/test/python/circuit/test_control_flow_builders.py index aa4974f4cdec..ce8088fcd26c 100644 --- a/test/python/circuit/test_control_flow_builders.py +++ b/test/python/circuit/test_control_flow_builders.py @@ -10,6 +10,8 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. +# pylint: disable=missing-function-docstring + """Test operations on the builder interfaces for control flow in dynamic QuantumCircuits.""" import copy @@ -25,10 +27,10 @@ QuantumCircuit, QuantumRegister, Qubit, + Store, ) from qiskit.circuit.classical import expr, types from qiskit.circuit.controlflow import ForLoopOp, IfElseOp, WhileLoopOp, SwitchCaseOp, CASE_DEFAULT -from qiskit.circuit.controlflow.builder import ControlFlowBuilderBlock from qiskit.circuit.controlflow.if_else import IfElsePlaceholder from qiskit.circuit.exceptions import CircuitError from qiskit.test import QiskitTestCase @@ -2998,6 +3000,251 @@ def test_global_phase_of_blocks(self): [i * math.pi / 7 for i in range(1, 7)], ) + def test_can_capture_input(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + base = QuantumCircuit(inputs=[a, b]) + with base.for_loop(range(3)): + base.store(a, expr.lift(True)) + self.assertEqual(set(base.data[-1].operation.blocks[0].iter_captured_vars()), {a}) + + def test_can_capture_declared(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + base = QuantumCircuit(declarations=[(a, expr.lift(False)), (b, expr.lift(True))]) + with base.if_test(expr.lift(False)): + base.store(a, expr.lift(True)) + self.assertEqual(set(base.data[-1].operation.blocks[0].iter_captured_vars()), {a}) + + def test_can_capture_capture(self): + # It's a bit wild to be manually building an outer circuit that's intended to be a subblock, + # but be using the control-flow builder interface internally, but eh, it should work. + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + base = QuantumCircuit(captures=[a, b]) + with base.while_loop(expr.lift(False)): + base.store(a, expr.lift(True)) + self.assertEqual(set(base.data[-1].operation.blocks[0].iter_captured_vars()), {a}) + + def test_can_capture_from_nested(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + c = expr.Var.new("c", types.Bool()) + base = QuantumCircuit(inputs=[a, b]) + with base.switch(expr.lift(False)) as case, case(case.DEFAULT): + base.add_var(c, expr.lift(False)) + with base.if_test(expr.lift(False)): + base.store(a, c) + outer_block = base.data[-1].operation.blocks[0] + inner_block = outer_block.data[-1].operation.blocks[0] + self.assertEqual(set(inner_block.iter_captured_vars()), {a, c}) + + # The containing block should have captured it as well, despite not using it explicitly. + self.assertEqual(set(outer_block.iter_captured_vars()), {a}) + self.assertEqual(set(outer_block.iter_declared_vars()), {c}) + + def test_can_manually_capture(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + base = QuantumCircuit(inputs=[a, b]) + with base.while_loop(expr.lift(False)): + # Why do this? Who knows, but it clearly has a well-defined meaning. + base.add_capture(a) + self.assertEqual(set(base.data[-1].operation.blocks[0].iter_captured_vars()), {a}) + + def test_later_blocks_do_not_inherit_captures(self): + """Neither 'if' nor 'switch' should have later blocks inherit the captures from the earlier + blocks, and the earlier blocks shouldn't be affected by later ones.""" + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + c = expr.Var.new("c", types.Bool()) + + base = QuantumCircuit(inputs=[a, b, c]) + with base.if_test(expr.lift(False)) as else_: + base.store(a, expr.lift(False)) + with else_: + base.store(b, expr.lift(False)) + blocks = base.data[-1].operation.blocks + self.assertEqual(set(blocks[0].iter_captured_vars()), {a}) + self.assertEqual(set(blocks[1].iter_captured_vars()), {b}) + + base = QuantumCircuit(inputs=[a, b, c]) + with base.switch(expr.lift(False)) as case: + with case(0): + base.store(a, expr.lift(False)) + with case(case.DEFAULT): + base.store(b, expr.lift(False)) + blocks = base.data[-1].operation.blocks + self.assertEqual(set(blocks[0].iter_captured_vars()), {a}) + self.assertEqual(set(blocks[1].iter_captured_vars()), {b}) + + def test_blocks_have_independent_declarations(self): + """The blocks of if and switch should be separate scopes for declarations.""" + b1 = expr.Var.new("b", types.Bool()) + b2 = expr.Var.new("b", types.Bool()) + self.assertNotEqual(b1, b2) + + base = QuantumCircuit() + with base.if_test(expr.lift(False)) as else_: + base.add_var(b1, expr.lift(False)) + with else_: + base.add_var(b2, expr.lift(False)) + blocks = base.data[-1].operation.blocks + self.assertEqual(set(blocks[0].iter_declared_vars()), {b1}) + self.assertEqual(set(blocks[1].iter_declared_vars()), {b2}) + + base = QuantumCircuit() + with base.switch(expr.lift(False)) as case: + with case(0): + base.add_var(b1, expr.lift(False)) + with case(case.DEFAULT): + base.add_var(b2, expr.lift(False)) + blocks = base.data[-1].operation.blocks + self.assertEqual(set(blocks[0].iter_declared_vars()), {b1}) + self.assertEqual(set(blocks[1].iter_declared_vars()), {b2}) + + def test_can_shadow_outer_name(self): + outer = expr.Var.new("a", types.Bool()) + inner = expr.Var.new("a", types.Bool()) + base = QuantumCircuit(inputs=[outer]) + with base.if_test(expr.lift(False)): + base.add_var(inner, expr.lift(True)) + block = base.data[-1].operation.blocks[0] + self.assertEqual(set(block.iter_declared_vars()), {inner}) + self.assertEqual(set(block.iter_captured_vars()), set()) + + def test_iterators_run_over_scope(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + c = expr.Var.new("c", types.Bool()) + d = expr.Var.new("d", types.Bool()) + + base = QuantumCircuit(inputs=[a, b, c]) + self.assertEqual(set(base.iter_input_vars()), {a, b, c}) + self.assertEqual(set(base.iter_declared_vars()), set()) + self.assertEqual(set(base.iter_captured_vars()), set()) + + with base.switch(expr.lift(3)) as case: + with case(0): + # Nothing here. + self.assertEqual(set(base.iter_vars()), set()) + self.assertEqual(set(base.iter_input_vars()), set()) + self.assertEqual(set(base.iter_declared_vars()), set()) + self.assertEqual(set(base.iter_captured_vars()), set()) + + # Capture a variable. + base.store(a, expr.lift(False)) + self.assertEqual(set(base.iter_captured_vars()), {a}) + + # Declare a variable. + base.add_var(d, expr.lift(False)) + self.assertEqual(set(base.iter_declared_vars()), {d}) + self.assertEqual(set(base.iter_vars()), {a, d}) + + with case(1): + # We should have reset. + self.assertEqual(set(base.iter_vars()), set()) + self.assertEqual(set(base.iter_input_vars()), set()) + self.assertEqual(set(base.iter_declared_vars()), set()) + self.assertEqual(set(base.iter_captured_vars()), set()) + + # Capture a variable. + base.store(b, expr.lift(False)) + self.assertEqual(set(base.iter_captured_vars()), {b}) + + # Capture some more in another scope. + with base.while_loop(expr.lift(False)): + self.assertEqual(set(base.iter_vars()), set()) + base.store(c, expr.lift(False)) + self.assertEqual(set(base.iter_captured_vars()), {c}) + + self.assertEqual(set(base.iter_captured_vars()), {b, c}) + self.assertEqual(set(base.iter_vars()), {b, c}) + # And back to the outer scope. + self.assertEqual(set(base.iter_input_vars()), {a, b, c}) + self.assertEqual(set(base.iter_declared_vars()), set()) + self.assertEqual(set(base.iter_captured_vars()), set()) + + def test_get_var_respects_scope(self): + outer = expr.Var.new("a", types.Bool()) + inner = expr.Var.new("a", types.Bool()) + base = QuantumCircuit(inputs=[outer]) + self.assertEqual(base.get_var("a"), outer) + with base.if_test(expr.lift(False)) as else_: + # Before we've done anything, getting the variable should get the outer one. + self.assertEqual(base.get_var("a"), outer) + + # If we shadow it, we should get the shadowed one after. + base.add_var(inner, expr.lift(False)) + self.assertEqual(base.get_var("a"), inner) + with else_: + # In a new scope, we should see the outer one again. + self.assertEqual(base.get_var("a"), outer) + # ... until we shadow it. + base.add_var(inner, expr.lift(False)) + self.assertEqual(base.get_var("a"), inner) + self.assertEqual(base.get_var("a"), outer) + + def test_has_var_respects_scope(self): + outer = expr.Var.new("a", types.Bool()) + inner = expr.Var.new("a", types.Bool()) + base = QuantumCircuit(inputs=[outer]) + self.assertEqual(base.get_var("a"), outer) + with base.if_test(expr.lift(False)) as else_: + self.assertFalse(base.has_var("b")) + + # Before we've done anything, we should see the outer one. + self.assertTrue(base.has_var("a")) + self.assertTrue(base.has_var(outer)) + self.assertFalse(base.has_var(inner)) + + # If we shadow it, we should see the shadowed one after. + base.add_var(inner, expr.lift(False)) + self.assertTrue(base.has_var("a")) + self.assertFalse(base.has_var(outer)) + self.assertTrue(base.has_var(inner)) + with else_: + # In a new scope, we should see the outer one again. + self.assertTrue(base.has_var("a")) + self.assertTrue(base.has_var(outer)) + self.assertFalse(base.has_var(inner)) + + # ... until we shadow it. + base.add_var(inner, expr.lift(False)) + self.assertTrue(base.has_var("a")) + self.assertFalse(base.has_var(outer)) + self.assertTrue(base.has_var(inner)) + + self.assertTrue(base.has_var("a")) + self.assertTrue(base.has_var(outer)) + self.assertFalse(base.has_var(inner)) + + def test_store_to_clbit_captures_bit(self): + base = QuantumCircuit(1, 2) + with base.if_test(expr.lift(False)): + base.store(expr.lift(base.clbits[0]), expr.lift(True)) + + expected = QuantumCircuit(1, 2) + body = QuantumCircuit([expected.clbits[0]]) + body.store(expr.lift(expected.clbits[0]), expr.lift(True)) + expected.if_test(expr.lift(False), body, [], [0]) + + self.assertEqual(base, expected) + + def test_store_to_register_captures_register(self): + cr1 = ClassicalRegister(2, "cr1") + cr2 = ClassicalRegister(2, "cr2") + base = QuantumCircuit(cr1, cr2) + with base.if_test(expr.lift(False)): + base.store(expr.lift(cr1), expr.lift(3)) + + body = QuantumCircuit(cr1) + body.store(expr.lift(cr1), expr.lift(3)) + expected = QuantumCircuit(cr1, cr2) + expected.if_test(expr.lift(False), body, [], cr1[:]) + + self.assertEqual(base, expected) + @ddt.ddt class TestControlFlowBuildersFailurePaths(QiskitTestCase): @@ -3505,23 +3752,6 @@ def test_non_context_manager_calling_states_reject_missing_resources(self, resou ): test.switch(test.clbits[0], [(False, body)], qubits=qubits, clbits=clbits) - @ddt.data(None, [Clbit()], 0) - def test_builder_block_add_bits_reject_bad_bits(self, bit): - """Test that :obj:`.ControlFlowBuilderBlock` raises if something is given that is an - incorrect type. - - This isn't intended to be something users do at all; the builder block is an internal - construct only, but this keeps coverage checking happy.""" - - def dummy_requester(resource): - raise CircuitError - - builder_block = ControlFlowBuilderBlock( - qubits=(), clbits=(), resource_requester=dummy_requester - ) - with self.assertRaisesRegex(TypeError, r"Can only add qubits or classical bits.*"): - builder_block.add_bits([bit]) - def test_compose_front_inplace_invalid_within_builder(self): """Test that `QuantumCircuit.compose` raises a sensible error when called within a control-flow builder block.""" @@ -3546,3 +3776,124 @@ def test_compose_new_invalid_within_builder(self): with outer.if_test((outer.clbits[0], 1)): with self.assertRaisesRegex(CircuitError, r"Cannot emit a new composed circuit.*"): outer.compose(inner, inplace=False) + + def test_cannot_capture_variable_not_in_scope(self): + a = expr.Var.new("a", types.Bool()) + + base = QuantumCircuit(1, 1) + with base.if_test((0, True)) as else_, self.assertRaisesRegex(CircuitError, "not in scope"): + base.store(a, expr.lift(False)) + with else_, self.assertRaisesRegex(CircuitError, "not in scope"): + base.store(a, expr.lift(False)) + + base.add_input(a) + with base.while_loop((0, True)), self.assertRaisesRegex(CircuitError, "not in scope"): + base.store(expr.Var.new("a", types.Bool()), expr.lift(False)) + + with base.for_loop(range(3)): + with base.switch(base.clbits[0]) as case, case(0): + with self.assertRaisesRegex(CircuitError, "not in scope"): + base.store(expr.Var.new("a", types.Bool()), expr.lift(False)) + + def test_cannot_add_existing_variable(self): + a = expr.Var.new("a", types.Bool()) + base = QuantumCircuit() + with base.if_test(expr.lift(False)) as else_: + base.add_var(a, expr.lift(False)) + with self.assertRaisesRegex(CircuitError, "already present"): + base.add_var(a, expr.lift(False)) + with else_: + base.add_var(a, expr.lift(False)) + with self.assertRaisesRegex(CircuitError, "already present"): + base.add_var(a, expr.lift(False)) + + def test_cannot_shadow_in_same_scope(self): + a = expr.Var.new("a", types.Bool()) + base = QuantumCircuit() + with base.switch(expr.lift(3)) as case: + with case(0): + base.add_var(a, expr.lift(False)) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + base.add_var(a.name, expr.lift(False)) + with case(case.DEFAULT): + base.add_var(a, expr.lift(False)) + with self.assertRaisesRegex(CircuitError, "its name shadows"): + base.add_var(a.name, expr.lift(False)) + + def test_cannot_shadow_captured_variable(self): + """It shouldn't be possible to shadow a variable that has already been captured into the + block.""" + outer = expr.Var.new("a", types.Bool()) + inner = expr.Var.new("a", types.Bool()) + + base = QuantumCircuit(inputs=[outer]) + with base.while_loop(expr.lift(True)): + # Capture the outer. + base.store(outer, expr.lift(True)) + # Attempt to shadow it. + with self.assertRaisesRegex(CircuitError, "its name shadows"): + base.add_var(inner, expr.lift(False)) + + def test_cannot_use_outer_variable_after_shadow(self): + """If we've shadowed a variable, the outer one shouldn't be visible to us for use.""" + outer = expr.Var.new("a", types.Bool()) + inner = expr.Var.new("a", types.Bool()) + + base = QuantumCircuit(inputs=[outer]) + with base.for_loop(range(3)): + # Shadow the outer. + base.add_var(inner, expr.lift(False)) + with self.assertRaisesRegex(CircuitError, "cannot use.*shadowed"): + base.store(outer, expr.lift(True)) + + def test_cannot_use_beyond_outer_shadow(self): + outer = expr.Var.new("a", types.Bool()) + inner = expr.Var.new("a", types.Bool()) + base = QuantumCircuit(inputs=[outer]) + with base.while_loop(expr.lift(True)): + # Shadow 'outer' + base.add_var(inner, expr.lift(True)) + with base.switch(expr.lift(3)) as case, case(0): + with self.assertRaisesRegex(CircuitError, "not in scope"): + # Attempt to access the shadowed variable. + base.store(outer, expr.lift(False)) + + def test_exception_during_initialisation_does_not_add_variable(self): + uint_var = expr.Var.new("a", types.Uint(16)) + bool_expr = expr.Value(False, types.Bool()) + with self.assertRaises(CircuitError): + Store(uint_var, bool_expr) + base = QuantumCircuit() + with base.while_loop(expr.lift(False)): + # Should succeed. + b = base.add_var("b", expr.lift(False)) + try: + base.add_var(uint_var, bool_expr) + except CircuitError: + pass + # Should succeed. + c = base.add_var("c", expr.lift(False)) + local_vars = set(base.iter_vars()) + self.assertEqual(local_vars, {b, c}) + + def test_cannot_use_old_var_not_in_circuit(self): + base = QuantumCircuit() + with base.if_test(expr.lift(False)) as else_: + with self.assertRaisesRegex(CircuitError, "not present"): + base.store(expr.lift(Clbit()), expr.lift(False)) + with else_: + with self.assertRaisesRegex(CircuitError, "not present"): + with base.if_test(expr.equal(ClassicalRegister(2, "c"), 3)): + pass + + def test_cannot_add_input_in_scope(self): + base = QuantumCircuit() + with base.for_loop(range(3)): + with self.assertRaisesRegex(CircuitError, "cannot add an input variable"): + base.add_input("a", types.Bool()) + + def test_cannot_add_uninitialized_in_scope(self): + base = QuantumCircuit() + with base.for_loop(range(3)): + with self.assertRaisesRegex(CircuitError, "cannot add an uninitialized variable"): + base.add_uninitialized_var(expr.Var.new("a", types.Bool())) diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index e53318a7c442..7f3bcee95f12 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -1021,12 +1021,13 @@ def test_standard_base_gate_setting(self, gate_class): """ if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}: self.skipTest("SingletonControlledGate isn't directly instantiated.") - num_free_params = len(_get_free_params(gate_class.__init__, ignore=["self"])) + gate_params = _get_free_params(gate_class.__init__, ignore=["self"]) + num_free_params = len(gate_params) free_params = [0.1 * i for i in range(num_free_params)] - if gate_class in [MCU1Gate, MCPhaseGate]: - free_params[1] = 3 - elif gate_class in [MCXGate]: - free_params[0] = 3 + # set number of control qubits + for i in range(num_free_params): + if gate_params[i] == "num_ctrl_qubits": + free_params[i] = 3 base_gate = gate_class(*free_params) cgate = base_gate.control() @@ -1153,12 +1154,13 @@ def test_base_gate_params_reference(self): with self.subTest(i=repr(gate_class)): if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}: self.skipTest("Singleton class isn't intended to be created directly.") - num_free_params = len(_get_free_params(gate_class.__init__, ignore=["self"])) - free_params = [0.1 * (i + 1) for i in range(num_free_params)] - if gate_class in [MCU1Gate, MCPhaseGate]: - free_params[1] = 3 - elif gate_class in [MCXGate]: - free_params[0] = 3 + gate_params = _get_free_params(gate_class.__init__, ignore=["self"]) + num_free_params = len(gate_params) + free_params = [0.1 * i for i in range(num_free_params)] + # set number of control qubits + for i in range(num_free_params): + if gate_params[i] == "num_ctrl_qubits": + free_params[i] = 3 base_gate = gate_class(*free_params) if base_gate.params: @@ -1379,12 +1381,13 @@ def test_open_controlled_to_matrix(self, gate_class, ctrl_state): """Test open controlled to_matrix.""" if gate_class in {SingletonControlledGate, _SingletonControlledGateOverrides}: self.skipTest("SingletonGateClass isn't intended for direct initalization") - num_free_params = len(_get_free_params(gate_class.__init__, ignore=["self"])) + gate_params = _get_free_params(gate_class.__init__, ignore=["self"]) + num_free_params = len(gate_params) free_params = [0.1 * i for i in range(1, num_free_params + 1)] - if gate_class in [MCU1Gate, MCPhaseGate]: - free_params[1] = 3 - elif gate_class in [MCXGate]: - free_params[0] = 3 + # set number of control qubits + for i in range(num_free_params): + if gate_params[i] == "num_ctrl_qubits": + free_params[i] = 3 cgate = gate_class(*free_params) cgate.ctrl_state = ctrl_state @@ -1487,7 +1490,8 @@ def test_controlled_standard_gates(self, num_ctrl_qubits, gate_class): ctrl_state_zeros = 0 ctrl_state_mixed = ctrl_state_ones >> int(num_ctrl_qubits / 2) - numargs = len(_get_free_params(gate_class)) + gate_params = _get_free_params(gate_class) + numargs = len(gate_params) args = [theta] * numargs if gate_class in [MSGate, Barrier]: args[0] = 2 @@ -1495,6 +1499,12 @@ def test_controlled_standard_gates(self, num_ctrl_qubits, gate_class): args[1] = 2 elif issubclass(gate_class, MCXGate): args = [5] + else: + # set number of control qubits + for i in range(numargs): + if gate_params[i] == "num_ctrl_qubits": + args[i] = 2 + gate = gate_class(*args) for ctrl_state in (ctrl_state_ones, ctrl_state_zeros, ctrl_state_mixed): diff --git a/test/python/circuit/test_hamiltonian_gate.py b/test/python/circuit/test_hamiltonian_gate.py index 0769d765968a..37702a6a07a9 100644 --- a/test/python/circuit/test_hamiltonian_gate.py +++ b/test/python/circuit/test_hamiltonian_gate.py @@ -18,7 +18,7 @@ import qiskit -from qiskit.circuit.library import HamiltonianGate, UnitaryGate +from qiskit.circuit.library import HamiltonianGate from qiskit.test import QiskitTestCase from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.circuit import Parameter @@ -169,4 +169,4 @@ def test_decomposes_into_correct_unitary(self): qc.append(uni2q, [0, 1]) qc = qc.assign_parameters({theta: -np.pi / 2}).decompose() decomposed_ham = qc.data[0].operation - self.assertEqual(decomposed_ham, UnitaryGate(Operator.from_label("XY"))) + self.assertEqual(Operator(decomposed_ham), 1j * Operator.from_label("XY")) diff --git a/test/python/circuit/test_instructions.py b/test/python/circuit/test_instructions.py index fc60be60fdc1..d111d2d21181 100644 --- a/test/python/circuit/test_instructions.py +++ b/test/python/circuit/test_instructions.py @@ -18,16 +18,20 @@ import numpy as np -from qiskit.circuit import Gate -from qiskit.circuit import Parameter -from qiskit.circuit import Instruction, InstructionSet -from qiskit.circuit import QuantumCircuit -from qiskit.circuit import QuantumRegister, ClassicalRegister, Qubit, Clbit -from qiskit.circuit.library.standard_gates.h import HGate -from qiskit.circuit.library.standard_gates.rz import RZGate -from qiskit.circuit.library.standard_gates.x import CXGate -from qiskit.circuit.library.standard_gates.s import SGate -from qiskit.circuit.library.standard_gates.t import TGate +from qiskit.circuit import ( + Gate, + Parameter, + Instruction, + InstructionSet, + QuantumCircuit, + QuantumRegister, + ClassicalRegister, + Qubit, + Clbit, + IfElseOp, +) +from qiskit.circuit.library import HGate, RZGate, CXGate, SGate, TGate +from qiskit.circuit.classical import expr from qiskit.test import QiskitTestCase from qiskit.circuit.exceptions import CircuitError from qiskit.circuit.random import random_circuit @@ -426,6 +430,26 @@ def test_repr_of_instructions(self): ), ) + def test_instruction_condition_bits(self): + """Test that the ``condition_bits`` property behaves correctly until it is deprecated and + removed.""" + bits = [Clbit(), Clbit()] + cr1 = ClassicalRegister(2, "cr1") + cr2 = ClassicalRegister(2, "cr2") + body = QuantumCircuit(cr1, cr2, bits) + + def key(bit): + return body.find_bit(bit).index + + op = IfElseOp((bits[0], False), body) + self.assertEqual(op.condition_bits, [bits[0]]) + + op = IfElseOp((cr1, 3), body) + self.assertEqual(op.condition_bits, list(cr1)) + + op = IfElseOp(expr.logic_and(bits[1], expr.equal(cr2, 3)), body) + self.assertEqual(sorted(op.condition_bits, key=key), sorted([bits[1]] + list(cr2), key=key)) + def test_instructionset_c_if_direct_resource(self): """Test that using :meth:`.InstructionSet.c_if` with an exact classical resource always works, and produces the expected condition.""" diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py index c694b39e6e7a..6a5f780a96af 100644 --- a/test/python/circuit/test_parameters.py +++ b/test/python/circuit/test_parameters.py @@ -1332,7 +1332,14 @@ def _paramvec_names(prefix, length): class TestParameterExpressions(QiskitTestCase): """Test expressions of Parameters.""" - supported_operations = [add, sub, mul, truediv] + # supported operations dictionary operation : accuracy (0=exact match) + supported_operations = { + add: 0, + sub: 0, + mul: 0, + truediv: 0, + pow: 1e-12, + } def setUp(self): super().setUp() @@ -1504,12 +1511,19 @@ def test_expressions_of_parameter_with_constant(self): x = Parameter("x") - for op in self.supported_operations: + for op, rel_tol in self.supported_operations.items(): for const in good_constants: expr = op(const, x) bound_expr = expr.bind({x: 2.3}) - self.assertEqual(complex(bound_expr), op(const, 2.3)) + res = complex(bound_expr) + expected = op(const, 2.3) + if rel_tol > 0: + self.assertTrue( + cmath.isclose(res, expected, rel_tol=rel_tol), f"{res} != {expected}" + ) + else: + self.assertEqual(res, expected) # Division by zero will raise. Tested elsewhere. if const == 0 and op == truediv: @@ -1954,6 +1968,21 @@ def test_parameter_expression_grad(self): self.assertEqual(expr.gradient(x), 2 * x) self.assertEqual(expr.gradient(x).gradient(x), 2) + def test_parameter_expression_exp_log_vs_pow(self): + """Test exp, log, pow for ParameterExpressions by asserting x**y = exp(y log(x)).""" + + x = Parameter("x") + y = Parameter("y") + pow1 = x**y + pow2 = (y * x.log()).exp() + for x_val in [2, 1.3, numpy.pi]: + for y_val in [2, 1.3, 0, -1, -1.0, numpy.pi, 1j]: + with self.subTest(msg="with x={x_val}, y={y_val}"): + vals = {x: x_val, y: y_val} + pow1_val = pow1.bind(vals) + pow2_val = pow2.bind(vals) + self.assertTrue(cmath.isclose(pow1_val, pow2_val), f"{pow1_val} != {pow2_val}") + def test_bound_expression_is_real(self): """Test is_real on bound parameters.""" x = Parameter("x") diff --git a/test/python/circuit/test_piecewise_polynomial.py b/test/python/circuit/test_piecewise_polynomial.py index 9bc34179ba63..e84cc35de87b 100644 --- a/test/python/circuit/test_piecewise_polynomial.py +++ b/test/python/circuit/test_piecewise_polynomial.py @@ -101,7 +101,7 @@ def pw_poly(x): with self.subTest(msg="missing number of state qubits"): with self.assertRaises(AttributeError): # no state qubits set - print(pw_polynomial_rotations.draw()) + _ = str(pw_polynomial_rotations.draw()) with self.subTest(msg="default setup, just setting number of state qubits"): pw_polynomial_rotations.num_state_qubits = 2 diff --git a/test/python/circuit/test_singleton.py b/test/python/circuit/test_singleton.py index ffa3f6d076bb..ebc656f8d933 100644 --- a/test/python/circuit/test_singleton.py +++ b/test/python/circuit/test_singleton.py @@ -14,7 +14,7 @@ """ -Tests for singleton gate behavior +Tests for singleton gate and instruction behavior """ import copy @@ -36,15 +36,16 @@ XGate, C4XGate, ) +from qiskit.circuit import Measure, Reset from qiskit.circuit import Clbit, QuantumCircuit, QuantumRegister, ClassicalRegister -from qiskit.circuit.singleton import SingletonGate, SingletonInstruction +from qiskit.circuit.singleton import SingletonGate from qiskit.converters import dag_to_circuit, circuit_to_dag from qiskit.test.base import QiskitTestCase -class TestSingletonGate(QiskitTestCase): - """Qiskit SingletonGate tests.""" +class TestSingleton(QiskitTestCase): + """Qiskit SingletonGate and SingletonInstruction tests.""" def test_default_singleton(self): gate = HGate() @@ -325,20 +326,38 @@ def __init__(self, x): self.assertEqual(gate.x, 1) self.assertIsNot(MyAbstractGate(1), MyAbstractGate(1)) - def test_inherit_singleton(self): - class Measure(SingletonInstruction): - def __init__(self): - super().__init__("measure", 1, 1, []) + def test_return_type_singleton_instructions(self): + measure = Measure() + new_measure = Measure() + self.assertIs(measure, new_measure) + self.assertIs(measure.base_class, Measure) + self.assertIsInstance(measure, Measure) + + reset = Reset() + new_reset = Reset() + self.assertIs(reset, new_reset) + self.assertIs(reset.base_class, Reset) + self.assertIsInstance(reset, Reset) + + def test_singleton_instruction_integration(self): + measure = Measure() + reset = Reset() + qc = QuantumCircuit(1, 1) + qc.measure(0, 0) + qc.reset(0) + self.assertIs(qc.data[0].operation, measure) + self.assertIs(qc.data[1].operation, reset) + def test_inherit_singleton_instructions(self): class ESPMeasure(Measure): pass - base = Measure() - esp = ESPMeasure() - self.assertIs(esp, ESPMeasure()) - self.assertIsNot(esp, base) - self.assertIs(base.base_class, Measure) - self.assertIs(esp.base_class, ESPMeasure) + measure_base = Measure() + esp_measure = ESPMeasure() + self.assertIs(esp_measure, ESPMeasure()) + self.assertIsNot(esp_measure, measure_base) + self.assertIs(measure_base.base_class, Measure) + self.assertIs(esp_measure.base_class, ESPMeasure) def test_singleton_with_default(self): # Explicitly setting the label to its default. diff --git a/test/python/circuit/test_store.py b/test/python/circuit/test_store.py new file mode 100644 index 000000000000..7977765d8e45 --- /dev/null +++ b/test/python/circuit/test_store.py @@ -0,0 +1,199 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +from qiskit.test import QiskitTestCase +from qiskit.circuit import Store, Clbit, CircuitError, QuantumCircuit, ClassicalRegister +from qiskit.circuit.classical import expr, types + + +class TestStoreInstruction(QiskitTestCase): + """Tests of the properties of the ``Store`` instruction itself.""" + + def test_happy_path_construction(self): + lvalue = expr.Var.new("a", types.Bool()) + rvalue = expr.lift(Clbit()) + constructed = Store(lvalue, rvalue) + self.assertIsInstance(constructed, Store) + self.assertEqual(constructed.lvalue, lvalue) + self.assertEqual(constructed.rvalue, rvalue) + + def test_implicit_cast(self): + lvalue = expr.Var.new("a", types.Bool()) + rvalue = expr.Var.new("b", types.Uint(8)) + constructed = Store(lvalue, rvalue) + self.assertIsInstance(constructed, Store) + self.assertEqual(constructed.lvalue, lvalue) + self.assertEqual(constructed.rvalue, expr.Cast(rvalue, types.Bool(), implicit=True)) + + def test_rejects_non_lvalue(self): + not_an_lvalue = expr.logic_and( + expr.Var.new("a", types.Bool()), expr.Var.new("b", types.Bool()) + ) + rvalue = expr.lift(False) + with self.assertRaisesRegex(CircuitError, "not an l-value"): + Store(not_an_lvalue, rvalue) + + def test_rejects_explicit_cast(self): + lvalue = expr.Var.new("a", types.Uint(16)) + rvalue = expr.Var.new("b", types.Uint(8)) + with self.assertRaisesRegex(CircuitError, "an explicit cast is required"): + Store(lvalue, rvalue) + + def test_rejects_dangerous_cast(self): + lvalue = expr.Var.new("a", types.Uint(8)) + rvalue = expr.Var.new("b", types.Uint(16)) + with self.assertRaisesRegex(CircuitError, "an explicit cast is required.*may be lossy"): + Store(lvalue, rvalue) + + def test_rejects_c_if(self): + instruction = Store(expr.Var.new("a", types.Bool()), expr.Var.new("b", types.Bool())) + with self.assertRaises(NotImplementedError): + instruction.c_if(Clbit(), False) + + +class TestStoreCircuit(QiskitTestCase): + """Tests of the `QuantumCircuit.store` method and appends of `Store`.""" + + def test_produces_expected_operation(self): + a = expr.Var.new("a", types.Bool()) + value = expr.Value(True, types.Bool()) + + qc = QuantumCircuit(inputs=[a]) + qc.store(a, value) + self.assertEqual(qc.data[-1].operation, Store(a, value)) + + qc = QuantumCircuit(captures=[a]) + qc.store(a, value) + self.assertEqual(qc.data[-1].operation, Store(a, value)) + + qc = QuantumCircuit(declarations=[(a, expr.lift(False))]) + qc.store(a, value) + self.assertEqual(qc.data[-1].operation, Store(a, value)) + + def test_allows_stores_with_clbits(self): + clbits = [Clbit(), Clbit()] + a = expr.Var.new("a", types.Bool()) + qc = QuantumCircuit(clbits, inputs=[a]) + qc.store(clbits[0], True) + qc.store(expr.Var(clbits[1], types.Bool()), a) + qc.store(clbits[0], clbits[1]) + qc.store(expr.lift(clbits[0]), expr.lift(clbits[1])) + qc.store(a, expr.lift(clbits[1])) + + expected = [ + Store(expr.lift(clbits[0]), expr.lift(True)), + Store(expr.lift(clbits[1]), a), + Store(expr.lift(clbits[0]), expr.lift(clbits[1])), + Store(expr.lift(clbits[0]), expr.lift(clbits[1])), + Store(a, expr.lift(clbits[1])), + ] + actual = [instruction.operation for instruction in qc.data] + self.assertEqual(actual, expected) + + def test_allows_stores_with_cregs(self): + cregs = [ClassicalRegister(8, "cr1"), ClassicalRegister(8, "cr2")] + a = expr.Var.new("a", types.Uint(8)) + qc = QuantumCircuit(*cregs, captures=[a]) + qc.store(cregs[0], 0xFF) + qc.store(expr.Var(cregs[1], types.Uint(8)), a) + qc.store(cregs[0], cregs[1]) + qc.store(expr.lift(cregs[0]), expr.lift(cregs[1])) + qc.store(a, cregs[1]) + + expected = [ + Store(expr.lift(cregs[0]), expr.lift(0xFF)), + Store(expr.lift(cregs[1]), a), + Store(expr.lift(cregs[0]), expr.lift(cregs[1])), + Store(expr.lift(cregs[0]), expr.lift(cregs[1])), + Store(a, expr.lift(cregs[1])), + ] + actual = [instruction.operation for instruction in qc.data] + self.assertEqual(actual, expected) + + def test_lifts_values(self): + a = expr.Var.new("a", types.Bool()) + qc = QuantumCircuit(captures=[a]) + qc.store(a, True) + self.assertEqual(qc.data[-1].operation, Store(a, expr.lift(True))) + + b = expr.Var.new("b", types.Uint(16)) + qc.add_capture(b) + qc.store(b, 0xFFFF) + self.assertEqual(qc.data[-1].operation, Store(b, expr.lift(0xFFFF))) + + def test_rejects_vars_not_in_circuit(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + + qc = QuantumCircuit() + with self.assertRaisesRegex(CircuitError, "'a'.*not present"): + qc.store(expr.Var.new("a", types.Bool()), True) + + # Not the same 'a' + qc.add_input(a) + with self.assertRaisesRegex(CircuitError, "'a'.*not present"): + qc.store(expr.Var.new("a", types.Bool()), True) + with self.assertRaisesRegex(CircuitError, "'b'.*not present"): + qc.store(a, b) + + def test_rejects_bits_not_in_circuit(self): + a = expr.Var.new("a", types.Bool()) + clbit = Clbit() + qc = QuantumCircuit(captures=[a]) + with self.assertRaisesRegex(CircuitError, "not present"): + qc.store(clbit, False) + with self.assertRaisesRegex(CircuitError, "not present"): + qc.store(clbit, a) + with self.assertRaisesRegex(CircuitError, "not present"): + qc.store(a, clbit) + + def test_rejects_cregs_not_in_circuit(self): + a = expr.Var.new("a", types.Uint(8)) + creg = ClassicalRegister(8, "cr1") + qc = QuantumCircuit(captures=[a]) + with self.assertRaisesRegex(CircuitError, "not present"): + qc.store(creg, 0xFF) + with self.assertRaisesRegex(CircuitError, "not present"): + qc.store(creg, a) + with self.assertRaisesRegex(CircuitError, "not present"): + qc.store(a, creg) + + def test_rejects_non_lvalue(self): + a = expr.Var.new("a", types.Bool()) + b = expr.Var.new("b", types.Bool()) + qc = QuantumCircuit(inputs=[a, b]) + not_an_lvalue = expr.logic_and(a, b) + with self.assertRaisesRegex(CircuitError, "not an l-value"): + qc.store(not_an_lvalue, expr.lift(False)) + + def test_rejects_explicit_cast(self): + lvalue = expr.Var.new("a", types.Uint(16)) + rvalue = expr.Var.new("b", types.Uint(8)) + qc = QuantumCircuit(inputs=[lvalue, rvalue]) + with self.assertRaisesRegex(CircuitError, "an explicit cast is required"): + qc.store(lvalue, rvalue) + + def test_rejects_dangerous_cast(self): + lvalue = expr.Var.new("a", types.Uint(8)) + rvalue = expr.Var.new("b", types.Uint(16)) + qc = QuantumCircuit(inputs=[lvalue, rvalue]) + with self.assertRaisesRegex(CircuitError, "an explicit cast is required.*may be lossy"): + qc.store(lvalue, rvalue) + + def test_rejects_c_if(self): + a = expr.Var.new("a", types.Bool()) + qc = QuantumCircuit([Clbit()], inputs=[a]) + instruction_set = qc.store(a, True) + with self.assertRaises(NotImplementedError): + instruction_set.c_if(qc.clbits[0], False) diff --git a/test/python/circuit/test_unitary.py b/test/python/circuit/test_unitary.py index 27367372f329..26f3eb3fea2e 100644 --- a/test/python/circuit/test_unitary.py +++ b/test/python/circuit/test_unitary.py @@ -25,6 +25,7 @@ from qiskit.quantum_info.random import random_unitary from qiskit.quantum_info.operators import Operator from qiskit.transpiler.passes import CXCancellation +from qiskit.qasm2 import dumps class TestUnitaryGate(QiskitTestCase): @@ -79,7 +80,7 @@ def test_1q_unitary(self): qc.x(qr[0]) qc.append(UnitaryGate(matrix), [qr[0]]) # test of qasm output - self.log.info(qc.qasm()) + self.log.info(dumps(qc)) # test of text drawer self.log.info(qc) dag = circuit_to_dag(qc) @@ -105,7 +106,7 @@ def test_2q_unitary(self): passman.append(CXCancellation()) qc2 = passman.run(qc) # test of qasm output - self.log.info(qc2.qasm()) + self.log.info(dumps(qc2)) # test of text drawer self.log.info(qc2) dag = circuit_to_dag(qc) @@ -221,9 +222,9 @@ def test_qasm_unitary_only_one_def(self): "qreg q0[2];\ncreg c0[1];\n" "x q0[0];\n" "unitary q0[0];\n" - "unitary q0[1];\n" + "unitary q0[1];" ) - self.assertEqual(expected_qasm, qc.qasm()) + self.assertEqual(expected_qasm, dumps(qc)) def test_qasm_unitary_twice(self): """test that a custom unitary can be converted to qasm and that if @@ -245,10 +246,10 @@ def test_qasm_unitary_twice(self): "qreg q0[2];\ncreg c0[1];\n" "x q0[0];\n" "unitary q0[0];\n" - "unitary q0[1];\n" + "unitary q0[1];" ) - self.assertEqual(expected_qasm, qc.qasm()) - self.assertEqual(expected_qasm, qc.qasm()) + self.assertEqual(expected_qasm, dumps(qc)) + self.assertEqual(expected_qasm, dumps(qc)) def test_qasm_2q_unitary(self): """test that a 2 qubit custom unitary can be converted to qasm""" @@ -270,9 +271,9 @@ def test_qasm_2q_unitary(self): "creg c0[1];\n" "x q0[0];\n" "unitary q0[0],q0[1];\n" - "unitary q0[1],q0[0];\n" + "unitary q0[1],q0[0];" ) - self.assertEqual(expected_qasm, qc.qasm()) + self.assertEqual(expected_qasm, dumps(qc)) def test_qasm_unitary_noop(self): """Test that an identity unitary can be converted to OpenQASM 2""" @@ -283,9 +284,9 @@ def test_qasm_unitary_noop(self): 'include "qelib1.inc";\n' "gate unitary q0,q1,q2 { }\n" "qreg q0[3];\n" - "unitary q0[0],q0[1],q0[2];\n" + "unitary q0[0],q0[1],q0[2];" ) - self.assertEqual(expected_qasm, qc.qasm()) + self.assertEqual(expected_qasm, dumps(qc)) def test_unitary_decomposition(self): """Test decomposition for unitary gates over 2 qubits.""" diff --git a/test/python/compiler/test_assembler.py b/test/python/compiler/test_assembler.py index 2641012857a6..56f7e90b7af8 100644 --- a/test/python/compiler/test_assembler.py +++ b/test/python/compiler/test_assembler.py @@ -27,7 +27,6 @@ from qiskit.pulse import Schedule, Acquire, Play from qiskit.pulse.channels import MemorySlot, AcquireChannel, DriveChannel, MeasureChannel from qiskit.pulse.configuration import Kernel, Discriminator -from qiskit.pulse.library import gaussian from qiskit.qobj import QasmQobj, PulseQobj from qiskit.qobj.utils import MeasLevel, MeasReturnType from qiskit.pulse.macros import measure @@ -1201,7 +1200,7 @@ def test_pulse_name_conflicts_in_other_schedule(self): ch_d0 = pulse.DriveChannel(0) for amp in (0.1, 0.2): sched = Schedule() - sched += Play(gaussian(duration=100, amp=amp, sigma=30, name="my_pulse"), ch_d0) + sched += Play(pulse.Gaussian(duration=100, amp=amp, sigma=30, name="my_pulse"), ch_d0) sched += measure(qubits=[0], backend=backend) << 100 schedules.append(sched) diff --git a/test/python/compiler/test_compiler.py b/test/python/compiler/test_compiler.py index 910d1e5866fe..627d5310531f 100644 --- a/test/python/compiler/test_compiler.py +++ b/test/python/compiler/test_compiler.py @@ -24,6 +24,7 @@ from qiskit.test import QiskitTestCase from qiskit.providers.fake_provider import FakeRueschlikon, FakeTenerife from qiskit.qobj import QasmQobj +from qiskit.qasm2 import dumps class TestCompiler(QiskitTestCase): @@ -100,8 +101,8 @@ def test_compile_coupling_map(self): ) job = backend.run(qc_b, shots=shots, seed_simulator=88) result = job.result() - qasm_to_check = qc.qasm() - self.assertEqual(len(qasm_to_check), 173) + qasm_to_check = dumps(qc) + self.assertEqual(len(qasm_to_check), 172) counts = result.get_counts(qc) target = {"000": shots / 2, "111": shots / 2} diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 9a54bdc7da0b..2a13f35adfb9 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -87,7 +87,7 @@ from qiskit.test import QiskitTestCase, slow_test from qiskit.tools import parallel from qiskit.transpiler import CouplingMap, Layout, PassManager, TransformationPass -from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.exceptions import TranspilerError, CircuitTooWideForTarget from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements, GateDirection, VF2PostLayout from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager, level_0_pass_manager @@ -987,7 +987,7 @@ def test_check_circuit_width(self): qc = QuantumCircuit(15, 15) - with self.assertRaises(TranspilerError): + with self.assertRaises(CircuitTooWideForTarget): transpile(qc, coupling_map=cmap) @data(0, 1, 2, 3) @@ -1696,6 +1696,38 @@ def test_paulis_to_constrained_1q_basis(self, opt_level, basis): self.assertGreaterEqual(set(basis) | {"barrier"}, transpiled.count_ops().keys()) self.assertEqual(Operator(qc), Operator(transpiled)) + @data(0, 1, 2, 3) + def test_barrier_not_output(self, opt_level): + """Test that barriers added as part internal transpiler operations do not leak out.""" + qc = QuantumCircuit(2, 2) + qc.cx(0, 1) + qc.measure(range(2), range(2)) + tqc = transpile( + qc, + initial_layout=[1, 4], + coupling_map=[[1, 2], [2, 3], [3, 4]], + optimization_level=opt_level, + ) + self.assertNotIn("barrier", tqc.count_ops()) + + @data(0, 1, 2, 3) + def test_barrier_not_output_input_preservered(self, opt_level): + """Test that barriers added as part internal transpiler operations do not leak out.""" + qc = QuantumCircuit(2, 2) + qc.cx(0, 1) + qc.measure_all() + tqc = transpile( + qc, + initial_layout=[1, 4], + coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4]], + optimization_level=opt_level, + ) + op_counts = tqc.count_ops() + self.assertEqual(op_counts["barrier"], 1) + for inst in tqc.data: + if inst.operation.name == "barrier": + self.assertEqual(len(inst.qubits), 2) + @combine(opt_level=[0, 1, 2, 3]) def test_transpile_annotated_ops(self, opt_level): """Test transpilation of circuits with annotated operations.""" diff --git a/test/python/converters/test_ast_to_dag.py b/test/python/converters/test_ast_to_dag.py deleted file mode 100644 index 490465b5967b..000000000000 --- a/test/python/converters/test_ast_to_dag.py +++ /dev/null @@ -1,67 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for the converters.""" - -import os -import unittest - -from qiskit.converters import ast_to_dag, circuit_to_dag -from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit -from qiskit import qasm -from qiskit.test import QiskitTestCase - - -class TestAstToDag(QiskitTestCase): - """Test AST to DAG.""" - - def setUp(self): - super().setUp() - qr = QuantumRegister(3) - cr = ClassicalRegister(3) - self.circuit = QuantumCircuit(qr, cr) - self.circuit.ccx(qr[0], qr[1], qr[2]) - self.circuit.measure(qr, cr) - self.dag = circuit_to_dag(self.circuit) - - def test_from_ast_to_dag(self): - """Test Unroller.execute()""" - qasm_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "qasm") - ast = qasm.Qasm(os.path.join(qasm_dir, "example.qasm")).parse() - dag_circuit = ast_to_dag(ast) - expected_result = """\ -OPENQASM 2.0; -include "qelib1.inc"; -qreg q[3]; -qreg r[3]; -creg c[3]; -creg d[3]; -h q[0]; -h q[1]; -h q[2]; -cx q[0],r[0]; -cx q[1],r[1]; -cx q[2],r[2]; -barrier q[0],q[1],q[2]; -measure q[0] -> c[0]; -measure q[1] -> c[1]; -measure q[2] -> c[2]; -measure r[0] -> d[0]; -measure r[1] -> d[1]; -measure r[2] -> d[2]; -""" - expected_dag = circuit_to_dag(QuantumCircuit.from_qasm_str(expected_result)) - self.assertEqual(dag_circuit, expected_dag) - - -if __name__ == "__main__": - unittest.main(verbosity=2) diff --git a/test/python/dagcircuit/test_collect_blocks.py b/test/python/dagcircuit/test_collect_blocks.py index adcfd2be2eed..2cfae5486083 100644 --- a/test/python/dagcircuit/test_collect_blocks.py +++ b/test/python/dagcircuit/test_collect_blocks.py @@ -914,6 +914,23 @@ def test_split_layers_dagdependency(self): self.assertEqual(len(blocks[2]), 1) self.assertEqual(len(blocks[3]), 1) + def test_block_collapser_register_condition(self): + """Test that BlockCollapser can handle a register being used more than once.""" + qc = QuantumCircuit(1, 2) + qc.x(0).c_if(qc.cregs[0], 0) + qc.y(0).c_if(qc.cregs[0], 1) + + dag = circuit_to_dag(qc) + blocks = BlockCollector(dag).collect_all_matching_blocks( + lambda _: True, split_blocks=False, min_block_size=1 + ) + dag = BlockCollapser(dag).collapse_to_operation(blocks, lambda circ: circ.to_instruction()) + collapsed_qc = dag_to_circuit(dag) + + self.assertEqual(len(collapsed_qc.data), 1) + self.assertEqual(collapsed_qc.data[0].operation.definition.num_qubits, 1) + self.assertEqual(collapsed_qc.data[0].operation.definition.num_clbits, 2) + if __name__ == "__main__": unittest.main() diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index 9d73ac5a1af0..b539ff3d5da3 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -487,18 +487,6 @@ def test_apply_operation_back(self): self.assertEqual(len(list(self.dag.nodes())), 16) self.assertEqual(len(list(self.dag.edges())), 17) - def test_apply_operation_rejects_none(self): - """Test that the ``apply_operation_*`` methods warn when given ``None``.""" - noop = Instruction("noop", 0, 0, []) - with self.assertWarnsRegex(DeprecationWarning, "Passing 'None'"): - self.dag.apply_operation_back(noop, None, ()) - with self.assertWarnsRegex(DeprecationWarning, "Passing 'None'"): - self.dag.apply_operation_back(noop, (), None) - with self.assertWarnsRegex(DeprecationWarning, "Passing 'None'"): - self.dag.apply_operation_front(noop, None, ()) - with self.assertWarnsRegex(DeprecationWarning, "Passing 'None'"): - self.dag.apply_operation_front(noop, (), None) - def test_edges(self): """Test that DAGCircuit.edges() behaves as expected with ops.""" x_gate = XGate().c_if(*self.condition) diff --git a/test/python/opflow/__init__.py b/test/python/opflow/__init__.py deleted file mode 100644 index 16d56b668594..000000000000 --- a/test/python/opflow/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Opflow test module""" - -from .opflow_test_case import QiskitOpflowTestCase - -__all__ = ["QiskitOpflowTestCase"] diff --git a/test/python/opflow/opflow_test_case.py b/test/python/opflow/opflow_test_case.py deleted file mode 100644 index 142fb1db76b5..000000000000 --- a/test/python/opflow/opflow_test_case.py +++ /dev/null @@ -1,30 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Opflow Test Case""" - -import warnings -from qiskit.test import QiskitTestCase - - -class QiskitOpflowTestCase(QiskitTestCase): - """Opflow test Case""" - - def setUp(self): - super().setUp() - # ignore opflow msgs - warnings.filterwarnings("ignore", category=DeprecationWarning, message=r".*opflow.*") - - def tearDown(self): - super().tearDown() - # restore opflow msgs - warnings.filterwarnings("error", category=DeprecationWarning, message=r".*opflow.*") diff --git a/test/python/opflow/test_abelian_grouper.py b/test/python/opflow/test_abelian_grouper.py deleted file mode 100644 index 4fd6cf2a85ba..000000000000 --- a/test/python/opflow/test_abelian_grouper.py +++ /dev/null @@ -1,133 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Abelian Grouper""" - -import random -import unittest -from itertools import combinations, product -from test.python.opflow import QiskitOpflowTestCase - -from ddt import data, ddt, unpack - -from qiskit.opflow import AbelianGrouper, commutator, I, OpflowError, Plus, SummedOp, X, Y, Z, Zero - - -@ddt -class TestAbelianGrouper(QiskitOpflowTestCase): - """Abelian Grouper tests.""" - - @data(*product(["h2_op", "generic"], [True, False])) - @unpack - def test_abelian_grouper(self, pauli_op, is_summed_op): - """Abelian grouper test""" - if pauli_op == "h2_op": - paulis = ( - (-1.052373245772859 * I ^ I) - + (0.39793742484318045 * I ^ Z) - + (-0.39793742484318045 * Z ^ I) - + (-0.01128010425623538 * Z ^ Z) - + (0.18093119978423156 * X ^ X) - ) - num_groups = 2 - else: - paulis = ( - (I ^ I ^ X ^ X * 0.2) - + (Z ^ Z ^ X ^ X * 0.3) - + (Z ^ Z ^ Z ^ Z * 0.4) - + (X ^ X ^ Z ^ Z * 0.5) - + (X ^ X ^ X ^ X * 0.6) - + (I ^ X ^ X ^ X * 0.7) - ) - num_groups = 4 - if is_summed_op: - paulis = paulis.to_pauli_op() - grouped_sum = AbelianGrouper().convert(paulis) - self.assertEqual(len(grouped_sum.oplist), num_groups) - for group in grouped_sum: - for op_1, op_2 in combinations(group, 2): - if is_summed_op: - self.assertEqual(op_1 @ op_2, op_2 @ op_1) - else: - self.assertTrue(commutator(op_1, op_2).is_zero()) - - def test_ablian_grouper_no_commute(self): - """Abelian grouper test when non-PauliOp is given""" - ops = Zero ^ Plus + X ^ Y - with self.assertRaises(OpflowError): - _ = AbelianGrouper.group_subops(ops) - - @data(True, False) - def test_group_subops(self, is_summed_op): - """grouper subroutine test""" - paulis = (I ^ X) + (2 * X ^ X) + (3 * Z ^ Y) - if is_summed_op: - paulis = paulis.to_pauli_op() - grouped_sum = AbelianGrouper.group_subops(paulis) - self.assertEqual(len(grouped_sum), 2) - with self.subTest("test group subops 1"): - if is_summed_op: - expected = SummedOp( - [ - SummedOp([I ^ X, 2.0 * X ^ X], abelian=True), - SummedOp([3.0 * Z ^ Y], abelian=True), - ] - ) - self.assertEqual(grouped_sum, expected) - else: - self.assertSetEqual( - frozenset(frozenset(grouped_sum[i].primitive.to_list()) for i in range(2)), - frozenset({frozenset({("ZY", 3)}), frozenset({("IX", 1), ("XX", 2)})}), - ) - - paulis = X + (2 * Y) + (3 * Z) - if is_summed_op: - paulis = paulis.to_pauli_op() - grouped_sum = AbelianGrouper.group_subops(paulis) - self.assertEqual(len(grouped_sum), 3) - with self.subTest("test group subops 2"): - if is_summed_op: - self.assertEqual(grouped_sum, paulis) - else: - self.assertSetEqual( - frozenset(sum((grouped_sum[i].primitive.to_list() for i in range(3)), [])), - frozenset([("X", 1), ("Y", 2), ("Z", 3)]), - ) - - @data(True, False) - def test_abelian_grouper_random(self, is_summed_op): - """Abelian grouper test with random paulis""" - random.seed(1234) - k = 10 # size of pauli operators - n = 100 # number of pauli operators - num_tests = 20 # number of tests - for _ in range(num_tests): - paulis = [] - for _ in range(n): - pauliop = 1 - for eachop in random.choices([I] * 5 + [X, Y, Z], k=k): - pauliop ^= eachop - paulis.append(pauliop) - pauli_sum = sum(paulis) - if is_summed_op: - pauli_sum = pauli_sum.to_pauli_op() - grouped_sum = AbelianGrouper().convert(pauli_sum) - for group in grouped_sum: - for op_1, op_2 in combinations(group, 2): - if is_summed_op: - self.assertEqual(op_1 @ op_2, op_2 @ op_1) - else: - self.assertTrue(commutator(op_1, op_2).is_zero()) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_aer_pauli_expectation.py b/test/python/opflow/test_aer_pauli_expectation.py deleted file mode 100644 index 808f7bab8716..000000000000 --- a/test/python/opflow/test_aer_pauli_expectation.py +++ /dev/null @@ -1,297 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test AerPauliExpectation""" - -import itertools -import unittest -from test.python.opflow import QiskitOpflowTestCase -import numpy as np - -from qiskit.circuit.library import RealAmplitudes -from qiskit.opflow import ( - CX, - AerPauliExpectation, - CircuitSampler, - CircuitStateFn, - H, - I, - ListOp, - Minus, - One, - PauliExpectation, - PauliSumOp, - Plus, - S, - StateFn, - X, - Y, - Z, - Zero, - MatrixOp, -) -from qiskit.utils import QuantumInstance, optionals - - -class TestAerPauliExpectation(QiskitOpflowTestCase): - """Pauli Change of Basis Expectation tests.""" - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required to run this test") - def setUp(self) -> None: - super().setUp() - from qiskit_aer import AerSimulator - - self.seed = 97 - self.backend = AerSimulator() - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - self.backend, seed_simulator=self.seed, seed_transpiler=self.seed - ) - self.sampler = CircuitSampler(q_instance, attach_results=True) - self.expect = AerPauliExpectation() - - def test_pauli_expect_pair(self): - """pauli expect pair test""" - op = Z ^ Z - # wvf = (Pl^Pl) + (Ze^Ze) - wvf = CX @ (H ^ I) @ Zero - converted_meas = self.expect.convert(~StateFn(op) @ wvf) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - self.assertAlmostEqual(sampled.eval(), 0, delta=0.1) - - def test_pauli_expect_single(self): - """pauli expect single test""" - paulis = [Z, X, Y, I] - states = [Zero, One, Plus, Minus, S @ Plus, S @ Minus] - for pauli, state in itertools.product(paulis, states): - converted_meas = self.expect.convert(~StateFn(pauli) @ state) - matmulmean = state.adjoint().to_matrix() @ pauli.to_matrix() @ state.to_matrix() - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - self.assertAlmostEqual(sampled.eval(), matmulmean, delta=0.1) - - def test_pauli_expect_op_vector(self): - """pauli expect op vector test""" - paulis_op = ListOp([X, Y, Z, I]) - converted_meas = self.expect.convert(~StateFn(paulis_op)) - - with self.assertWarns(DeprecationWarning): - plus_mean = converted_meas @ Plus - sampled_plus = self.sampler.convert(plus_mean) - np.testing.assert_array_almost_equal(sampled_plus.eval(), [1, 0, 0, 1], decimal=1) - - minus_mean = converted_meas @ Minus - sampled_minus = self.sampler.convert(minus_mean) - np.testing.assert_array_almost_equal(sampled_minus.eval(), [-1, 0, 0, 1], decimal=1) - - zero_mean = converted_meas @ Zero - sampled_zero = self.sampler.convert(zero_mean) - np.testing.assert_array_almost_equal(sampled_zero.eval(), [0, 0, 1, 1], decimal=1) - - sum_zero = (Plus + Minus) * (0.5**0.5) - sum_zero_mean = converted_meas @ sum_zero - sampled_zero_mean = self.sampler.convert(sum_zero_mean) - - # !!NOTE!!: Depolarizing channel (Sampling) means interference - # does not happen between circuits in sum, so expectation does - # not equal expectation for Zero!! - np.testing.assert_array_almost_equal(sampled_zero_mean.eval(), [0, 0, 0, 1]) - - def test_pauli_expect_state_vector(self): - """pauli expect state vector test""" - states_op = ListOp([One, Zero, Plus, Minus]) - paulis_op = X - converted_meas = self.expect.convert(~StateFn(paulis_op) @ states_op) - - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - - # Small test to see if execution results are accessible - for composed_op in sampled: - self.assertTrue(hasattr(composed_op[0], "execution_results")) - - np.testing.assert_array_almost_equal(sampled.eval(), [0, 0, 1, -1], decimal=1) - - def test_pauli_expect_non_hermitian_matrixop(self): - """pauli expect state vector with non hermitian operator test""" - states_op = ListOp([One, Zero, Plus, Minus]) - op_mat = np.array([[0, 1], [2, 3]]) - op = MatrixOp(op_mat) - converted_meas = self.expect.convert(StateFn(op, is_measurement=True) @ states_op) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - - np.testing.assert_array_almost_equal(sampled.eval(), [3, 0, 3, 0], decimal=1) - - def test_pauli_expect_non_hermitian_pauliop(self): - """pauli expect state vector with non hermitian operator test""" - states_op = ListOp([One, Zero, Plus, Minus]) - op = 1j * X - converted_meas = self.expect.convert(StateFn(op, is_measurement=True) @ states_op) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - np.testing.assert_array_almost_equal(sampled.eval(), [0, 0, 1j, -1j], decimal=1) - - def test_pauli_expect_op_vector_state_vector(self): - """pauli expect op vector state vector test""" - paulis_op = ListOp([X, Y, Z, I]) - states_op = ListOp([One, Zero, Plus, Minus]) - - valids = [ - [+0, 0, 1, -1], - [+0, 0, 0, 0], - [-1, 1, 0, -0], - [+1, 1, 1, 1], - ] - converted_meas = self.expect.convert(~StateFn(paulis_op) @ states_op) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - np.testing.assert_array_almost_equal(sampled.eval(), valids, decimal=1) - - def test_multi_representation_ops(self): - """Test observables with mixed representations""" - - mixed_ops = ListOp([X.to_matrix_op(), H, H + I, X]) - converted_meas = self.expect.convert(~StateFn(mixed_ops)) - - plus_mean = converted_meas @ Plus - with self.assertWarns(DeprecationWarning): - sampled_plus = self.sampler.convert(plus_mean) - - np.testing.assert_array_almost_equal( - sampled_plus.eval(), [1, 0.5**0.5, (1 + 0.5**0.5), 1], decimal=1 - ) - - def test_parameterized_qobj(self): - """grouped pauli expectation test""" - - two_qubit_h2 = ( - (-1.052373245772859 * I ^ I) - + (0.39793742484318045 * I ^ Z) - + (-0.39793742484318045 * Z ^ I) - + (-0.01128010425623538 * Z ^ Z) - + (0.18093119978423156 * X ^ X) - ) - with self.assertWarns(DeprecationWarning): - aer_sampler = CircuitSampler( - self.sampler.quantum_instance, param_qobj=True, attach_results=True - ) - ansatz = RealAmplitudes() - ansatz.num_qubits = 2 - - observable_meas = self.expect.convert(StateFn(two_qubit_h2, is_measurement=True)) - ansatz_circuit_op = CircuitStateFn(ansatz) - expect_op = observable_meas.compose(ansatz_circuit_op).reduce() - - def generate_parameters(num): - param_bindings = {} - for param in ansatz.parameters: - values = [] - for _ in range(num): - values.append(np.random.rand()) - param_bindings[param] = values - return param_bindings - - def validate_sampler(ideal, sut, param_bindings): - with self.assertWarns(DeprecationWarning): - expect_sampled = ideal.convert(expect_op, params=param_bindings).eval() - actual_sampled = sut.convert(expect_op, params=param_bindings).eval() - self.assertTrue( - np.allclose(actual_sampled, expect_sampled), - f"{actual_sampled} != {expect_sampled}", - ) - - def get_circuit_templates(sampler): - return sampler._transpiled_circ_templates - - def validate_aer_binding_used(templates): - self.assertIsNotNone(templates) - - def validate_aer_templates_reused(prev_templates, cur_templates): - self.assertIs(prev_templates, cur_templates) - - validate_sampler(self.sampler, aer_sampler, generate_parameters(1)) - cur_templates = get_circuit_templates(aer_sampler) - - validate_aer_binding_used(cur_templates) - - prev_templates = cur_templates - validate_sampler(self.sampler, aer_sampler, generate_parameters(2)) - cur_templates = get_circuit_templates(aer_sampler) - - validate_aer_templates_reused(prev_templates, cur_templates) - - prev_templates = cur_templates - validate_sampler(self.sampler, aer_sampler, generate_parameters(2)) # same num of params - cur_templates = get_circuit_templates(aer_sampler) - - validate_aer_templates_reused(prev_templates, cur_templates) - - def test_pauli_expectation_param_qobj(self): - """Test PauliExpectation with param_qobj""" - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - self.backend, seed_simulator=self.seed, seed_transpiler=self.seed, shots=10000 - ) - qubit_op = (0.1 * I ^ I) + (0.2 * I ^ Z) + (0.3 * Z ^ I) + (0.4 * Z ^ Z) + (0.5 * X ^ X) - ansatz = RealAmplitudes(qubit_op.num_qubits) - ansatz_circuit_op = CircuitStateFn(ansatz) - observable = PauliExpectation().convert(~StateFn(qubit_op)) - expect_op = observable.compose(ansatz_circuit_op).reduce() - params1 = {} - params2 = {} - for param in ansatz.parameters: - params1[param] = [0] - params2[param] = [0, 0] - - with self.assertWarns(DeprecationWarning): - sampler1 = CircuitSampler(backend=q_instance, param_qobj=False) - samples1 = sampler1.convert(expect_op, params=params1) - val1 = np.real(samples1.eval())[0] - samples2 = sampler1.convert(expect_op, params=params2) - val2 = np.real(samples2.eval()) - sampler2 = CircuitSampler(backend=q_instance, param_qobj=True) - samples3 = sampler2.convert(expect_op, params=params1) - val3 = np.real(samples3.eval()) - samples4 = sampler2.convert(expect_op, params=params2) - val4 = np.real(samples4.eval()) - - np.testing.assert_array_almost_equal([val1] * 2, val2, decimal=2) - np.testing.assert_array_almost_equal(val1, val3, decimal=2) - np.testing.assert_array_almost_equal([val1] * 2, val4, decimal=2) - - def test_list_pauli_sum(self): - """Test AerPauliExpectation for ListOp[PauliSumOp]""" - test_op = ListOp([PauliSumOp.from_list([("XX", 1), ("ZI", 3), ("ZZ", 5)])]) - observable = AerPauliExpectation().convert(~StateFn(test_op)) - self.assertIsInstance(observable, ListOp) - self.assertIsInstance(observable[0], CircuitStateFn) - self.assertTrue(observable[0].is_measurement) - - def test_expectation_with_coeff(self): - """Test AerPauliExpectation with coefficients.""" - with self.subTest("integer coefficients"): - exp = 3 * ~StateFn(X) @ (2 * Minus) - with self.assertWarns(DeprecationWarning): - target = self.sampler.convert(self.expect.convert(exp)).eval() - self.assertAlmostEqual(target, -12) - - with self.subTest("complex coefficients"): - exp = 3j * ~StateFn(X) @ (2j * Minus) - with self.assertWarns(DeprecationWarning): - target = self.sampler.convert(self.expect.convert(exp)).eval() - self.assertAlmostEqual(target, -12j) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_cvar.py b/test/python/opflow/test_cvar.py deleted file mode 100644 index 4d38356f2718..000000000000 --- a/test/python/opflow/test_cvar.py +++ /dev/null @@ -1,261 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""The Conditional Value at Risk (CVaR) measurement.""" - -import unittest -import warnings - -from test.python.opflow import QiskitOpflowTestCase -import numpy as np -from ddt import ddt, data - -from qiskit import QuantumCircuit -from qiskit.utils import algorithm_globals -from qiskit.opflow import ( - CVaRMeasurement, - StateFn, - Z, - I, - X, - Y, - Plus, - PauliSumOp, - PauliExpectation, - MatrixExpectation, - CVaRExpectation, - ListOp, - CircuitOp, - AerPauliExpectation, - MatrixOp, - OpflowError, -) - - -class TestCVaRMeasurement(QiskitOpflowTestCase): - """Test the CVaR measurement.""" - - def expected_cvar(self, statevector, operator, alpha): - """Compute the expected CVaR expected value.""" - - probabilities = statevector * np.conj(statevector) - - # get energies - num_bits = int(np.log2(len(statevector))) - energies = [] - for i, _ in enumerate(probabilities): - basis_state = np.binary_repr(i, num_bits) - energies += [operator.eval(basis_state).eval(basis_state)] - - # sort ascending - i_sorted = np.argsort(energies) - energies = [energies[i] for i in i_sorted] - probabilities = [probabilities[i] for i in i_sorted] - - # add up - result = 0 - accumulated_probabilities = 0 - for energy, probability in zip(energies, probabilities): - accumulated_probabilities += probability - if accumulated_probabilities <= alpha: - result += probability * energy - else: # final term - result += (alpha - accumulated_probabilities + probability) * energy - break - - return result / alpha - - def cleanup_algorithm_globals(self, massive): - """Method used to reset the values of algorithm_globals.""" - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.massive = massive - - def test_cvar_simple(self): - """Test a simple case with a single Pauli.""" - theta = 1.2 - qc = QuantumCircuit(1) - qc.ry(theta, 0) - statefn = StateFn(qc) - - for alpha in [0.2, 0.4, 1]: - with self.subTest(alpha=alpha): - cvar = (CVaRMeasurement(Z, alpha) @ statefn).eval() - ref = self.expected_cvar(statefn.to_matrix(), Z, alpha) - self.assertAlmostEqual(cvar, ref) - - def test_cvar_simple_with_coeff(self): - """Test a simple case with a non-unity coefficient""" - theta = 2.2 - qc = QuantumCircuit(1) - qc.ry(theta, 0) - statefn = StateFn(qc) - - alpha = 0.2 - cvar = ((-1 * CVaRMeasurement(Z, alpha)) @ statefn).eval() - ref = self.expected_cvar(statefn.to_matrix(), Z, alpha) - self.assertAlmostEqual(cvar, -1 * ref) - - def test_add(self): - """Test addition.""" - theta = 2.2 - qc = QuantumCircuit(1) - qc.ry(theta, 0) - statefn = StateFn(qc) - - alpha = 0.2 - cvar = -1 * CVaRMeasurement(Z, alpha) - ref = self.expected_cvar(statefn.to_matrix(), Z, alpha) - - other = ~StateFn(I) - - # test add in both directions - res1 = ((cvar + other) @ statefn).eval() - res2 = ((other + other) @ statefn).eval() - - self.assertAlmostEqual(res1, 1 - ref) - self.assertAlmostEqual(res2, 1 - ref) - - def invalid_input(self): - """Test invalid input raises an error.""" - op = Z - - with self.subTest("alpha < 0"): - with self.assertRaises(ValueError): - _ = CVaRMeasurement(op, alpha=-0.2) - - with self.subTest("alpha > 1"): - with self.assertRaises(ValueError): - _ = CVaRMeasurement(op, alpha=12.3) - - with self.subTest("Single pauli operator not diagonal"): - op = Y - with self.assertRaises(OpflowError): - _ = CVaRMeasurement(op) - - with self.subTest("Summed pauli operator not diagonal"): - op = X ^ Z + Z ^ I - with self.assertRaises(OpflowError): - _ = CVaRMeasurement(op) - - with self.subTest("List operator not diagonal"): - op = ListOp([X ^ Z, Z ^ I]) - with self.assertRaises(OpflowError): - _ = CVaRMeasurement(op) - - with self.subTest("Matrix operator not diagonal"): - op = MatrixOp([[1, 1], [0, 1]]) - with self.assertRaises(OpflowError): - _ = CVaRMeasurement(op) - - def test_unsupported_operations(self): - """Assert unsupported operations raise an error.""" - cvar = CVaRMeasurement(Z) - - attrs = ["to_matrix", "to_matrix_op", "to_density_matrix", "to_circuit_op", "sample"] - for attr in attrs: - with self.subTest(attr): - with self.assertRaises(NotImplementedError): - _ = getattr(cvar, attr)() - - with self.subTest("adjoint"): - with self.assertRaises(OpflowError): - cvar.adjoint() - - def test_cvar_on_paulisumop(self): - """Test a large PauliSumOp is checked for diagonality efficiently. - - Regression test for Qiskit/qiskit-terra#7573. - """ - op = PauliSumOp.from_list([("Z" * 30, 1)]) - # assert global algorithm settings do not have massive calculations turned on - # -- which is the default, but better to be sure in the test! - # also add a cleanup so we're sure to reset to the original value after the test, even if - # the test would fail - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - self.addCleanup(self.cleanup_algorithm_globals, algorithm_globals.massive) - algorithm_globals.massive = False - - cvar = CVaRMeasurement(op, alpha=0.1) - fake_probabilities = [0.2, 0.8] - fake_energies = [1, 2] - - expectation = cvar.compute_cvar(fake_energies, fake_probabilities) - self.assertEqual(expectation, 1) - - -@ddt -class TestCVaRExpectation(QiskitOpflowTestCase): - """Test the CVaR expectation object.""" - - def test_construction(self): - """Test the correct operator expression is constructed.""" - - alpha = 0.5 - base_expecation = PauliExpectation() - cvar_expecation = CVaRExpectation(alpha=alpha, expectation=base_expecation) - - with self.subTest("single operator"): - op = ~StateFn(Z) @ Plus - expected = CVaRMeasurement(Z, alpha) @ Plus - cvar = cvar_expecation.convert(op) - self.assertEqual(cvar, expected) - - with self.subTest("list operator"): - op = ~StateFn(ListOp([Z ^ Z, I ^ Z])) @ (Plus ^ Plus) - expected = ListOp( - [ - CVaRMeasurement((Z ^ Z), alpha) @ (Plus ^ Plus), - CVaRMeasurement((I ^ Z), alpha) @ (Plus ^ Plus), - ] - ) - cvar = cvar_expecation.convert(op) - self.assertEqual(cvar, expected) - - def test_unsupported_expectation(self): - """Assert passing an AerPauliExpectation raises an error.""" - expecation = AerPauliExpectation() - with self.assertRaises(NotImplementedError): - _ = CVaRExpectation(alpha=1, expectation=expecation) - - @data(PauliExpectation(), MatrixExpectation()) - def test_underlying_expectation(self, base_expecation): - """Test the underlying expectation works correctly.""" - - cvar_expecation = CVaRExpectation(alpha=0.3, expectation=base_expecation) - circuit = QuantumCircuit(2) - circuit.z(0) - circuit.cp(0.5, 0, 1) - circuit.t(1) - op = ~StateFn(CircuitOp(circuit)) @ (Plus ^ 2) - - cvar = cvar_expecation.convert(op) - expected = base_expecation.convert(op) - - # test if the operators have been transformed in the same manner - self.assertEqual(cvar.oplist[0].primitive, expected.oplist[0].primitive) - - def test_compute_variance(self): - """Test if the compute_variance method works""" - alphas = [0, 0.3, 0.5, 0.7, 1] - correct_vars = [0, 0, 0, 0.8163, 1] - for i, alpha in enumerate(alphas): - base_expecation = PauliExpectation() - cvar_expecation = CVaRExpectation(alpha=alpha, expectation=base_expecation) - op = ~StateFn(Z ^ Z) @ (Plus ^ Plus) - cvar_var = cvar_expecation.compute_variance(op) - np.testing.assert_almost_equal(cvar_var, correct_vars[i], decimal=3) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_evolution.py b/test/python/opflow/test_evolution.py deleted file mode 100644 index 55810dd7a114..000000000000 --- a/test/python/opflow/test_evolution.py +++ /dev/null @@ -1,384 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Evolution""" - -import unittest - -from test.python.opflow import QiskitOpflowTestCase -import numpy as np -import scipy.linalg - -import qiskit -from qiskit.circuit import Parameter, ParameterVector -from qiskit.circuit.library import UnitaryGate -from qiskit.opflow import ( - CX, - CircuitOp, - EvolutionFactory, - EvolvedOp, - H, - I, - ListOp, - PauliTrotterEvolution, - QDrift, - SummedOp, - Suzuki, - Trotter, - X, - Y, - Z, - Zero, -) - - -class TestEvolution(QiskitOpflowTestCase): - """Evolution tests.""" - - def test_exp_i(self): - """exponential of Pauli test""" - op = Z.exp_i() - gate = op.to_circuit().data[0].operation - self.assertIsInstance(gate, qiskit.circuit.library.RZGate) - self.assertEqual(gate.params[0], 2) - - def test_trotter_with_identity(self): - """trotterization of operator with identity term""" - op = (2.0 * I ^ I) + (Z ^ Y) - exact_matrix = scipy.linalg.expm(-1j * op.to_matrix()) - evo = PauliTrotterEvolution(trotter_mode="suzuki", reps=2) - with self.subTest("all PauliOp terms"): - circ_op = evo.convert(EvolvedOp(op)) - circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data - np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) - - with self.subTest("MatrixOp identity term"): - op = (2.0 * I ^ I).to_matrix_op() + (Z ^ Y) - circ_op = evo.convert(EvolvedOp(op)) - circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data - np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) - - with self.subTest("CircuitOp identity term"): - op = (2.0 * I ^ I).to_circuit_op() + (Z ^ Y) - circ_op = evo.convert(EvolvedOp(op)) - circuit_matrix = qiskit.quantum_info.Operator(circ_op.to_circuit()).data - np.testing.assert_array_almost_equal(exact_matrix, circuit_matrix) - - def test_pauli_evolution(self): - """pauli evolution test""" - op = ( - (-1.052373245772859 * I ^ I) - + (0.39793742484318045 * I ^ Z) - + (0.18093119978423156 * X ^ X) - + (-0.39793742484318045 * Z ^ I) - + (-0.01128010425623538 * Z ^ Z) - ) - evolution = EvolutionFactory.build(operator=op) - # wf = (Pl^Pl) + (Ze^Ze) - wf = ((np.pi / 2) * op).exp_i() @ CX @ (H ^ I) @ Zero - mean = evolution.convert(wf) - self.assertIsNotNone(mean) - - def test_summedop_pauli_evolution(self): - """SummedOp[PauliOp] evolution test""" - op = SummedOp( - [ - (-1.052373245772859 * I ^ I), - (0.39793742484318045 * I ^ Z), - (0.18093119978423156 * X ^ X), - (-0.39793742484318045 * Z ^ I), - (-0.01128010425623538 * Z ^ Z), - ] - ) - evolution = EvolutionFactory.build(operator=op) - # wf = (Pl^Pl) + (Ze^Ze) - wf = ((np.pi / 2) * op).exp_i() @ CX @ (H ^ I) @ Zero - mean = evolution.convert(wf) - self.assertIsNotNone(mean) - - def test_parameterized_evolution(self): - """parameterized evolution test""" - thetas = ParameterVector("θ", length=7) - op = ( - (thetas[0] * I ^ I) - + (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) - ) - op = op * thetas[6] - evolution = PauliTrotterEvolution(trotter_mode="trotter", reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - mean = evolution.convert(wf) - circuit = mean.to_circuit() - # Check that all parameters are in the circuit - for p in thetas: - self.assertIn(p, circuit.parameters) - # Check that the identity-parameters only exist as global phase - self.assertNotIn(thetas[0], circuit._parameter_table.get_keys()) - - def test_bind_parameters(self): - """bind parameters test""" - thetas = ParameterVector("θ", length=6) - op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) - ) - op = thetas[0] * op - evolution = PauliTrotterEvolution(trotter_mode="trotter", reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - wf = wf.assign_parameters({thetas: np.arange(10, 16)}) - mean = evolution.convert(wf) - circuit_params = mean.to_circuit().parameters - # Check that the no parameters are in the circuit - for p in thetas[1:]: - self.assertNotIn(p, circuit_params) - - def test_bind_circuit_parameters(self): - """bind circuit parameters test""" - thetas = ParameterVector("θ", length=6) - op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) - ) - op = thetas[0] * op - evolution = PauliTrotterEvolution(trotter_mode="trotter", reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - evo = evolution.convert(wf) - mean = evo.assign_parameters({thetas: np.arange(10, 16)}) - # Check that the no parameters are in the circuit - for p in thetas[1:]: - self.assertNotIn(p, mean.to_circuit().parameters) - # Check that original circuit is unchanged - for p in thetas: - self.assertIn(p, evo.to_circuit().parameters) - - # TODO test with other Op types than CircuitStateFn - def test_bind_parameter_list(self): - """bind parameters list test""" - thetas = ParameterVector("θ", length=6) - op = ( - (thetas[1] * I ^ Z) - + (thetas[2] * X ^ X) - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z) - + (thetas[5] * Z ^ Z) - ) - op = thetas[0] * op - evolution = PauliTrotterEvolution(trotter_mode="trotter", reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - evo = evolution.convert(wf) - param_list = np.transpose([np.arange(10, 16), np.arange(2, 8), np.arange(30, 36)]).tolist() - means = evo.assign_parameters({thetas: param_list}) - self.assertIsInstance(means, ListOp) - # Check that the no parameters are in the circuit - for p in thetas[1:]: - for circop in means.oplist: - self.assertNotIn(p, circop.to_circuit().parameters) - # Check that original circuit is unchanged - for p in thetas: - self.assertIn(p, evo.to_circuit().parameters) - - def test_bind_parameters_complex(self): - """bind parameters with a complex value test""" - th1 = Parameter("th1") - th2 = Parameter("th2") - - operator = th1 * X + th2 * Y - bound_operator = operator.bind_parameters({th1: 3j, th2: 2}) - - expected_bound_operator = SummedOp([3j * X, (2 + 0j) * Y]) - self.assertEqual(bound_operator, expected_bound_operator) - - def test_qdrift(self): - """QDrift test""" - op = (2 * Z ^ Z) + (3 * X ^ X) - (4 * Y ^ Y) + (0.5 * Z ^ I) - trotterization = QDrift().convert(op) - self.assertGreater(len(trotterization.oplist), 150) - last_coeff = None - # Check that all types are correct and all coefficients are equals - for op in trotterization.oplist: - self.assertIsInstance(op, (EvolvedOp, CircuitOp)) - if isinstance(op, EvolvedOp): - if last_coeff: - self.assertEqual(op.primitive.coeff, last_coeff) - else: - last_coeff = op.primitive.coeff - - def test_qdrift_summed_op(self): - """QDrift test for SummedOp""" - op = SummedOp( - [ - (2 * Z ^ Z), - (3 * X ^ X), - (-4 * Y ^ Y), - (0.5 * Z ^ I), - ] - ) - trotterization = QDrift().convert(op) - self.assertGreater(len(trotterization.oplist), 150) - last_coeff = None - # Check that all types are correct and all coefficients are equals - for op in trotterization.oplist: - self.assertIsInstance(op, (EvolvedOp, CircuitOp)) - if isinstance(op, EvolvedOp): - if last_coeff: - self.assertEqual(op.primitive.coeff, last_coeff) - else: - last_coeff = op.primitive.coeff - - def test_matrix_op_evolution(self): - """MatrixOp evolution test""" - op = ( - (-1.052373245772859 * I ^ I) - + (0.39793742484318045 * I ^ Z) - + (0.18093119978423156 * X ^ X) - + (-0.39793742484318045 * Z ^ I) - + (-0.01128010425623538 * Z ^ Z) * np.pi / 2 - ) - exp_mat = op.to_matrix_op().exp_i().to_matrix() - ref_mat = scipy.linalg.expm(-1j * op.to_matrix()) - np.testing.assert_array_almost_equal(ref_mat, exp_mat) - - def test_log_i(self): - """MatrixOp.log_i() test""" - op = ( - (-1.052373245772859 * I ^ I) - + (0.39793742484318045 * I ^ Z) - + (0.18093119978423156 * X ^ X) - + (-0.39793742484318045 * Z ^ I) - + (-0.01128010425623538 * Z ^ Z) * np.pi / 2 - ) - # Test with CircuitOp - log_exp_op = op.to_matrix_op().exp_i().log_i().to_pauli_op() - np.testing.assert_array_almost_equal(op.to_matrix(), log_exp_op.to_matrix()) - - # Test with MatrixOp - log_exp_op = op.to_matrix_op().exp_i().to_matrix_op().log_i().to_pauli_op() - np.testing.assert_array_almost_equal(op.to_matrix(), log_exp_op.to_matrix()) - - # Test with PauliOp - log_exp_op = op.to_matrix_op().exp_i().to_pauli_op().log_i().to_pauli_op() - np.testing.assert_array_almost_equal(op.to_matrix(), log_exp_op.to_matrix()) - - # Test with EvolvedOp - log_exp_op = op.exp_i().to_pauli_op().log_i().to_pauli_op() - np.testing.assert_array_almost_equal(op.to_matrix(), log_exp_op.to_matrix()) - - # Test with proper ListOp - op = ListOp( - [ - (0.39793742484318045 * I ^ Z), - (0.18093119978423156 * X ^ X), - (-0.39793742484318045 * Z ^ I), - (-0.01128010425623538 * Z ^ Z) * np.pi / 2, - ] - ) - log_exp_op = op.to_matrix_op().exp_i().to_matrix_op().log_i().to_pauli_op() - np.testing.assert_array_almost_equal(op.to_matrix(), log_exp_op.to_matrix()) - - def test_matrix_op_parameterized_evolution(self): - """parameterized MatrixOp evolution test""" - theta = Parameter("θ") - op = ( - (-1.052373245772859 * I ^ I) - + (0.39793742484318045 * I ^ Z) - + (0.18093119978423156 * X ^ X) - + (-0.39793742484318045 * Z ^ I) - + (-0.01128010425623538 * Z ^ Z) - ) - op = op * theta - wf = (op.to_matrix_op().exp_i()) @ CX @ (H ^ I) @ Zero - self.assertIn(theta, wf.to_circuit().parameters) - - op = op.assign_parameters({theta: 1}) - exp_mat = op.to_matrix_op().exp_i().to_matrix() - ref_mat = scipy.linalg.expm(-1j * op.to_matrix()) - np.testing.assert_array_almost_equal(ref_mat, exp_mat) - - wf = wf.assign_parameters({theta: 3}) - self.assertNotIn(theta, wf.to_circuit().parameters) - - def test_mixed_evolution(self): - """bind parameters test""" - thetas = ParameterVector("θ", length=6) - op = ( - (thetas[1] * (I ^ Z).to_matrix_op()) - + (thetas[2] * (X ^ X)).to_matrix_op() - + (thetas[3] * Z ^ I) - + (thetas[4] * Y ^ Z).to_circuit_op() - + (thetas[5] * (Z ^ I).to_circuit_op()) - ) - op = thetas[0] * op - evolution = PauliTrotterEvolution(trotter_mode="trotter", reps=1) - # wf = (Pl^Pl) + (Ze^Ze) - wf = (op).exp_i() @ CX @ (H ^ I) @ Zero - wf = wf.assign_parameters({thetas: np.arange(10, 16)}) - mean = evolution.convert(wf) - circuit_params = mean.to_circuit().parameters - # Check that the no parameters are in the circuit - for p in thetas[1:]: - self.assertNotIn(p, circuit_params) - - def test_reps(self): - """Test reps and order params in Trotterization""" - reps = 7 - trotter = Trotter(reps=reps) - self.assertEqual(trotter.reps, reps) - - order = 5 - suzuki = Suzuki(reps=reps, order=order) - self.assertEqual(suzuki.reps, reps) - self.assertEqual(suzuki.order, order) - - qdrift = QDrift(reps=reps) - self.assertEqual(qdrift.reps, reps) - - def test_suzuki_directly(self): - """Test for Suzuki converter""" - operator = X + Z - - evo = Suzuki() - evolution = evo.convert(operator) - - matrix = np.array( - [[0.29192658 - 0.45464871j, -0.84147098j], [-0.84147098j, 0.29192658 + 0.45464871j]] - ) - np.testing.assert_array_almost_equal(evolution.to_matrix(), matrix) - - def test_evolved_op_to_instruction(self): - """Test calling `to_instruction` on a plain EvolvedOp. - - Regression test of Qiskit/qiskit-terra#8025. - """ - op = EvolvedOp(0.5 * X) - circuit = op.to_instruction() - - unitary = scipy.linalg.expm(-0.5j * X.to_matrix()) - expected = UnitaryGate(unitary) - - self.assertEqual(circuit, expected) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_expectation_factory.py b/test/python/opflow/test_expectation_factory.py deleted file mode 100644 index f03a8517733d..000000000000 --- a/test/python/opflow/test_expectation_factory.py +++ /dev/null @@ -1,39 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test the expectation factory.""" - -import unittest -from test.python.opflow import QiskitOpflowTestCase - -from qiskit.opflow import PauliExpectation, AerPauliExpectation, ExpectationFactory, Z, I, X -from qiskit.utils import optionals - - -class TestExpectationFactory(QiskitOpflowTestCase): - """Tests for the expectation factory.""" - - @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required to run this test") - def test_aer_simulator_pauli_sum(self): - """Test expectation selection with Aer's qasm_simulator.""" - from qiskit_aer import AerSimulator - - backend = AerSimulator() - op = 0.2 * (X ^ X) + 0.1 * (Z ^ I) - with self.assertWarns(DeprecationWarning): - with self.subTest("Defaults"): - expectation = ExpectationFactory.build(op, backend, include_custom=False) - self.assertIsInstance(expectation, PauliExpectation) - - with self.subTest("Include custom"): - expectation = ExpectationFactory.build(op, backend, include_custom=True) - self.assertIsInstance(expectation, AerPauliExpectation) diff --git a/test/python/opflow/test_gradients.py b/test/python/opflow/test_gradients.py deleted file mode 100644 index b0fee1233bf5..000000000000 --- a/test/python/opflow/test_gradients.py +++ /dev/null @@ -1,1618 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# ============================================================================= - -"""Test Quantum Gradient Framework""" - -import unittest -import warnings -from test.python.opflow import QiskitOpflowTestCase -from itertools import product -import numpy as np -from ddt import ddt, data, idata, unpack - -from qiskit import QuantumCircuit, QuantumRegister, BasicAer -from qiskit.test import slow_test -from qiskit.utils import QuantumInstance -from qiskit.exceptions import MissingOptionalLibraryError -from qiskit.utils import algorithm_globals -from qiskit.algorithms import VQE -from qiskit.algorithms.optimizers import CG -from qiskit.opflow import ( - I, - X, - Y, - Z, - StateFn, - CircuitStateFn, - ListOp, - CircuitSampler, - TensoredOp, - SummedOp, -) -from qiskit.opflow.gradients import Gradient, NaturalGradient, Hessian -from qiskit.opflow.gradients.qfi import QFI -from qiskit.opflow.gradients.circuit_gradients import LinComb -from qiskit.opflow.gradients.circuit_qfis import LinCombFull, OverlapBlockDiag, OverlapDiag -from qiskit.circuit import Parameter -from qiskit.circuit import ParameterVector -from qiskit.circuit.library import RealAmplitudes, EfficientSU2 -from qiskit.utils import optionals - -if optionals.HAS_JAX: - import jax.numpy as jnp - - -@ddt -class TestGradients(QiskitOpflowTestCase): - """Test Qiskit Gradient Framework""" - - def setUp(self): - super().setUp() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.random_seed = 50 - - @data("lin_comb", "param_shift", "fin_diff") - def test_gradient_p(self, method): - """Test the state gradient for p - |psi> = 1/sqrt(2)[[1, exp(ia)]] - Tr(|psi>/da = - 0.5 sin(a) - """ - ham = 0.5 * X - 1 * Z - a = Parameter("a") - params = a - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.p(a, q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - - state_grad = Gradient(grad_method=method).convert(operator=op, params=params) - values_dict = [{a: np.pi / 4}, {a: 0}, {a: np.pi / 2}] - correct_values = [-0.5 / np.sqrt(2), 0, -0.5] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_gradient_u(self, method): - """Test the state gradient for U - Tr(|psi>/da = - 0.5 sin(a) - 1 cos(a)sin(b) - d/db = - 1 sin(a)cos(b) - """ - - ham = 0.5 * X - 1 * Z - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - - state_grad = Gradient(grad_method=method).convert(operator=op, params=params) - values_dict = [ - {a: np.pi / 4, b: np.pi}, - {params[0]: np.pi / 4, params[1]: np.pi / 4}, - {params[0]: np.pi / 2, params[1]: np.pi / 4}, - ] - correct_values = [ - [-0.5 / np.sqrt(2), 1 / np.sqrt(2)], - [-0.5 / np.sqrt(2) - 0.5, -1 / 2.0], - [-0.5, -1 / np.sqrt(2)], - ] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_state_gradient2(self, method): - """Test the state gradient 2 - - Tr(|psi>/da = - 0.5 sin(a) - 2 cos(a)sin(a) - """ - ham = 0.5 * X - 1 * Z - a = Parameter("a") - # b = Parameter('b') - params = [a] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(a, q[0]) - qc.rx(a, q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - - state_grad = Gradient(grad_method=method).convert(operator=op, params=params) - values_dict = [{a: np.pi / 4}, {a: 0}, {a: np.pi / 2}] - correct_values = [-1.353553, -0, -0.5] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_state_gradient3(self, method): - """Test the state gradient 3 - - Tr(|psi>/da = - 0.5 sin(a) - 1 cos(a)sin(cos(a)+1) + 1 sin^2(a)cos(cos(a)+1) - """ - ham = 0.5 * X - 1 * Z - a = Parameter("a") - # b = Parameter('b') - params = a - c = np.cos(a) + 1 - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(a, q[0]) - qc.rx(c, q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - - state_grad = Gradient(grad_method=method).convert(operator=op, params=params) - values_dict = [{a: np.pi / 4}, {a: 0}, {a: np.pi / 2}] - correct_values = [-1.1220, -0.9093, 0.0403] - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_state_gradient4(self, method): - """Test the state gradient 4 - Tr(|psi>/da0 = - 0.5 sin(a0) - 1 cos(a0)sin(a1) - d/da1 = - 1 sin(a0)cos(a1) - """ - - ham = 0.5 * X - 1 * Z - a = ParameterVector("a", 2) - params = a - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - - state_grad = Gradient(grad_method=method).convert(operator=op, params=params) - values_dict = [ - {a: [np.pi / 4, np.pi]}, - {a: [np.pi / 4, np.pi / 4]}, - {a: [np.pi / 2, np.pi / 4]}, - ] - correct_values = [ - [-0.5 / np.sqrt(2), 1 / np.sqrt(2)], - [-0.5 / np.sqrt(2) - 0.5, -1 / 2.0], - [-0.5, -1 / np.sqrt(2)], - ] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_state_hessian(self, method): - """Test the state Hessian - - Tr(|psi>/da^2 = - 0.5 cos(a) + 1 sin(a)sin(b) - d^2/dbda = - 1 cos(a)cos(b) - d^2/dbda = - 1 cos(a)cos(b) - d^2/db^2 = + 1 sin(a)sin(b) - """ - - ham = 0.5 * X - 1 * Z - params = ParameterVector("a", 2) - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - state_hess = Hessian(hess_method=method).convert(operator=op) - - values_dict = [ - {params[0]: np.pi / 4, params[1]: np.pi}, - {params[0]: np.pi / 4, params[1]: np.pi / 4}, - ] - correct_values = [ - [[-0.5 / np.sqrt(2), 1 / np.sqrt(2)], [1 / np.sqrt(2), 0]], - [[-0.5 / np.sqrt(2) + 0.5, -1 / 2.0], [-1 / 2.0, 0.5]], - ] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_hess.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @unittest.skipIf(not optionals.HAS_JAX, "Skipping test due to missing jax module.") - @data("lin_comb", "param_shift", "fin_diff") - def test_state_hessian_custom_combo_fn(self, method): - """Test the state Hessian with on an operator which includes - a user-defined combo_fn. - - Tr(|psi>/da^2 = - 0.5 cos(a) + 1 sin(a)sin(b) - d^2/dbda = - 1 cos(a)cos(b) - d^2/dbda = - 1 cos(a)cos(b) - d^2/db^2 = + 1 sin(a)sin(b) - """ - - ham = 0.5 * X - 1 * Z - a = Parameter("a") - b = Parameter("b") - params = [(a, a), (a, b), (b, b)] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(a, q[0]) - qc.rx(b, q[0]) - - op = ListOp( - [~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0)], - combo_fn=lambda x: x[0] ** 3 + 4 * x[0], - ) - state_hess = Hessian(hess_method=method).convert(operator=op, params=params) - - values_dict = [ - {a: np.pi / 4, b: np.pi}, - {a: np.pi / 4, b: np.pi / 4}, - {a: np.pi / 2, b: np.pi / 4}, - ] - - correct_values = [ - [-1.28163104, 2.56326208, 1.06066017], - [-0.04495626, -2.40716991, 1.8125], - [2.82842712, -1.5, 1.76776695], - ] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_hess.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_prob_grad(self, method): - """Test the probability gradient - - dp0/da = cos(a)sin(b) / 2 - dp1/da = - cos(a)sin(b) / 2 - dp0/db = sin(a)cos(b) / 2 - dp1/db = - sin(a)cos(b) / 2 - """ - - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - - op = CircuitStateFn(primitive=qc, coeff=1.0) - - prob_grad = Gradient(grad_method=method).convert(operator=op, params=params) - values_dict = [ - {a: np.pi / 4, b: 0}, - {params[0]: np.pi / 4, params[1]: np.pi / 4}, - {params[0]: np.pi / 2, params[1]: np.pi}, - ] - correct_values = [ - [[0, 0], [1 / (2 * np.sqrt(2)), -1 / (2 * np.sqrt(2))]], - [[1 / 4, -1 / 4], [1 / 4, -1 / 4]], - [[0, 0], [-1 / 2, 1 / 2]], - ] - for i, value_dict in enumerate(values_dict): - for j, prob_grad_result in enumerate(prob_grad.assign_parameters(value_dict).eval()): - np.testing.assert_array_almost_equal( - prob_grad_result, correct_values[i][j], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_prob_hess(self, method): - """Test the probability Hessian using linear combination of unitaries method - - d^2p0/da^2 = - sin(a)sin(b) / 2 - d^2p1/da^2 = sin(a)sin(b) / 2 - d^2p0/dadb = cos(a)cos(b) / 2 - d^2p1/dadb = - cos(a)cos(b) / 2 - """ - - a = Parameter("a") - b = Parameter("b") - params = [(a, a), (a, b)] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(a, q[0]) - qc.rx(b, q[0]) - - op = CircuitStateFn(primitive=qc, coeff=1.0) - - prob_hess = Hessian(hess_method=method).convert(operator=op, params=params) - values_dict = [{a: np.pi / 4, b: 0}, {a: np.pi / 4, b: np.pi / 4}, {a: np.pi / 2, b: np.pi}] - correct_values = [ - [[0, 0], [1 / (2 * np.sqrt(2)), -1 / (2 * np.sqrt(2))]], - [[-1 / 4, 1 / 4], [1 / 4, -1 / 4]], - [[0, 0], [0, 0]], - ] - for i, value_dict in enumerate(values_dict): - for j, prob_hess_result in enumerate(prob_hess.assign_parameters(value_dict).eval()): - np.testing.assert_array_almost_equal( - prob_hess_result, correct_values[i][j], decimal=1 - ) - - @idata( - product( - ["lin_comb", "param_shift", "fin_diff"], - [None, "lasso", "ridge", "perturb_diag", "perturb_diag_elements"], - ) - ) - @unpack - def test_natural_gradient(self, method, regularization): - """Test the natural gradient""" - try: - for params in (ParameterVector("a", 2), [Parameter("a"), Parameter("b")]): - ham = 0.5 * X - 1 * Z - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - nat_grad = NaturalGradient( - grad_method=method, regularization=regularization - ).convert(operator=op) - values_dict = [{params[0]: np.pi / 4, params[1]: np.pi / 2}] - - # reference values obtained by classically computing the natural gradients - correct_values = [[-3.26, 1.63]] if regularization == "ridge" else [[-4.24, 0]] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - nat_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - - def test_natural_gradient2(self): - """Test the natural gradient 2""" - with self.assertRaises(TypeError): - _ = NaturalGradient().convert(None, None) - - @idata( - zip( - ["lin_comb_full", "overlap_block_diag", "overlap_diag"], - [LinCombFull, OverlapBlockDiag, OverlapDiag], - ) - ) - @unpack - def test_natural_gradient3(self, qfi_method, circuit_qfi): - """Test the natural gradient 3""" - nat_grad = NaturalGradient(qfi_method=qfi_method) - self.assertIsInstance(nat_grad.qfi_method, circuit_qfi) - - @idata( - product( - ["lin_comb", "param_shift", "fin_diff"], - ["lin_comb_full", "overlap_block_diag", "overlap_diag"], - [None, "ridge", "perturb_diag", "perturb_diag_elements"], - ) - ) - @unpack - def test_natural_gradient4(self, grad_method, qfi_method, regularization): - """Test the natural gradient 4""" - - # Avoid regularization = lasso intentionally because it does not converge - try: - ham = 0.5 * X - 1 * Z - a = Parameter("a") - params = a - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(a, q[0]) - - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - nat_grad = NaturalGradient( - grad_method=grad_method, qfi_method=qfi_method, regularization=regularization - ).convert(operator=op, params=params) - values_dict = [{a: np.pi / 4}] - correct_values = [[0.0]] if regularization == "ridge" else [[-1.41421342]] - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - nat_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=3 - ) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - - def test_gradient_p_imag(self): - """Test the imaginary state gradient for p - |psi(a)> = 1/sqrt(2)[[1, exp(ia)]] - = iexp(-ia)/2 <1|H(|0>+exp(ia)|1>) - Im() = 0.5 cos(a). - """ - ham = X - a = Parameter("a") - params = a - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.p(a, q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - - state_grad = LinComb(aux_meas_op=(-1) * Y).convert(operator=op, params=params) - values_dict = [{a: np.pi / 4}, {a: 0}, {a: np.pi / 2}] - correct_values = [1 / np.sqrt(2), 1, 0] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - def test_qfi_p_imag(self): - """Test the imaginary state QFI for RXRY""" - x = Parameter("x") - y = Parameter("y") - circuit = QuantumCircuit(1) - circuit.ry(y, 0) - circuit.rx(x, 0) - state = StateFn(circuit) - - dx = ( - lambda x, y: (-1) - * 0.5j - * np.array( - [ - [ - -1j * np.sin(x / 2) * np.cos(y / 2) + np.cos(x / 2) * np.sin(y / 2), - np.cos(x / 2) * np.cos(y / 2) - 1j * np.sin(x / 2) * np.sin(y / 2), - ] - ] - ) - ) - dy = ( - lambda x, y: (-1) - * 0.5j - * np.array( - [ - [ - -1j * np.cos(x / 2) * np.sin(y / 2) + np.sin(x / 2) * np.cos(y / 2), - 1j * np.cos(x / 2) * np.cos(y / 2) - 1 * np.sin(x / 2) * np.sin(y / 2), - ] - ] - ) - ) - - state_grad = LinCombFull(aux_meas_op=-1 * Y, phase_fix=False).convert( - operator=state, params=[x, y] - ) - values_dict = [{x: 0, y: np.pi / 4}, {x: 0, y: np.pi / 2}, {x: np.pi / 2, y: 0}] - - for value_dict in values_dict: - x_ = list(value_dict.values())[0] - y_ = list(value_dict.values())[1] - correct_values = [ - [ - 4 * np.imag(np.dot(dx(x_, y_), np.conj(np.transpose(dx(x_, y_))))[0][0]), - 4 * np.imag(np.dot(dy(x_, y_), np.conj(np.transpose(dx(x_, y_))))[0][0]), - ], - [ - 4 * np.imag(np.dot(dy(x_, y_), np.conj(np.transpose(dx(x_, y_))))[0][0]), - 4 * np.imag(np.dot(dy(x_, y_), np.conj(np.transpose(dy(x_, y_))))[0][0]), - ], - ] - - np.testing.assert_array_almost_equal( - state_grad.assign_parameters(value_dict).eval(), correct_values, decimal=3 - ) - - @unittest.skipIf(not optionals.HAS_JAX, "Skipping test due to missing jax module.") - @idata(product(["lin_comb", "param_shift", "fin_diff"], [True, False])) - @unpack - def test_jax_chain_rule(self, method: str, autograd: bool): - """Test the chain rule functionality using Jax - - d/d = 2 - d/d = - sin() - = Tr(|psi> = Tr(|psi>/da = d/d d/da + d/d d/da = - 2 cos(a)sin(a) - - sin(sin(a)sin(b)) * cos(a)sin(b) - d/db = d/d d/db + d/d d/db = - sin(sin(a)sin(b)) * sin(a)cos(b) - """ - - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - - def combo_fn(x): - return jnp.power(x[0], 2) + jnp.cos(x[1]) - - def grad_combo_fn(x): - return np.array([2 * x[0], -np.sin(x[1])]) - - op = ListOp( - [ - ~StateFn(X) @ CircuitStateFn(primitive=qc, coeff=1.0), - ~StateFn(Z) @ CircuitStateFn(primitive=qc, coeff=1.0), - ], - combo_fn=combo_fn, - grad_combo_fn=None if autograd else grad_combo_fn, - ) - - state_grad = Gradient(grad_method=method).convert(operator=op, params=params) - values_dict = [ - {a: np.pi / 4, b: np.pi}, - {params[0]: np.pi / 4, params[1]: np.pi / 4}, - {params[0]: np.pi / 2, params[1]: np.pi / 4}, - ] - correct_values = [[-1.0, 0.0], [-1.2397, -0.2397], [0, -0.45936]] - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_grad_combo_fn_chain_rule(self, method): - """Test the chain rule for a custom gradient combo function.""" - np.random.seed(2) - - def combo_fn(x): - amplitudes = x[0].primitive.data - pdf = np.multiply(amplitudes, np.conj(amplitudes)) - return np.sum(np.log(pdf)) / (-len(amplitudes)) - - def grad_combo_fn(x): - amplitudes = x[0].primitive.data - pdf = np.multiply(amplitudes, np.conj(amplitudes)) - grad = [] - for prob in pdf: - grad += [-1 / prob] - return grad - - qc = RealAmplitudes(2, reps=1) - grad_op = ListOp([StateFn(qc.decompose())], combo_fn=combo_fn, grad_combo_fn=grad_combo_fn) - grad = Gradient(grad_method=method).convert(grad_op) - - value_dict = dict(zip(qc.ordered_parameters, np.random.rand(len(qc.ordered_parameters)))) - correct_values = [ - [(-0.16666259133549044 + 0j)], - [(-7.244949702732864 + 0j)], - [(-2.979791752749964 + 0j)], - [(-5.310186078432614 + 0j)], - ] - np.testing.assert_array_almost_equal( - grad.assign_parameters(value_dict).eval(), correct_values - ) - - def test_grad_combo_fn_chain_rule_nat_grad(self): - """Test the chain rule for a custom gradient combo function.""" - np.random.seed(2) - - def combo_fn(x): - amplitudes = x[0].primitive.data - pdf = np.multiply(amplitudes, np.conj(amplitudes)) - return np.sum(np.log(pdf)) / (-len(amplitudes)) - - def grad_combo_fn(x): - amplitudes = x[0].primitive.data - pdf = np.multiply(amplitudes, np.conj(amplitudes)) - grad = [] - for prob in pdf: - grad += [-1 / prob] - return grad - - try: - qc = RealAmplitudes(2, reps=1) - grad_op = ListOp( - [StateFn(qc.decompose())], combo_fn=combo_fn, grad_combo_fn=grad_combo_fn - ) - grad = NaturalGradient(grad_method="lin_comb", regularization="ridge").convert( - grad_op, qc.ordered_parameters - ) - value_dict = dict( - zip(qc.ordered_parameters, np.random.rand(len(qc.ordered_parameters))) - ) - correct_values = [[0.20777236], [-18.92560338], [-15.89005475], [-10.44002031]] - np.testing.assert_array_almost_equal( - grad.assign_parameters(value_dict).eval(), correct_values, decimal=3 - ) - except MissingOptionalLibraryError as ex: - self.skipTest(str(ex)) - - @data("lin_comb", "param_shift", "fin_diff") - def test_operator_coefficient_gradient(self, method): - """Test the operator coefficient gradient - - Tr( | psi > < psi | Z) = sin(a)sin(b) - Tr( | psi > < psi | X) = cos(a) - """ - a = Parameter("a") - b = Parameter("b") - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(a, q[0]) - qc.rx(b, q[0]) - - coeff_0 = Parameter("c_0") - coeff_1 = Parameter("c_1") - ham = coeff_0 * X + coeff_1 * Z - op = StateFn(ham, is_measurement=True) @ CircuitStateFn(primitive=qc, coeff=1.0) - gradient_coeffs = [coeff_0, coeff_1] - coeff_grad = Gradient(grad_method=method).convert(op, gradient_coeffs) - values_dict = [ - {coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi}, - {coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi / 4}, - ] - correct_values = [[1 / np.sqrt(2), 0], [1 / np.sqrt(2), 1 / 2]] - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - coeff_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_operator_coefficient_hessian(self, method): - """Test the operator coefficient hessian - - = Tr( | psi > < psi | Z) = sin(a)sin(b) - = Tr( | psi > < psi | X) = cos(a) - d/dc_0 = 2 * c_0 * + c_1 * - d/dc_1 = c_0 * - d^2/dc_0^2 = 2 * - d^2/dc_0dc_1 = - d^2/dc_1dc_0 = - d^2/dc_1^2 = 0 - """ - a = Parameter("a") - b = Parameter("b") - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(a, q[0]) - qc.rx(b, q[0]) - - coeff_0 = Parameter("c_0") - coeff_1 = Parameter("c_1") - ham = coeff_0 * coeff_0 * X + coeff_1 * coeff_0 * Z - op = StateFn(ham, is_measurement=True) @ CircuitStateFn(primitive=qc, coeff=1.0) - gradient_coeffs = [(coeff_0, coeff_0), (coeff_0, coeff_1), (coeff_1, coeff_1)] - coeff_grad = Hessian(hess_method=method).convert(op, gradient_coeffs) - values_dict = [ - {coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi}, - {coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi / 4}, - ] - - correct_values = [[2 / np.sqrt(2), 0, 0], [2 / np.sqrt(2), 1 / 2, 0]] - - for i, value_dict in enumerate(values_dict): - np.testing.assert_array_almost_equal( - coeff_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_circuit_sampler(self, method): - """Test the gradient with circuit sampler - - Tr(|psi>/da = - 0.5 sin(a) - 1 cos(a)sin(b) - d/db = - 1 sin(a)cos(b) - """ - - ham = 0.5 * X - 1 * Z - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) - - shots = 8000 - if method == "fin_diff": - np.random.seed(8) - state_grad = Gradient(grad_method=method, epsilon=shots ** (-1 / 6.0)).convert( - operator=op - ) - else: - state_grad = Gradient(grad_method=method).convert(operator=op) - values_dict = [ - {a: np.pi / 4, b: np.pi}, - {params[0]: np.pi / 4, params[1]: np.pi / 4}, - {params[0]: np.pi / 2, params[1]: np.pi / 4}, - ] - correct_values = [ - [-0.5 / np.sqrt(2), 1 / np.sqrt(2)], - [-0.5 / np.sqrt(2) - 0.5, -1 / 2.0], - [-0.5, -1 / np.sqrt(2)], - ] - - backend = BasicAer.get_backend("qasm_simulator") - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance(backend=backend, shots=shots) - - with self.assertWarns(DeprecationWarning): - for i, value_dict in enumerate(values_dict): - sampler = CircuitSampler(backend=q_instance).convert( - state_grad, params={k: [v] for k, v in value_dict.items()} - ) - np.testing.assert_array_almost_equal( - sampler.eval()[0], correct_values[i], decimal=1 - ) - - @data("lin_comb", "param_shift", "fin_diff") - def test_circuit_sampler2(self, method): - """Test the probability gradient with the circuit sampler - - dp0/da = cos(a)sin(b) / 2 - dp1/da = - cos(a)sin(b) / 2 - dp0/db = sin(a)cos(b) / 2 - dp1/db = - sin(a)cos(b) / 2 - """ - - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - - op = CircuitStateFn(primitive=qc, coeff=1.0) - - shots = 8000 - if method == "fin_diff": - np.random.seed(8) - prob_grad = Gradient(grad_method=method, epsilon=shots ** (-1 / 6.0)).convert( - operator=op, params=params - ) - else: - prob_grad = Gradient(grad_method=method).convert(operator=op, params=params) - values_dict = [ - {a: [np.pi / 4], b: [0]}, - {params[0]: [np.pi / 4], params[1]: [np.pi / 4]}, - {params[0]: [np.pi / 2], params[1]: [np.pi]}, - ] - correct_values = [ - [[0, 0], [1 / (2 * np.sqrt(2)), -1 / (2 * np.sqrt(2))]], - [[1 / 4, -1 / 4], [1 / 4, -1 / 4]], - [[0, 0], [-1 / 2, 1 / 2]], - ] - - backend = BasicAer.get_backend("qasm_simulator") - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance(backend=backend, shots=shots) - - with self.assertWarns(DeprecationWarning): - for i, value_dict in enumerate(values_dict): - sampler = CircuitSampler(backend=q_instance).convert(prob_grad, params=value_dict) - result = sampler.eval()[0] - self.assertTrue(np.allclose(result[0].toarray(), correct_values[i][0], atol=0.1)) - self.assertTrue(np.allclose(result[1].toarray(), correct_values[i][1], atol=0.1)) - - @idata(["statevector_simulator", "qasm_simulator"]) - def test_gradient_wrapper(self, backend_type): - """Test the gradient wrapper for probability gradients - dp0/da = cos(a)sin(b) / 2 - dp1/da = - cos(a)sin(b) / 2 - dp0/db = sin(a)cos(b) / 2 - dp1/db = - sin(a)cos(b) / 2 - """ - method = "param_shift" - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - - op = CircuitStateFn(primitive=qc, coeff=1.0) - - shots = 8000 - backend = BasicAer.get_backend(backend_type) - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - backend=backend, shots=shots, seed_simulator=2, seed_transpiler=2 - ) - - if method == "fin_diff": - np.random.seed(8) - prob_grad = Gradient(grad_method=method, epsilon=shots ** (-1 / 6.0)).gradient_wrapper( - operator=op, bind_params=params, backend=q_instance - ) - else: - - with self.assertWarns(DeprecationWarning): - prob_grad = Gradient(grad_method=method).gradient_wrapper( - operator=op, bind_params=params, backend=q_instance - ) - - values = [[np.pi / 4, 0], [np.pi / 4, np.pi / 4], [np.pi / 2, np.pi]] - correct_values = [ - [[0, 0], [1 / (2 * np.sqrt(2)), -1 / (2 * np.sqrt(2))]], - [[1 / 4, -1 / 4], [1 / 4, -1 / 4]], - [[0, 0], [-1 / 2, 1 / 2]], - ] - with self.assertWarns(DeprecationWarning): - for i, value in enumerate(values): - result = prob_grad(value) - if backend_type == "qasm_simulator": # sparse result - result = [result[0].toarray(), result[1].toarray()] - - self.assertTrue(np.allclose(result[0], correct_values[i][0], atol=0.1)) - self.assertTrue(np.allclose(result[1], correct_values[i][1], atol=0.1)) - - @data(("statevector_simulator", 1e-7), ("qasm_simulator", 2e-1)) - @unpack - def test_gradient_wrapper2(self, backend_type, atol): - """Test the gradient wrapper for gradients checking that statevector and qasm gives the - same results - - dp0/da = cos(a)sin(b) / 2 - dp1/da = - cos(a)sin(b) / 2 - dp0/db = sin(a)cos(b) / 2 - dp1/db = - sin(a)cos(b) / 2 - """ - method = "lin_comb" - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - qc = QuantumCircuit(2) - qc.h(1) - qc.h(0) - qc.sdg(1) - qc.cz(0, 1) - qc.ry(params[0], 0) - qc.rz(params[1], 0) - qc.h(1) - - obs = (Z ^ X) - (Y ^ Y) - op = StateFn(obs, is_measurement=True) @ CircuitStateFn(primitive=qc) - - shots = 8192 if backend_type == "qasm_simulator" else 1 - - values = [[0, np.pi / 2], [np.pi / 4, np.pi / 4], [np.pi / 3, np.pi / 9]] - correct_values = [[-4.0, 0], [-2.0, -4.82842712], [-0.68404029, -7.01396121]] - for i, value in enumerate(values): - backend = BasicAer.get_backend(backend_type) - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - backend=backend, shots=shots, seed_simulator=2, seed_transpiler=2 - ) - grad = NaturalGradient(grad_method=method).gradient_wrapper( - operator=op, bind_params=params, backend=q_instance - ) - result = grad(value) - self.assertTrue(np.allclose(result, correct_values[i], atol=atol)) - - @slow_test - def test_vqe(self): - """Test VQE with gradients""" - - method = "lin_comb" - backend = "qasm_simulator" - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - BasicAer.get_backend(backend), seed_simulator=79, seed_transpiler=2 - ) - - # Define the Hamiltonian - h2_hamiltonian = ( - -1.05 * (I ^ I) + 0.39 * (I ^ Z) - 0.39 * (Z ^ I) - 0.01 * (Z ^ Z) + 0.18 * (X ^ X) - ) - h2_energy = -1.85727503 - - # Define the Ansatz - wavefunction = QuantumCircuit(2) - params = ParameterVector("theta", length=8) - itr = iter(params) - wavefunction.ry(next(itr), 0) - wavefunction.ry(next(itr), 1) - wavefunction.rz(next(itr), 0) - wavefunction.rz(next(itr), 1) - wavefunction.cx(0, 1) - wavefunction.ry(next(itr), 0) - wavefunction.ry(next(itr), 1) - wavefunction.rz(next(itr), 0) - wavefunction.rz(next(itr), 1) - - # Conjugate Gradient algorithm - optimizer = CG(maxiter=10) - - grad = Gradient(grad_method=method) - - # Gradient callable - with self.assertWarns(DeprecationWarning): - vqe = VQE( - ansatz=wavefunction, optimizer=optimizer, gradient=grad, quantum_instance=q_instance - ) - result = vqe.compute_minimum_eigenvalue(operator=h2_hamiltonian) - np.testing.assert_almost_equal(result.optimal_value, h2_energy, decimal=0) - - def test_qfi_overlap_works_with_bound_parameters(self): - """Test all QFI methods work if the circuit contains a gate with bound parameters.""" - - x = Parameter("x") - circuit = QuantumCircuit(1) - circuit.ry(np.pi / 4, 0) - circuit.rx(x, 0) - state = StateFn(circuit) - - methods = ["lin_comb_full", "overlap_diag", "overlap_block_diag"] - reference = 0.5 - - for method in methods: - with self.subTest(method): - qfi = QFI(method) - value = np.real(qfi.convert(state, [x]).bind_parameters({x: 0.12}).eval()) - self.assertAlmostEqual(value[0][0], reference) - - -@ddt -class TestParameterGradients(QiskitOpflowTestCase): - """Test taking the gradient of parameter expressions.""" - - def test_grad(self): - """Test taking the gradient of parameter expressions.""" - x, y = Parameter("x"), Parameter("y") - with self.subTest("linear"): - expr = 2 * x + y - - grad = expr.gradient(x) - self.assertEqual(grad, 2) - - grad = expr.gradient(y) - self.assertEqual(grad, 1) - - with self.subTest("polynomial"): - expr = x * x * x - x * y + y * y - - grad = expr.gradient(x) - self.assertEqual(grad, 3 * x * x - y) - - grad = expr.gradient(y) - self.assertEqual(grad, -1 * x + 2 * y) - - def test_converted_to_float_if_bound(self): - """Test the gradient is a float when no free symbols are left.""" - x = Parameter("x") - expr = 2 * x + 1 - grad = expr.gradient(x) - self.assertIsInstance(grad, float) - - def test_converted_to_complex_if_bound(self): - """Test the gradient is a complex when no free symbols are left.""" - x = Parameter("x") - x2 = 1j * x - expr = 2 * x2 + 1 - grad = expr.gradient(x) - self.assertIsInstance(grad, complex) - - -@ddt -class TestQFI(QiskitOpflowTestCase): - """Tests for the quantum Fisher information.""" - - @data("lin_comb_full", "overlap_block_diag", "overlap_diag") - def test_qfi_simple(self, method): - """Test if the quantum fisher information calculation is correct for a simple test case. - - QFI = [[1, 0], [0, 1]] - [[0, 0], [0, cos^2(a)]] - """ - # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - # convert the circuit to a QFI object - op = CircuitStateFn(qc) - qfi = QFI(qfi_method=method).convert(operator=op) - - # test for different values - values_dict = [{a: np.pi / 4, b: 0.1}, {a: np.pi, b: 0.1}, {a: np.pi / 2, b: 0.1}] - correct_values = [[[1, 0], [0, 0.5]], [[1, 0], [0, 0]], [[1, 0], [0, 1]]] - - for i, value_dict in enumerate(values_dict): - actual = qfi.assign_parameters(value_dict).eval() - np.testing.assert_array_almost_equal(actual, correct_values[i], decimal=1) - - def test_qfi_phase_fix(self): - """Test the phase-fix argument in a QFI calculation - - QFI = [[1, 0], [0, 1]]. - """ - # create the circuit - a, b = Parameter("a"), Parameter("b") - qc = QuantumCircuit(1) - qc.h(0) - qc.rz(a, 0) - qc.rx(b, 0) - - # convert the circuit to a QFI object - op = CircuitStateFn(qc) - qfi = LinCombFull(phase_fix=False).convert(operator=op, params=[a, b]) - - # test for different values - value_dict = {a: np.pi / 4, b: 0.1} - correct_values = [[1, 0], [0, 1]] - - actual = qfi.assign_parameters(value_dict).eval() - np.testing.assert_array_almost_equal(actual, correct_values, decimal=2) - - def test_qfi_maxcut(self): - """Test the QFI for a simple MaxCut problem. - - This is interesting because it contains the same parameters in different gates. - """ - # create maxcut circuit for the hamiltonian - # H = (I ^ I ^ Z ^ Z) + (I ^ Z ^ I ^ Z) + (Z ^ I ^ I ^ Z) + (I ^ Z ^ Z ^ I) - - x = ParameterVector("x", 2) - ansatz = QuantumCircuit(4) - - # initial hadamard layer - ansatz.h(ansatz.qubits) - - # e^{iZZ} layers - def expiz(qubit0, qubit1): - ansatz.cx(qubit0, qubit1) - ansatz.rz(2 * x[0], qubit1) - ansatz.cx(qubit0, qubit1) - - expiz(2, 1) - expiz(3, 0) - expiz(2, 0) - expiz(1, 0) - - # mixer layer with RX gates - for i in range(ansatz.num_qubits): - ansatz.rx(2 * x[1], i) - - point = {x[0]: 0.4, x[1]: 0.69} - - # reference computed via finite difference - reference = np.array([[16.0, -5.551], [-5.551, 18.497]]) - - # QFI from gradient framework - qfi = QFI().convert(CircuitStateFn(ansatz), params=x[:]) - actual = np.array(qfi.bind_parameters(point).eval()).real - np.testing.assert_array_almost_equal(actual, reference, decimal=3) - - def test_qfi_circuit_shared_params(self): - """Test the QFI circuits for parameters shared across some gates.""" - # create the test circuit - x = Parameter("x") - circuit = QuantumCircuit(1) - circuit.rx(x, 0) - circuit.rx(x, 0) - - # construct the QFI circuits used in the evaluation - - circuit1 = QuantumCircuit(2) - circuit1.h(1) - circuit1.x(1) - circuit1.cx(1, 0) - circuit1.x(1) - circuit1.cx(1, 0) - # circuit1.rx(x, 0) # trimmed - # circuit1.rx(x, 0) # trimmed - circuit1.h(1) - - circuit2 = QuantumCircuit(2) - circuit2.h(1) - circuit2.x(1) - circuit2.cx(1, 0) - circuit2.x(1) - circuit2.rx(x, 0) - circuit2.cx(1, 0) - # circuit2.rx(x, 0) # trimmed - circuit2.h(1) - - circuit3 = QuantumCircuit(2) - circuit3.h(1) - circuit3.cx(1, 0) - circuit3.x(1) - circuit3.rx(x, 0) - circuit3.cx(1, 0) - # circuit3.rx(x, 0) # trimmed - circuit3.x(1) - circuit3.h(1) - - circuit4 = QuantumCircuit(2) - circuit4.h(1) - circuit4.rx(x, 0) - circuit4.x(1) - circuit4.cx(1, 0) - circuit4.x(1) - circuit4.cx(1, 0) - # circuit4.rx(x, 0) # trimmed - circuit4.h(1) - - # this naming and adding of register is required bc circuit's are only equal if the - # register have the same names - circuit5 = QuantumCircuit(2) - circuit5.h(1) - circuit5.sdg(1) - circuit5.cx(1, 0) - # circuit5.rx(x, 0) # trimmed - circuit5.h(1) - - circuit6 = QuantumCircuit(2) - circuit6.h(1) - circuit6.sdg(1) - circuit6.rx(x, 0) - circuit6.cx(1, 0) - circuit6.h(1) - - # compare - qfi = QFI().convert(StateFn(circuit), params=[x]) - - circuit_sets = ( - [circuit1, circuit2, circuit3, circuit4], - [circuit5, circuit6], - [circuit5, circuit6], - ) - list_ops = ( - qfi.oplist[0].oplist[0].oplist[:-1], - qfi.oplist[0].oplist[0].oplist[-1].oplist[0].oplist, - qfi.oplist[0].oplist[0].oplist[-1].oplist[1].oplist, - ) - - # compose both on the same circuit such that the comparison works - base = QuantumCircuit(2) - - for i, (circuit_set, list_op) in enumerate(zip(circuit_sets, list_ops)): - for j, (reference, composed_op) in enumerate(zip(circuit_set, list_op)): - with self.subTest(f"set {i} circuit {j}"): - primitive = composed_op[1].primitive - self.assertEqual(base.compose(primitive), base.compose(reference)) - - def test_overlap_qfi_bound_parameters(self): - """Test the overlap QFI works on a circuit with multi-parameter bound gates.""" - x = Parameter("x") - circuit = QuantumCircuit(1) - circuit.u(1, 2, 3, 0) - circuit.rx(x, 0) - - qfi = QFI("overlap_diag").convert(StateFn(circuit), [x]) - value = qfi.bind_parameters({x: 1}).eval()[0][0] - ref = 0.87737713 - self.assertAlmostEqual(value, ref) - - def test_overlap_qfi_raises_on_multiparam(self): - """Test the overlap QFI raises an appropriate error on multi-param unbound gates.""" - x = ParameterVector("x", 2) - circuit = QuantumCircuit(1) - circuit.u(x[0], x[1], 2, 0) - - with self.assertRaises(NotImplementedError): - _ = QFI("overlap_diag").convert(StateFn(circuit), [x]) - - def test_overlap_qfi_raises_on_unsupported_gate(self): - """Test the overlap QFI raises an appropriate error on multi-param unbound gates.""" - x = Parameter("x") - circuit = QuantumCircuit(1) - circuit.p(x, 0) - - with self.assertRaises(NotImplementedError): - _ = QFI("overlap_diag").convert(StateFn(circuit), [x]) - - @data(-Y, Z - 1j * Y) - def test_aux_meas_op(self, aux_meas_op): - """Test various auxiliary measurement operators for probability gradients with LinComb - Gradient. - - """ - - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - - op = CircuitStateFn(primitive=qc, coeff=1.0) - - shots = 10000 - - prob_grad = LinComb(aux_meas_op=aux_meas_op).convert(operator=op, params=params) - value_dicts = [{a: [np.pi / 4], b: [0]}, {a: [np.pi / 2], b: [np.pi / 4]}] - if aux_meas_op == -Y: - correct_values = [ - [[-0.5, 0.5], [-1 / (np.sqrt(2) * 2), -1 / (np.sqrt(2) * 2)]], - [[-1 / (np.sqrt(2) * 2), 1 / (np.sqrt(2) * 2)], [0, 0]], - ] - else: - correct_values = [ - [[-0.5j, 0.5j], [(1 - 1j) / (np.sqrt(2) * 2), (-1 - 1j) / (np.sqrt(2) * 2)]], - [ - [-1j / (np.sqrt(2) * 2), 1j / (np.sqrt(2) * 2)], - [1 / (np.sqrt(2) * 2), -1 / (np.sqrt(2) * 2)], - ], - ] - - for backend_type in ["qasm_simulator", "statevector_simulator"]: - - for j, value_dict in enumerate(value_dicts): - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - backend=BasicAer.get_backend(backend_type), shots=shots - ) - result = ( - CircuitSampler(backend=q_instance) - .convert(prob_grad, params=value_dict) - .eval()[0] - ) - if backend_type == "qasm_simulator": # sparse result - result = [result[0].toarray()[0], result[1].toarray()[0]] - for i, item in enumerate(result): - np.testing.assert_array_almost_equal(item, correct_values[j][i], decimal=1) - - def test_unsupported_aux_meas_op(self): - """Test error for unsupported auxiliary measurement operator in LinComb Gradient. - - dp0/da = cos(a)sin(b) / 2 - dp1/da = - cos(a)sin(b) / 2 - dp0/db = sin(a)cos(b) / 2 - dp1/db = - sin(a)cos(b) / 2 - """ - - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - q = QuantumRegister(1) - qc = QuantumCircuit(q) - qc.h(q) - qc.rz(params[0], q[0]) - qc.rx(params[1], q[0]) - - op = CircuitStateFn(primitive=qc, coeff=1.0) - - shots = 8000 - - aux_meas_op = X - - with self.assertRaises(ValueError): - prob_grad = LinComb(aux_meas_op=aux_meas_op).convert(operator=op, params=params) - value_dict = {a: [np.pi / 4], b: [0]} - - backend = BasicAer.get_backend("qasm_simulator") - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance(backend=backend, shots=shots) - CircuitSampler(backend=q_instance).convert(prob_grad, params=value_dict).eval() - - def test_nat_grad_error(self): - """Test the NaturalGradient throws an Error. - - dp0/da = cos(a)sin(b) / 2 - dp1/da = - cos(a)sin(b) / 2 - dp0/db = sin(a)cos(b) / 2 - dp1/db = - sin(a)cos(b) / 2 - """ - method = "lin_comb" - a = Parameter("a") - b = Parameter("b") - params = [a, b] - - qc = QuantumCircuit(2) - qc.h(1) - qc.h(0) - qc.sdg(1) - qc.cz(0, 1) - qc.ry(params[0], 0) - qc.rz(params[1], 0) - qc.h(1) - - obs = (Z ^ X) - (Y ^ Y) - op = StateFn(obs, is_measurement=True) @ CircuitStateFn(primitive=qc) - - backend_type = "qasm_simulator" - shots = 1 - value = [0, np.pi / 2] - - backend = BasicAer.get_backend(backend_type) - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - backend=backend, shots=shots, seed_simulator=2, seed_transpiler=2 - ) - - with self.assertWarns(DeprecationWarning): - grad = NaturalGradient(grad_method=method).gradient_wrapper( - operator=op, bind_params=params, backend=q_instance - ) - - with self.assertWarns(DeprecationWarning): - with self.assertRaises(ValueError): - grad(value) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_matrix_expectation.py b/test/python/opflow/test_matrix_expectation.py deleted file mode 100644 index 10448c3a64e1..000000000000 --- a/test/python/opflow/test_matrix_expectation.py +++ /dev/null @@ -1,184 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test MatrixExpectation""" - -import unittest -from test.python.opflow import QiskitOpflowTestCase -import itertools -import numpy as np - -from qiskit.utils import QuantumInstance -from qiskit.opflow import ( - X, - Y, - Z, - I, - CX, - H, - S, - ListOp, - Zero, - One, - Plus, - Minus, - StateFn, - MatrixExpectation, - CircuitSampler, -) -from qiskit import BasicAer - - -class TestMatrixExpectation(QiskitOpflowTestCase): - """Pauli Change of Basis Expectation tests.""" - - def setUp(self) -> None: - super().setUp() - self.seed = 97 - backend = BasicAer.get_backend("statevector_simulator") - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - backend, seed_simulator=self.seed, seed_transpiler=self.seed - ) - self.sampler = CircuitSampler(q_instance, attach_results=True) - - self.expect = MatrixExpectation() - - def test_pauli_expect_pair(self): - """pauli expect pair test""" - - op = Z ^ Z - # wf = (Pl^Pl) + (Ze^Ze) - wf = CX @ (H ^ I) @ Zero - converted_meas = self.expect.convert(~StateFn(op) @ wf) - self.assertAlmostEqual(converted_meas.eval(), 0, delta=0.1) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - self.assertAlmostEqual(sampled.eval(), 0, delta=0.1) - - def test_pauli_expect_single(self): - """pauli expect single test""" - - paulis = [Z, X, Y, I] - states = [Zero, One, Plus, Minus, S @ Plus, S @ Minus] - for pauli, state in itertools.product(paulis, states): - converted_meas = self.expect.convert(~StateFn(pauli) @ state) - matmulmean = state.adjoint().to_matrix() @ pauli.to_matrix() @ state.to_matrix() - self.assertAlmostEqual(converted_meas.eval(), matmulmean, delta=0.1) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - self.assertAlmostEqual(sampled.eval(), matmulmean, delta=0.1) - - def test_pauli_expect_op_vector(self): - """pauli expect op vector test""" - - paulis_op = ListOp([X, Y, Z, I]) - converted_meas = self.expect.convert(~StateFn(paulis_op)) - - with self.assertWarns(DeprecationWarning): - - plus_mean = converted_meas @ Plus - np.testing.assert_array_almost_equal(plus_mean.eval(), [1, 0, 0, 1], decimal=1) - sampled_plus = self.sampler.convert(plus_mean) - np.testing.assert_array_almost_equal(sampled_plus.eval(), [1, 0, 0, 1], decimal=1) - - minus_mean = converted_meas @ Minus - np.testing.assert_array_almost_equal(minus_mean.eval(), [-1, 0, 0, 1], decimal=1) - sampled_minus = self.sampler.convert(minus_mean) - np.testing.assert_array_almost_equal(sampled_minus.eval(), [-1, 0, 0, 1], decimal=1) - - zero_mean = converted_meas @ Zero - np.testing.assert_array_almost_equal(zero_mean.eval(), [0, 0, 1, 1], decimal=1) - sampled_zero = self.sampler.convert(zero_mean) - np.testing.assert_array_almost_equal(sampled_zero.eval(), [0, 0, 1, 1], decimal=1) - - sum_zero = (Plus + Minus) * (0.5**0.5) - sum_zero_mean = converted_meas @ sum_zero - np.testing.assert_array_almost_equal(sum_zero_mean.eval(), [0, 0, 1, 1], decimal=1) - sampled_zero = self.sampler.convert(sum_zero) - - np.testing.assert_array_almost_equal( - (converted_meas @ sampled_zero).eval(), [0, 0, 1, 1], decimal=1 - ) - - for i, op in enumerate(paulis_op.oplist): - mat_op = op.to_matrix() - np.testing.assert_array_almost_equal( - zero_mean.eval()[i], - Zero.adjoint().to_matrix() @ mat_op @ Zero.to_matrix(), - decimal=1, - ) - np.testing.assert_array_almost_equal( - plus_mean.eval()[i], - Plus.adjoint().to_matrix() @ mat_op @ Plus.to_matrix(), - decimal=1, - ) - np.testing.assert_array_almost_equal( - minus_mean.eval()[i], - Minus.adjoint().to_matrix() @ mat_op @ Minus.to_matrix(), - decimal=1, - ) - - def test_pauli_expect_state_vector(self): - """pauli expect state vector test""" - - states_op = ListOp([One, Zero, Plus, Minus]) - paulis_op = X - converted_meas = self.expect.convert(~StateFn(paulis_op) @ states_op) - np.testing.assert_array_almost_equal(converted_meas.eval(), [0, 0, 1, -1], decimal=1) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - np.testing.assert_array_almost_equal(sampled.eval(), [0, 0, 1, -1], decimal=1) - - # Small test to see if execution results are accessible - for composed_op in sampled: - self.assertIn("statevector", composed_op[1].execution_results) - - def test_pauli_expect_op_vector_state_vector(self): - """pauli expect op vector state vector test""" - - paulis_op = ListOp([X, Y, Z, I]) - states_op = ListOp([One, Zero, Plus, Minus]) - - valids = [[+0, 0, 1, -1], [+0, 0, 0, 0], [-1, 1, 0, -0], [+1, 1, 1, 1]] - converted_meas = self.expect.convert(~StateFn(paulis_op)) - np.testing.assert_array_almost_equal((converted_meas @ states_op).eval(), valids, decimal=1) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(states_op) - - np.testing.assert_array_almost_equal((converted_meas @ sampled).eval(), valids, decimal=1) - - def test_multi_representation_ops(self): - """Test observables with mixed representations""" - - mixed_ops = ListOp([X.to_matrix_op(), H, H + I, X]) - converted_meas = self.expect.convert(~StateFn(mixed_ops)) - - plus_mean = converted_meas @ Plus - - with self.assertWarns(DeprecationWarning): - sampled_plus = self.sampler.convert(plus_mean) - - np.testing.assert_array_almost_equal( - sampled_plus.eval(), [1, 0.5**0.5, (1 + 0.5**0.5), 1], decimal=1 - ) - - def test_matrix_expectation_non_hermite_op(self): - """Test MatrixExpectation for non hermitian operator""" - - exp = ~StateFn(1j * Z) @ One - self.assertEqual(self.expect.convert(exp).eval(), 1j) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_op_construction.py b/test/python/opflow/test_op_construction.py deleted file mode 100644 index a3dcb7dd6671..000000000000 --- a/test/python/opflow/test_op_construction.py +++ /dev/null @@ -1,1385 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Operator construction, including OpPrimitives and singletons.""" - - -import itertools -import unittest -from math import pi -from test.python.opflow import QiskitOpflowTestCase - -import numpy as np -import scipy -from ddt import data, ddt, unpack -from scipy.sparse import csr_matrix -from scipy.stats import unitary_group - -from qiskit import QiskitError, transpile -from qiskit.circuit import ( - Instruction, - Parameter, - ParameterVector, - QuantumCircuit, - QuantumRegister, -) -from qiskit.circuit.library import CZGate, ZGate -from qiskit.opflow import ( - CX, - CircuitOp, - CircuitStateFn, - ComposedOp, - DictStateFn, - EvolvedOp, - H, - I, - ListOp, - MatrixOp, - Minus, - OperatorBase, - OperatorStateFn, - OpflowError, - PauliOp, - PrimitiveOp, - SparseVectorStateFn, - StateFn, - SummedOp, - T, - TensoredOp, - VectorStateFn, - X, - Y, - Z, - Zero, -) -from qiskit.quantum_info import Operator, Pauli, Statevector - -# pylint: disable=invalid-name - - -@ddt -class TestOpConstruction(QiskitOpflowTestCase): - """Operator Construction tests.""" - - def test_pauli_primitives(self): - """from to file test""" - newop = X ^ Y ^ Z ^ I - self.assertEqual(newop.primitive, Pauli("XYZI")) - - kpower_op = (Y ^ 5) ^ (I ^ 3) - self.assertEqual(kpower_op.primitive, Pauli("YYYYYIII")) - - kpower_op2 = (Y ^ I) ^ 4 - self.assertEqual(kpower_op2.primitive, Pauli("YIYIYIYI")) - - # Check immutability - self.assertEqual(X.primitive, Pauli("X")) - self.assertEqual(Y.primitive, Pauli("Y")) - self.assertEqual(Z.primitive, Pauli("Z")) - self.assertEqual(I.primitive, Pauli("I")) - - def test_composed_eval(self): - """Test eval of ComposedOp""" - self.assertAlmostEqual(Minus.eval("1"), -(0.5**0.5)) - - def test_xz_compose_phase(self): - """Test phase composition""" - self.assertEqual((-1j * Y).eval("0").eval("0"), 0) - self.assertEqual((-1j * Y).eval("0").eval("1"), 1) - self.assertEqual((-1j * Y).eval("1").eval("0"), -1) - self.assertEqual((-1j * Y).eval("1").eval("1"), 0) - self.assertEqual((X @ Z).eval("0").eval("0"), 0) - self.assertEqual((X @ Z).eval("0").eval("1"), 1) - self.assertEqual((X @ Z).eval("1").eval("0"), -1) - self.assertEqual((X @ Z).eval("1").eval("1"), 0) - self.assertEqual((1j * Y).eval("0").eval("0"), 0) - self.assertEqual((1j * Y).eval("0").eval("1"), -1) - self.assertEqual((1j * Y).eval("1").eval("0"), 1) - self.assertEqual((1j * Y).eval("1").eval("1"), 0) - self.assertEqual((Z @ X).eval("0").eval("0"), 0) - self.assertEqual((Z @ X).eval("0").eval("1"), -1) - self.assertEqual((Z @ X).eval("1").eval("0"), 1) - self.assertEqual((Z @ X).eval("1").eval("1"), 0) - - def test_evals(self): - """evals test""" - # TODO: Think about eval names - self.assertEqual(Z.eval("0").eval("0"), 1) - self.assertEqual(Z.eval("1").eval("0"), 0) - self.assertEqual(Z.eval("0").eval("1"), 0) - self.assertEqual(Z.eval("1").eval("1"), -1) - self.assertEqual(X.eval("0").eval("0"), 0) - self.assertEqual(X.eval("1").eval("0"), 1) - self.assertEqual(X.eval("0").eval("1"), 1) - self.assertEqual(X.eval("1").eval("1"), 0) - self.assertEqual(Y.eval("0").eval("0"), 0) - self.assertEqual(Y.eval("1").eval("0"), -1j) - self.assertEqual(Y.eval("0").eval("1"), 1j) - self.assertEqual(Y.eval("1").eval("1"), 0) - - with self.assertRaises(ValueError): - Y.eval("11") - - with self.assertRaises(ValueError): - (X ^ Y).eval("1111") - - with self.assertRaises(ValueError): - Y.eval((X ^ X).to_matrix_op()) - - # Check that Pauli logic eval returns same as matrix logic - self.assertEqual(PrimitiveOp(Z.to_matrix()).eval("0").eval("0"), 1) - self.assertEqual(PrimitiveOp(Z.to_matrix()).eval("1").eval("0"), 0) - self.assertEqual(PrimitiveOp(Z.to_matrix()).eval("0").eval("1"), 0) - self.assertEqual(PrimitiveOp(Z.to_matrix()).eval("1").eval("1"), -1) - self.assertEqual(PrimitiveOp(X.to_matrix()).eval("0").eval("0"), 0) - self.assertEqual(PrimitiveOp(X.to_matrix()).eval("1").eval("0"), 1) - self.assertEqual(PrimitiveOp(X.to_matrix()).eval("0").eval("1"), 1) - self.assertEqual(PrimitiveOp(X.to_matrix()).eval("1").eval("1"), 0) - self.assertEqual(PrimitiveOp(Y.to_matrix()).eval("0").eval("0"), 0) - self.assertEqual(PrimitiveOp(Y.to_matrix()).eval("1").eval("0"), -1j) - self.assertEqual(PrimitiveOp(Y.to_matrix()).eval("0").eval("1"), 1j) - self.assertEqual(PrimitiveOp(Y.to_matrix()).eval("1").eval("1"), 0) - - pauli_op = Z ^ I ^ X ^ Y - mat_op = PrimitiveOp(pauli_op.to_matrix()) - full_basis = list(map("".join, itertools.product("01", repeat=pauli_op.num_qubits))) - for bstr1, bstr2 in itertools.product(full_basis, full_basis): - # print('{} {} {} {}'.format(bstr1, bstr2, pauli_op.eval(bstr1, bstr2), - # mat_op.eval(bstr1, bstr2))) - np.testing.assert_array_almost_equal( - pauli_op.eval(bstr1).eval(bstr2), mat_op.eval(bstr1).eval(bstr2) - ) - - gnarly_op = SummedOp( - [ - (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(Z), - PrimitiveOp(Operator.from_label("+r0I")), - 3 * (X ^ CX ^ T), - ], - coeff=3 + 0.2j, - ) - gnarly_mat_op = PrimitiveOp(gnarly_op.to_matrix()) - full_basis = list(map("".join, itertools.product("01", repeat=gnarly_op.num_qubits))) - for bstr1, bstr2 in itertools.product(full_basis, full_basis): - np.testing.assert_array_almost_equal( - gnarly_op.eval(bstr1).eval(bstr2), gnarly_mat_op.eval(bstr1).eval(bstr2) - ) - - def test_circuit_construction(self): - """circuit construction test""" - hadq2 = H ^ I - cz = hadq2.compose(CX).compose(hadq2) - qc = QuantumCircuit(2) - qc.append(cz.primitive, qargs=range(2)) - - ref_cz_mat = PrimitiveOp(CZGate()).to_matrix() - np.testing.assert_array_almost_equal(cz.to_matrix(), ref_cz_mat) - - def test_io_consistency(self): - """consistency test""" - new_op = X ^ Y ^ I - label = "XYI" - # label = new_op.primitive.to_label() - self.assertEqual(str(new_op.primitive), label) - np.testing.assert_array_almost_equal( - new_op.primitive.to_matrix(), Operator.from_label(label).data - ) - self.assertEqual(new_op.primitive, Pauli(label)) - - x_mat = X.primitive.to_matrix() - y_mat = Y.primitive.to_matrix() - i_mat = np.eye(2, 2) - np.testing.assert_array_almost_equal( - new_op.primitive.to_matrix(), np.kron(np.kron(x_mat, y_mat), i_mat) - ) - - hi = np.kron(H.to_matrix(), I.to_matrix()) - hi2 = Operator.from_label("HI").data - hi3 = (H ^ I).to_matrix() - np.testing.assert_array_almost_equal(hi, hi2) - np.testing.assert_array_almost_equal(hi2, hi3) - - xy = np.kron(X.to_matrix(), Y.to_matrix()) - xy2 = Operator.from_label("XY").data - xy3 = (X ^ Y).to_matrix() - np.testing.assert_array_almost_equal(xy, xy2) - np.testing.assert_array_almost_equal(xy2, xy3) - - # Check if numpy array instantiation is the same as from Operator - matrix_op = Operator.from_label("+r") - np.testing.assert_array_almost_equal( - PrimitiveOp(matrix_op).to_matrix(), PrimitiveOp(matrix_op.data).to_matrix() - ) - # Ditto list of lists - np.testing.assert_array_almost_equal( - PrimitiveOp(matrix_op.data.tolist()).to_matrix(), - PrimitiveOp(matrix_op.data).to_matrix(), - ) - - # TODO make sure this works once we resolve endianness mayhem - # qc = QuantumCircuit(3) - # qc.x(2) - # qc.y(1) - # from qiskit import BasicAer, QuantumCircuit, execute - # unitary = execute(qc, BasicAer.get_backend('unitary_simulator')).result().get_unitary() - # np.testing.assert_array_almost_equal(new_op.primitive.to_matrix(), unitary) - - def test_to_matrix(self): - """to matrix text""" - np.testing.assert_array_equal(X.to_matrix(), Operator.from_label("X").data) - np.testing.assert_array_equal(Y.to_matrix(), Operator.from_label("Y").data) - np.testing.assert_array_equal(Z.to_matrix(), Operator.from_label("Z").data) - - op1 = Y + H - np.testing.assert_array_almost_equal(op1.to_matrix(), Y.to_matrix() + H.to_matrix()) - - op2 = op1 * 0.5 - np.testing.assert_array_almost_equal(op2.to_matrix(), op1.to_matrix() * 0.5) - - op3 = (4 - 0.6j) * op2 - np.testing.assert_array_almost_equal(op3.to_matrix(), op2.to_matrix() * (4 - 0.6j)) - - op4 = op3.tensor(X) - np.testing.assert_array_almost_equal( - op4.to_matrix(), np.kron(op3.to_matrix(), X.to_matrix()) - ) - - op5 = op4.compose(H ^ I) - np.testing.assert_array_almost_equal( - op5.to_matrix(), np.dot(op4.to_matrix(), (H ^ I).to_matrix()) - ) - - op6 = op5 + PrimitiveOp(Operator.from_label("+r").data) - np.testing.assert_array_almost_equal( - op6.to_matrix(), op5.to_matrix() + Operator.from_label("+r").data - ) - - param = Parameter("α") - m = np.array([[0, -1j], [1j, 0]]) - op7 = MatrixOp(m, param) - np.testing.assert_array_equal(op7.to_matrix(), m * param) - - param = Parameter("β") - op8 = PauliOp(primitive=Pauli("Y"), coeff=param) - np.testing.assert_array_equal(op8.to_matrix(), m * param) - - param = Parameter("γ") - qc = QuantumCircuit(1) - qc.h(0) - op9 = CircuitOp(qc, coeff=param) - m = np.array([[1, 1], [1, -1]]) / np.sqrt(2) - np.testing.assert_array_equal(op9.to_matrix(), m * param) - - def test_circuit_op_to_matrix(self): - """test CircuitOp.to_matrix""" - qc = QuantumCircuit(1) - qc.rz(1.0, 0) - qcop = CircuitOp(qc) - np.testing.assert_array_almost_equal( - qcop.to_matrix(), scipy.linalg.expm(-0.5j * Z.to_matrix()) - ) - - def test_matrix_to_instruction(self): - """Test MatrixOp.to_instruction yields an Instruction object.""" - matop = (H ^ 3).to_matrix_op() - with self.subTest("assert to_instruction returns Instruction"): - self.assertIsInstance(matop.to_instruction(), Instruction) - - matop = ((H ^ 3) + (Z ^ 3)).to_matrix_op() - with self.subTest("matrix operator is not unitary"): - with self.assertRaises(ValueError): - matop.to_instruction() - - def test_adjoint(self): - """adjoint test""" - gnarly_op = 3 * (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(T ^ Z) + PrimitiveOp( - Operator.from_label("+r0IX").data - ) - np.testing.assert_array_almost_equal( - np.conj(np.transpose(gnarly_op.to_matrix())), gnarly_op.adjoint().to_matrix() - ) - - def test_primitive_strings(self): - """get primitives test""" - self.assertEqual(X.primitive_strings(), {"Pauli"}) - - gnarly_op = 3 * (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(T ^ Z) + PrimitiveOp( - Operator.from_label("+r0IX").data - ) - self.assertEqual(gnarly_op.primitive_strings(), {"QuantumCircuit", "Matrix"}) - - def test_to_pauli_op(self): - """Test to_pauli_op method""" - gnarly_op = 3 * (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(T ^ Z) + PrimitiveOp( - Operator.from_label("+r0IX").data - ) - mat_op = gnarly_op.to_matrix_op() - pauli_op = gnarly_op.to_pauli_op() - self.assertIsInstance(pauli_op, SummedOp) - for p in pauli_op: - self.assertIsInstance(p, PauliOp) - np.testing.assert_array_almost_equal(mat_op.to_matrix(), pauli_op.to_matrix()) - - def test_circuit_permute(self): - r"""Test the CircuitOp's .permute method""" - perm = range(7)[::-1] - c_op = ( - ((CX ^ 3) ^ X) - @ (H ^ 7) - @ (X ^ Y ^ Z ^ I ^ X ^ X ^ X) - @ (Y ^ (CX ^ 3)) - @ (X ^ Y ^ Z ^ I ^ X ^ X ^ X) - ) - c_op_perm = c_op.permute(perm) - self.assertNotEqual(c_op, c_op_perm) - c_op_id = c_op_perm.permute(perm) - self.assertEqual(c_op, c_op_id) - - def test_summed_op_reduce(self): - """Test SummedOp""" - sum_op = (X ^ X * 2) + (Y ^ Y) # type: PauliSumOp - sum_op = sum_op.to_pauli_op() # type: SummedOp[PauliOp] - with self.subTest("SummedOp test 1"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [2, 1]) - - sum_op = (X ^ X * 2) + (Y ^ Y) - sum_op += Y ^ Y - sum_op = sum_op.to_pauli_op() # type: SummedOp[PauliOp] - with self.subTest("SummedOp test 2-a"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [2, 1, 1]) - - sum_op = sum_op.collapse_summands() - with self.subTest("SummedOp test 2-b"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [2, 2]) - - sum_op = (X ^ X * 2) + (Y ^ Y) - sum_op += (Y ^ Y) + (X ^ X * 2) - sum_op = sum_op.to_pauli_op() # type: SummedOp[PauliOp] - with self.subTest("SummedOp test 3-a"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "YY", "XX"]) - self.assertListEqual([op.coeff for op in sum_op], [2, 1, 1, 2]) - - sum_op = sum_op.reduce().to_pauli_op() - with self.subTest("SummedOp test 3-b"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [4, 2]) - - sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) - with self.subTest("SummedOp test 4-a"): - self.assertEqual(sum_op.coeff, 2) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [2, 1]) - - sum_op = sum_op.collapse_summands() - with self.subTest("SummedOp test 4-b"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [4, 2]) - - sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) - sum_op += Y ^ Y - with self.subTest("SummedOp test 5-a"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [4, 2, 1]) - - sum_op = sum_op.collapse_summands() - with self.subTest("SummedOp test 5-b"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [4, 3]) - - sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) - sum_op += ((X ^ X) * 2 + (Y ^ Y)).to_pauli_op() - with self.subTest("SummedOp test 6-a"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [4, 2, 2, 1]) - - sum_op = sum_op.collapse_summands() - with self.subTest("SummedOp test 6-b"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [6, 3]) - - sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) - sum_op += sum_op - with self.subTest("SummedOp test 7-a"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [4, 2, 4, 2]) - - sum_op = sum_op.collapse_summands() - with self.subTest("SummedOp test 7-b"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) - self.assertListEqual([op.coeff for op in sum_op], [8, 4]) - - sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) + SummedOp([X ^ X * 2, Z ^ Z], 3) - with self.subTest("SummedOp test 8-a"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "XX", "ZZ"]) - self.assertListEqual([op.coeff for op in sum_op], [4, 2, 6, 3]) - - sum_op = sum_op.collapse_summands() - with self.subTest("SummedOp test 8-b"): - self.assertEqual(sum_op.coeff, 1) - self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "ZZ"]) - self.assertListEqual([op.coeff for op in sum_op], [10, 2, 3]) - - sum_op = SummedOp([]) - with self.subTest("SummedOp test 9"): - self.assertEqual(sum_op.reduce(), sum_op) - - sum_op = ((Z + I) ^ Z) + (Z ^ X) - with self.subTest("SummedOp test 10"): - expected = SummedOp([PauliOp(Pauli("ZZ")), PauliOp(Pauli("IZ")), PauliOp(Pauli("ZX"))]) - self.assertEqual(sum_op.to_pauli_op(), expected) - - def test_compose_op_of_different_dim(self): - """ - Test if smaller operator expands to correct dim when composed with bigger operator. - Test if PrimitiveOps compose methods are consistent. - """ - # PauliOps of different dim - xy_p = X ^ Y - xyz_p = X ^ Y ^ Z - - pauli_op = xy_p @ xyz_p - expected_result = I ^ I ^ Z - self.assertEqual(pauli_op, expected_result) - - # MatrixOps of different dim - xy_m = xy_p.to_matrix_op() - xyz_m = xyz_p.to_matrix_op() - - matrix_op = xy_m @ xyz_m - self.assertEqual(matrix_op, expected_result.to_matrix_op()) - - # CircuitOps of different dim - xy_c = xy_p.to_circuit_op() - xyz_c = xyz_p.to_circuit_op() - - circuit_op = xy_c @ xyz_c - - self.assertTrue(np.array_equal(pauli_op.to_matrix(), matrix_op.to_matrix())) - self.assertTrue(np.allclose(pauli_op.to_matrix(), circuit_op.to_matrix(), rtol=1e-14)) - self.assertTrue(np.allclose(matrix_op.to_matrix(), circuit_op.to_matrix(), rtol=1e-14)) - - def test_permute_on_primitive_op(self): - """Test if permute methods of PrimitiveOps are consistent and work as expected.""" - indices = [1, 2, 4] - - # PauliOp - pauli_op = X ^ Y ^ Z - permuted_pauli_op = pauli_op.permute(indices) - expected_pauli_op = X ^ I ^ Y ^ Z ^ I - - self.assertEqual(permuted_pauli_op, expected_pauli_op) - - # CircuitOp - circuit_op = pauli_op.to_circuit_op() - permuted_circuit_op = circuit_op.permute(indices) - expected_circuit_op = expected_pauli_op.to_circuit_op() - - self.assertEqual( - Operator(permuted_circuit_op.primitive), Operator(expected_circuit_op.primitive) - ) - - # MatrixOp - matrix_op = pauli_op.to_matrix_op() - permuted_matrix_op = matrix_op.permute(indices) - expected_matrix_op = expected_pauli_op.to_matrix_op() - - equal = np.allclose(permuted_matrix_op.to_matrix(), expected_matrix_op.to_matrix()) - self.assertTrue(equal) - - def test_permute_on_list_op(self): - """Test if ListOp permute method is consistent with PrimitiveOps permute methods.""" - - op1 = (X ^ Y ^ Z).to_circuit_op() - op2 = Z ^ X ^ Y - - # ComposedOp - indices = [1, 2, 0] - primitive_op = op1 @ op2 - primitive_op_perm = primitive_op.permute(indices) # CircuitOp.permute - - composed_op = ComposedOp([op1, op2]) - composed_op_perm = composed_op.permute(indices) - - # reduce the ListOp to PrimitiveOp - to_primitive = composed_op_perm.oplist[0] @ composed_op_perm.oplist[1] - # compare resulting PrimitiveOps - equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) - self.assertTrue(equal) - - # TensoredOp - indices = [3, 5, 4, 0, 2, 1] - primitive_op = op1 ^ op2 - primitive_op_perm = primitive_op.permute(indices) - - tensored_op = TensoredOp([op1, op2]) - tensored_op_perm = tensored_op.permute(indices) - - # reduce the ListOp to PrimitiveOp - composed_oplist = tensored_op_perm.oplist - to_primitive = ( - composed_oplist[0] - @ (composed_oplist[1].oplist[0] ^ composed_oplist[1].oplist[1]) - @ composed_oplist[2] - ) - - # compare resulting PrimitiveOps - equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) - self.assertTrue(equal) - - # SummedOp - primitive_op = X ^ Y ^ Z - summed_op = SummedOp([primitive_op]) - - indices = [1, 2, 0] - primitive_op_perm = primitive_op.permute(indices) # PauliOp.permute - summed_op_perm = summed_op.permute(indices) - - # reduce the ListOp to PrimitiveOp - to_primitive = summed_op_perm.oplist[0] @ primitive_op @ summed_op_perm.oplist[2] - - # compare resulting PrimitiveOps - equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) - self.assertTrue(equal) - - def test_expand_on_list_op(self): - """Test if expanded ListOp has expected num_qubits.""" - add_qubits = 3 - - # ComposedOp - composed_op = ComposedOp([(X ^ Y ^ Z), (H ^ T), (Z ^ X ^ Y ^ Z).to_matrix_op()]) - expanded = composed_op._expand_dim(add_qubits) - self.assertEqual(composed_op.num_qubits + add_qubits, expanded.num_qubits) - - # TensoredOp - tensored_op = TensoredOp([(X ^ Y), (Z ^ I)]) - expanded = tensored_op._expand_dim(add_qubits) - self.assertEqual(tensored_op.num_qubits + add_qubits, expanded.num_qubits) - - # SummedOp - summed_op = SummedOp([(X ^ Y), (Z ^ I ^ Z)]) - expanded = summed_op._expand_dim(add_qubits) - self.assertEqual(summed_op.num_qubits + add_qubits, expanded.num_qubits) - - def test_expand_on_state_fn(self): - """Test if expanded StateFn has expected num_qubits.""" - num_qubits = 3 - add_qubits = 2 - - # case CircuitStateFn, with primitive QuantumCircuit - qc2 = QuantumCircuit(num_qubits) - qc2.cx(0, 1) - - cfn = CircuitStateFn(qc2, is_measurement=True) - - cfn_exp = cfn._expand_dim(add_qubits) - self.assertEqual(cfn_exp.num_qubits, add_qubits + num_qubits) - - # case OperatorStateFn, with OperatorBase primitive, in our case CircuitStateFn - osfn = OperatorStateFn(cfn) - osfn_exp = osfn._expand_dim(add_qubits) - - self.assertEqual(osfn_exp.num_qubits, add_qubits + num_qubits) - - # case DictStateFn - dsfn = DictStateFn("1" * num_qubits, is_measurement=True) - self.assertEqual(dsfn.num_qubits, num_qubits) - - dsfn_exp = dsfn._expand_dim(add_qubits) - self.assertEqual(dsfn_exp.num_qubits, num_qubits + add_qubits) - - # case VectorStateFn - vsfn = VectorStateFn(np.ones(2**num_qubits, dtype=complex)) - self.assertEqual(vsfn.num_qubits, num_qubits) - - vsfn_exp = vsfn._expand_dim(add_qubits) - self.assertEqual(vsfn_exp.num_qubits, num_qubits + add_qubits) - - def test_permute_on_state_fn(self): - """Test if StateFns permute are consistent.""" - - num_qubits = 4 - dim = 2**num_qubits - primitive_list = [1.0 / (i + 1) for i in range(dim)] - primitive_dict = {format(i, "b").zfill(num_qubits): 1.0 / (i + 1) for i in range(dim)} - - dict_fn = DictStateFn(primitive=primitive_dict, is_measurement=True) - vec_fn = VectorStateFn(primitive=primitive_list, is_measurement=True) - - # check if dict_fn and vec_fn are equivalent - equivalent = np.allclose(dict_fn.to_matrix(), vec_fn.to_matrix()) - self.assertTrue(equivalent) - - # permute - indices = [2, 3, 0, 1] - permute_dict = dict_fn.permute(indices) - permute_vect = vec_fn.permute(indices) - - equivalent = np.allclose(permute_dict.to_matrix(), permute_vect.to_matrix()) - self.assertTrue(equivalent) - - def test_compose_consistency(self): - """Test if PrimitiveOp @ ComposedOp is consistent with ComposedOp @ PrimitiveOp.""" - - # PauliOp - op1 = X ^ Y ^ Z - op2 = X ^ Y ^ Z - op3 = (X ^ Y ^ Z).to_circuit_op() - - comp1 = op1 @ ComposedOp([op2, op3]) - comp2 = ComposedOp([op3, op2]) @ op1 - self.assertListEqual(comp1.oplist, list(reversed(comp2.oplist))) - - # CircitOp - op1 = op1.to_circuit_op() - op2 = op2.to_circuit_op() - op3 = op3.to_matrix_op() - - comp1 = op1 @ ComposedOp([op2, op3]) - comp2 = ComposedOp([op3, op2]) @ op1 - self.assertListEqual(comp1.oplist, list(reversed(comp2.oplist))) - - # MatrixOp - op1 = op1.to_matrix_op() - op2 = op2.to_matrix_op() - op3 = op3.to_pauli_op() - - comp1 = op1 @ ComposedOp([op2, op3]) - comp2 = ComposedOp([op3, op2]) @ op1 - self.assertListEqual(comp1.oplist, list(reversed(comp2.oplist))) - - def test_compose_with_indices(self): - """Test compose method using its permutation feature.""" - - pauli_op = X ^ Y ^ Z - circuit_op = T ^ H - matrix_op = (X ^ Y ^ H ^ T).to_matrix_op() - evolved_op = EvolvedOp(matrix_op) - - # composition of PrimitiveOps - num_qubits = 4 - primitive_op = pauli_op @ circuit_op @ matrix_op - composed_op = pauli_op @ circuit_op @ evolved_op - self.assertEqual(primitive_op.num_qubits, num_qubits) - self.assertEqual(composed_op.num_qubits, num_qubits) - - # with permutation - num_qubits = 5 - indices = [1, 4] - permuted_primitive_op = evolved_op @ circuit_op.permute(indices) @ pauli_op @ matrix_op - composed_primitive_op = ( - evolved_op @ pauli_op.compose(circuit_op, permutation=indices, front=True) @ matrix_op - ) - - self.assertTrue( - np.allclose(permuted_primitive_op.to_matrix(), composed_primitive_op.to_matrix()) - ) - self.assertEqual(num_qubits, permuted_primitive_op.num_qubits) - - # ListOp - num_qubits = 6 - tensored_op = TensoredOp([pauli_op, circuit_op]) - summed_op = pauli_op + circuit_op.permute([2, 1]) - composed_op = circuit_op @ evolved_op @ matrix_op - - list_op = summed_op @ composed_op.compose( - tensored_op, permutation=[1, 2, 3, 5, 4], front=True - ) - self.assertEqual(num_qubits, list_op.num_qubits) - - num_qubits = 4 - circuit_fn = CircuitStateFn(primitive=circuit_op.primitive, is_measurement=True) - operator_fn = OperatorStateFn(primitive=circuit_op ^ circuit_op, is_measurement=True) - - no_perm_op = circuit_fn @ operator_fn - self.assertEqual(no_perm_op.num_qubits, num_qubits) - - indices = [0, 4] - perm_op = operator_fn.compose(circuit_fn, permutation=indices, front=True) - self.assertEqual(perm_op.num_qubits, max(indices) + 1) - - # StateFn - num_qubits = 3 - dim = 2**num_qubits - vec = [1.0 / (i + 1) for i in range(dim)] - dic = {format(i, "b").zfill(num_qubits): 1.0 / (i + 1) for i in range(dim)} - - is_measurement = True - op_state_fn = OperatorStateFn(matrix_op, is_measurement=is_measurement) # num_qubit = 4 - vec_state_fn = VectorStateFn(vec, is_measurement=is_measurement) # 3 - dic_state_fn = DictStateFn(dic, is_measurement=is_measurement) # 3 - circ_state_fn = CircuitStateFn(circuit_op.to_circuit(), is_measurement=is_measurement) # 2 - - composed_op = op_state_fn @ vec_state_fn @ dic_state_fn @ circ_state_fn - self.assertEqual(composed_op.num_qubits, op_state_fn.num_qubits) - - # with permutation - perm = [2, 4, 6] - composed = ( - op_state_fn - @ dic_state_fn.compose(vec_state_fn, permutation=perm, front=True) - @ circ_state_fn - ) - self.assertEqual(composed.num_qubits, max(perm) + 1) - - def test_summed_op_equals(self): - """Test corner cases of SummedOp's equals function.""" - with self.subTest("multiplicative factor"): - self.assertEqual(2 * X, X + X) - - with self.subTest("commutative"): - self.assertEqual(X + Z, Z + X) - - with self.subTest("circuit and paulis"): - z = CircuitOp(ZGate()) - self.assertEqual(Z + z, z + Z) - - with self.subTest("matrix op and paulis"): - z = MatrixOp([[1, 0], [0, -1]]) - self.assertEqual(Z + z, z + Z) - - with self.subTest("matrix multiplicative"): - z = MatrixOp([[1, 0], [0, -1]]) - self.assertEqual(2 * z, z + z) - - with self.subTest("parameter coefficients"): - expr = Parameter("theta") - z = MatrixOp([[1, 0], [0, -1]]) - self.assertEqual(expr * z, expr * z) - - with self.subTest("different coefficient types"): - expr = Parameter("theta") - z = MatrixOp([[1, 0], [0, -1]]) - self.assertNotEqual(expr * z, 2 * z) - - with self.subTest("additions aggregation"): - z = MatrixOp([[1, 0], [0, -1]]) - a = z + z + Z - b = 2 * z + Z - c = z + Z + z - self.assertEqual(a, b) - self.assertEqual(b, c) - self.assertEqual(a, c) - - def test_circuit_compose_register_independent(self): - """Test that CircuitOp uses combines circuits independent of the register. - - I.e. that is uses ``QuantumCircuit.compose`` over ``combine`` or ``extend``. - """ - op = Z ^ 2 - qr = QuantumRegister(2, "my_qr") - circuit = QuantumCircuit(qr) - composed = op.compose(CircuitOp(circuit)) - - self.assertEqual(composed.num_qubits, 2) - - def test_matrix_op_conversions(self): - """Test to reveal QiskitError when to_instruction or to_circuit method is called on - parameterized matrix op.""" - m = np.array([[0, 0, 1, 0], [0, 0, 0, -1], [1, 0, 0, 0], [0, -1, 0, 0]]) - matrix_op = MatrixOp(m, Parameter("beta")) - for method in ["to_instruction", "to_circuit"]: - with self.subTest(method): - # QiskitError: multiplication of Operator with ParameterExpression isn't implemented - self.assertRaises(QiskitError, getattr(matrix_op, method)) - - def test_list_op_to_circuit(self): - """Test if unitary ListOps transpile to circuit.""" - - # generate unitary matrices of dimension 2,4,8, seed is fixed - np.random.seed(233423) - u2 = unitary_group.rvs(2) - u4 = unitary_group.rvs(4) - u8 = unitary_group.rvs(8) - - # pauli matrices as numpy.arrays - x = np.array([[0.0, 1.0], [1.0, 0.0]]) - y = np.array([[0.0, -1.0j], [1.0j, 0.0]]) - z = np.array([[1.0, 0.0], [0.0, -1.0]]) - - # create MatrixOp and CircuitOp out of matrices - op2 = MatrixOp(u2) - op4 = MatrixOp(u4) - op8 = MatrixOp(u8) - c2 = op2.to_circuit_op() - - # algorithm using only matrix operations on numpy.arrays - xu4 = np.kron(x, u4) - zc2 = np.kron(z, u2) - zc2y = np.kron(zc2, y) - matrix = np.matmul(xu4, zc2y) - matrix = np.matmul(matrix, u8) - matrix = np.kron(matrix, u2) - operator = Operator(matrix) - - # same algorithm as above, but using PrimitiveOps - list_op = ((X ^ op4) @ (Z ^ c2 ^ Y) @ op8) ^ op2 - circuit = list_op.to_circuit() - - # verify that ListOp.to_circuit() outputs correct quantum circuit - self.assertTrue(operator.equiv(circuit), "ListOp.to_circuit() outputs wrong circuit!") - - def test_composed_op_to_circuit(self): - """ - Test if unitary ComposedOp transpile to circuit and represents expected operator. - Test if to_circuit on non-unitary ListOp raises exception. - """ - - x = np.array([[0.0, 1.0], [1.0, 0.0]]) # Pauli X as numpy array - y = np.array([[0.0, -1.0j], [1.0j, 0.0]]) # Pauli Y as numpy array - - m1 = np.array([[0, 0, 1, 0], [0, 0, 0, -1], [0, 0, 0, 0], [0, 0, 0, 0]]) # non-unitary - m2 = np.array([[0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0], [0, -1, 0, 0]]) # non-unitary - - m_op1 = MatrixOp(m1) - m_op2 = MatrixOp(m2) - - pm1 = (X ^ Y) ^ m_op1 # non-unitary TensoredOp - pm2 = (X ^ Y) ^ m_op2 # non-unitary TensoredOp - - self.assertRaises(ValueError, pm1.to_circuit) - self.assertRaises(ValueError, pm2.to_circuit) - - summed_op = pm1 + pm2 # unitary SummedOp([TensoredOp, TensoredOp]) - circuit = summed_op.to_circuit() # should transpile without any exception - - # same algorithm that leads to summed_op above, but using only arrays and matrix operations - unitary = np.kron(np.kron(x, y), m1 + m2) - - self.assertTrue(Operator(unitary).equiv(circuit)) - - def test_pauli_op_to_circuit(self): - """Test PauliOp.to_circuit()""" - with self.subTest("single Pauli"): - pauli = PauliOp(Pauli("Y")) - expected = QuantumCircuit(1) - expected.y(0) - self.assertEqual(pauli.to_circuit(), expected) - - with self.subTest("single Pauli with phase"): - pauli = PauliOp(Pauli("-iX")) - expected = QuantumCircuit(1) - expected.x(0) - expected.global_phase = -pi / 2 - self.assertEqual(Operator(pauli.to_circuit()), Operator(expected)) - - with self.subTest("two qubit"): - pauli = PauliOp(Pauli("IX")) - expected = QuantumCircuit(2) - expected.pauli("IX", range(2)) - self.assertEqual(pauli.to_circuit(), expected) - expected = QuantumCircuit(2) - expected.x(0) - self.assertEqual(pauli.to_circuit().decompose(), expected) - - with self.subTest("Pauli identity"): - pauli = PauliOp(Pauli("I")) - expected = QuantumCircuit(1) - self.assertEqual(pauli.to_circuit(), expected) - - with self.subTest("two qubit with phase"): - pauli = PauliOp(Pauli("iXZ")) - expected = QuantumCircuit(2) - expected.pauli("XZ", range(2)) - expected.global_phase = pi / 2 - self.assertEqual(pauli.to_circuit(), expected) - expected = QuantumCircuit(2) - expected.z(0) - expected.x(1) - expected.global_phase = pi / 2 - self.assertEqual(pauli.to_circuit().decompose(), expected) - - def test_op_to_circuit_with_parameters(self): - """On parameterized SummedOp, to_matrix_op returns ListOp, instead of MatrixOp. To avoid - the infinite recursion, OpflowError is raised.""" - m1 = np.array([[0, 0, 1, 0], [0, 0, 0, -1], [0, 0, 0, 0], [0, 0, 0, 0]]) # non-unitary - m2 = np.array([[0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0], [0, -1, 0, 0]]) # non-unitary - - op1_with_param = MatrixOp(m1, Parameter("alpha")) # non-unitary - op2_with_param = MatrixOp(m2, Parameter("beta")) # non-unitary - - summed_op_with_param = op1_with_param + op2_with_param # unitary - # should raise OpflowError error - self.assertRaises(OpflowError, summed_op_with_param.to_circuit) - - def test_permute_list_op_with_inconsistent_num_qubits(self): - """Test if permute raises error if ListOp contains operators with different num_qubits.""" - list_op = ListOp([X, X ^ X]) - self.assertRaises(OpflowError, list_op.permute, [0, 1]) - - @data(Z, CircuitOp(ZGate()), MatrixOp([[1, 0], [0, -1]])) - def test_op_indent(self, op): - """Test that indentation correctly adds INDENTATION at the beginning of each line""" - initial_str = str(op) - indented_str = op._indent(initial_str) - starts_with_indent = indented_str.startswith(op.INDENTATION) - self.assertTrue(starts_with_indent) - indented_str_content = (indented_str[len(op.INDENTATION) :]).split(f"\n{op.INDENTATION}") - self.assertListEqual(indented_str_content, initial_str.split("\n")) - - def test_composed_op_immutable_under_eval(self): - """Test ``ComposedOp.eval`` does not change the operator instance.""" - op = 2 * ComposedOp([X]) - _ = op.eval() - # previous bug: after op.eval(), op was 2 * ComposedOp([2 * X]) - self.assertEqual(op, 2 * ComposedOp([X])) - - def test_op_parameters(self): - """Test that Parameters are stored correctly""" - phi = Parameter("φ") - theta = ParameterVector(name="θ", length=2) - - qc = QuantumCircuit(2) - qc.rz(phi, 0) - qc.rz(phi, 1) - for i in range(2): - qc.rx(theta[i], i) - qc.h(0) - qc.x(1) - - l = Parameter("λ") - op = PrimitiveOp(qc, coeff=l) - - params = {phi, l, *theta.params} - - self.assertEqual(params, op.parameters) - self.assertEqual(params, StateFn(op).parameters) - self.assertEqual(params, StateFn(qc, coeff=l).parameters) - - def test_list_op_parameters(self): - """Test that Parameters are stored correctly in a List Operator""" - lam = Parameter("λ") - phi = Parameter("φ") - omega = Parameter("ω") - - mat_op = PrimitiveOp([[0, 1], [1, 0]], coeff=omega) - - qc = QuantumCircuit(1) - qc.rx(phi, 0) - qc_op = PrimitiveOp(qc) - - op1 = SummedOp([mat_op, qc_op]) - - params = [phi, omega] - self.assertEqual(op1.parameters, set(params)) - - # check list nesting case - op2 = PrimitiveOp([[1, 0], [0, -1]], coeff=lam) - - list_op = ListOp([op1, op2]) - - params.append(lam) - self.assertEqual(list_op.parameters, set(params)) - - @data( - VectorStateFn([1, 0]), - CircuitStateFn(QuantumCircuit(1)), - OperatorStateFn(I), - OperatorStateFn(MatrixOp([[1, 0], [0, 1]])), - OperatorStateFn(CircuitOp(QuantumCircuit(1))), - ) - def test_statefn_eval(self, op): - """Test calling eval on StateFn returns the statevector.""" - expected = Statevector([1, 0]) - self.assertEqual(op.eval().primitive, expected) - - def test_sparse_eval(self): - """Test calling eval on a DictStateFn returns a sparse statevector.""" - op = DictStateFn({"0": 1}) - expected = scipy.sparse.csr_matrix([[1, 0]]) - self.assertFalse((op.eval().primitive != expected).toarray().any()) - - def test_sparse_to_dict(self): - """Test converting a sparse vector state function to a dict state function.""" - isqrt2 = 1 / np.sqrt(2) - sparse = scipy.sparse.csr_matrix([[0, isqrt2, 0, isqrt2]]) - sparse_fn = SparseVectorStateFn(sparse) - dict_fn = DictStateFn({"01": isqrt2, "11": isqrt2}) - - with self.subTest("sparse to dict"): - self.assertEqual(dict_fn, sparse_fn.to_dict_fn()) - - with self.subTest("dict to sparse"): - self.assertEqual(dict_fn.to_spmatrix_op(), sparse_fn) - - def test_to_circuit_op(self): - """Test to_circuit_op method.""" - vector = np.array([2, 2]) - vsfn = VectorStateFn([1, 1], coeff=2) - dsfn = DictStateFn({"0": 1, "1": 1}, coeff=2) - - for sfn in [vsfn, dsfn]: - np.testing.assert_array_almost_equal(sfn.to_circuit_op().eval().primitive.data, vector) - - def test_invalid_primitive(self): - """Test invalid MatrixOp construction""" - msg = ( - "MatrixOp can only be instantiated with " - "['list', 'ndarray', 'spmatrix', 'Operator'], not " - ) - - with self.assertRaises(TypeError) as cm: - _ = MatrixOp("invalid") - - self.assertEqual(str(cm.exception), msg + "'str'") - - with self.assertRaises(TypeError) as cm: - _ = MatrixOp(None) - - self.assertEqual(str(cm.exception), msg + "'NoneType'") - - with self.assertRaises(TypeError) as cm: - _ = MatrixOp(2.0) - - self.assertEqual(str(cm.exception), msg + "'float'") - - def test_summedop_equals(self): - """Test SummedOp.equals""" - ops = [Z, CircuitOp(ZGate()), MatrixOp([[1, 0], [0, -1]]), Zero, Minus] - sum_op = sum(ops + [ListOp(ops)]) - self.assertEqual(sum_op, sum_op) - self.assertEqual(sum_op + sum_op, 2 * sum_op) - self.assertEqual(sum_op + sum_op + sum_op, 3 * sum_op) - ops2 = [Z, CircuitOp(ZGate()), MatrixOp([[1, 0], [0, 1]]), Zero, Minus] - sum_op2 = sum(ops2 + [ListOp(ops)]) - self.assertNotEqual(sum_op, sum_op2) - self.assertEqual(sum_op2, sum_op2) - sum_op3 = sum(ops) - self.assertNotEqual(sum_op, sum_op3) - self.assertNotEqual(sum_op2, sum_op3) - self.assertEqual(sum_op3, sum_op3) - - def test_empty_listops(self): - """Test reduce and eval on ListOp with empty oplist.""" - with self.subTest("reduce empty ComposedOp "): - self.assertEqual(ComposedOp([]).reduce(), ComposedOp([])) - with self.subTest("reduce empty TensoredOp "): - self.assertEqual(TensoredOp([]).reduce(), TensoredOp([])) - with self.subTest("eval empty ComposedOp "): - self.assertEqual(ComposedOp([]).eval(), 0.0) - with self.subTest("eval empty TensoredOp "): - self.assertEqual(TensoredOp([]).eval(), 0.0) - - def test_composed_op_to_matrix_with_coeff(self): - """Test coefficients are properly handled. - - Regression test of Qiskit/qiskit-terra#9283. - """ - x = MatrixOp(X.to_matrix()) - composed = 0.5 * (x @ X) - - expected = 0.5 * np.eye(2) - - np.testing.assert_almost_equal(composed.to_matrix(), expected) - - def test_composed_op_to_matrix_with_vector(self): - """Test a matrix-vector composed op can be cast to matrix. - - Regression test of Qiskit/qiskit-terra#9283. - """ - x = MatrixOp(X.to_matrix()) - composed = x @ Zero - - expected = np.array([0, 1]) - - np.testing.assert_almost_equal(composed.to_matrix(), expected) - - def test_tensored_op_to_matrix(self): - """Test tensored operators to matrix works correctly with a global coefficient. - - Regression test of Qiskit/qiskit-terra#9398. - """ - op = TensoredOp([X, I], coeff=0.5) - expected = 1 / 2 * np.kron(X.to_matrix(), I.to_matrix()) - np.testing.assert_almost_equal(op.to_matrix(), expected) - - -class TestOpMethods(QiskitOpflowTestCase): - """Basic method tests.""" - - def test_listop_num_qubits(self): - """Test that ListOp.num_qubits checks that all operators have the same number of qubits.""" - op = ListOp([X ^ Y, Y ^ Z]) - with self.subTest("All operators have the same numbers of qubits"): - self.assertEqual(op.num_qubits, 2) - - op = ListOp([X ^ Y, Y]) - with self.subTest("Operators have different numbers of qubits"): - with self.assertRaises(ValueError): - op.num_qubits # pylint: disable=pointless-statement - - with self.assertRaises(ValueError): - X @ op # pylint: disable=pointless-statement - - def test_is_hermitian(self): - """Test is_hermitian method.""" - with self.subTest("I"): - self.assertTrue(I.is_hermitian()) - - with self.subTest("X"): - self.assertTrue(X.is_hermitian()) - - with self.subTest("Y"): - self.assertTrue(Y.is_hermitian()) - - with self.subTest("Z"): - self.assertTrue(Z.is_hermitian()) - - with self.subTest("XY"): - self.assertFalse((X @ Y).is_hermitian()) - - with self.subTest("CX"): - self.assertTrue(CX.is_hermitian()) - - with self.subTest("T"): - self.assertFalse(T.is_hermitian()) - - -@ddt -class TestListOpMethods(QiskitOpflowTestCase): - """Test ListOp accessing methods""" - - @data(ListOp, SummedOp, ComposedOp, TensoredOp) - def test_indexing(self, list_op_type): - """Test indexing and slicing""" - coeff = 3 + 0.2j - states_op = list_op_type([X, Y, Z, I], coeff=coeff) - - single_op = states_op[1] - self.assertIsInstance(single_op, OperatorBase) - self.assertNotIsInstance(single_op, ListOp) - - list_one_element = states_op[1:2] - self.assertIsInstance(list_one_element, list_op_type) - self.assertEqual(len(list_one_element), 1) - self.assertEqual(list_one_element[0], Y) - - list_two_elements = states_op[::2] - self.assertIsInstance(list_two_elements, list_op_type) - self.assertEqual(len(list_two_elements), 2) - self.assertEqual(list_two_elements[0], X) - self.assertEqual(list_two_elements[1], Z) - - self.assertEqual(list_one_element.coeff, coeff) - self.assertEqual(list_two_elements.coeff, coeff) - - -class TestListOpComboFn(QiskitOpflowTestCase): - """Test combo fn is propagated.""" - - def setUp(self): - super().setUp() - self.combo_fn = lambda x: [x_i**2 for x_i in x] - self.listop = ListOp([X], combo_fn=self.combo_fn) - - def assertComboFnPreserved(self, processed_op): - """Assert the quadratic combo_fn is preserved.""" - x = [1, 2, 3] - self.assertListEqual(processed_op.combo_fn(x), self.combo_fn(x)) - - def test_at_conversion(self): - """Test after conversion the combo_fn is preserved.""" - for method in ["to_matrix_op", "to_pauli_op", "to_circuit_op"]: - with self.subTest(method): - converted = getattr(self.listop, method)() - self.assertComboFnPreserved(converted) - - def test_after_mul(self): - """Test after multiplication the combo_fn is preserved.""" - self.assertComboFnPreserved(2 * self.listop) - - def test_at_traverse(self): - """Test after traversing the combo_fn is preserved.""" - - def traverse_fn(op): - return -op - - traversed = self.listop.traverse(traverse_fn) - self.assertComboFnPreserved(traversed) - - def test_after_adjoint(self): - """Test after traversing the combo_fn is preserved.""" - self.assertComboFnPreserved(self.listop.adjoint()) - - def test_after_reduce(self): - """Test after reducing the combo_fn is preserved.""" - self.assertComboFnPreserved(self.listop.reduce()) - - -def pauli_group_labels(nq, full_group=True): - """Generate list of the N-qubit pauli group string labels""" - labels = ["".join(i) for i in itertools.product(("I", "X", "Y", "Z"), repeat=nq)] - if full_group: - labels = ["".join(i) for i in itertools.product(("", "-i", "-", "i"), labels)] - return labels - - -def operator_from_label(label): - """Construct operator from full Pauli group label""" - return Operator(Pauli(label)) - - -@ddt -class TestPauliOp(QiskitOpflowTestCase): - """PauliOp tests.""" - - def test_construct(self): - """constructor test""" - pauli = Pauli("XYZX") - coeff = 3.0 - pauli_op = PauliOp(pauli, coeff) - self.assertIsInstance(pauli_op, PauliOp) - self.assertEqual(pauli_op.primitive, pauli) - self.assertEqual(pauli_op.coeff, coeff) - self.assertEqual(pauli_op.num_qubits, 4) - - def test_add(self): - """add test""" - pauli_sum = X + Y - summed_op = SummedOp([X, Y]) - self.assertEqual(pauli_sum, summed_op) - - a = Parameter("a") - b = Parameter("b") - actual = PauliOp(Pauli("X"), a) + PauliOp(Pauli("Y"), b) - expected = SummedOp([PauliOp(Pauli("X"), a), PauliOp(Pauli("Y"), b)]) - self.assertEqual(actual, expected) - - def test_adjoint(self): - """adjoint test""" - pauli_op = PauliOp(Pauli("XYZX"), coeff=3) - expected = PauliOp(Pauli("XYZX"), coeff=3) - - self.assertEqual(~pauli_op, expected) - - pauli_op = PauliOp(Pauli("XXY"), coeff=2j) - expected = PauliOp(Pauli("XXY"), coeff=-2j) - self.assertEqual(~pauli_op, expected) - - pauli_op = PauliOp(Pauli("XYZX"), coeff=2 + 3j) - expected = PauliOp(Pauli("XYZX"), coeff=2 - 3j) - self.assertEqual(~pauli_op, expected) - - pauli_op = PauliOp(Pauli("iXYZX"), coeff=2 + 3j) - expected = PauliOp(Pauli("-iXYZX"), coeff=2 - 3j) - self.assertEqual(~pauli_op, expected) - - @data(*itertools.product(pauli_group_labels(2, full_group=True), repeat=2)) - @unpack - def test_compose(self, label1, label2): - """compose test""" - p1 = PauliOp(Pauli(label1)) - p2 = PauliOp(Pauli(label2)) - value = Operator(p1 @ p2) - op1 = operator_from_label(label1) - op2 = operator_from_label(label2) - target = op1 @ op2 - self.assertEqual(value, target) - - def test_equals(self): - """equality test""" - - self.assertEqual(I @ X, X) - self.assertEqual(X, I @ X) - - theta = Parameter("theta") - pauli_op = theta * X ^ Z - expected = PauliOp( - Pauli("XZ"), - coeff=1.0 * theta, - ) - self.assertEqual(pauli_op, expected) - - def test_eval(self): - """eval test""" - target0 = (X ^ Y ^ Z).eval("000") - target1 = (X ^ Y ^ Z).eval(Zero ^ 3) - expected = DictStateFn({"110": 1j}) - self.assertEqual(target0, expected) - self.assertEqual(target1, expected) - - def test_exp_i(self): - """exp_i test""" - target = (2 * X ^ Z).exp_i() - expected = EvolvedOp(PauliOp(Pauli("XZ"), coeff=2.0), coeff=1.0) - self.assertEqual(target, expected) - - @data(([1, 2, 4], "XIYZI"), ([2, 1, 0], "ZYX")) - @unpack - def test_permute(self, permutation, expected_pauli): - """Test the permute method.""" - pauli_op = PauliOp(Pauli("XYZ"), coeff=1.0) - expected = PauliOp(Pauli(expected_pauli), coeff=1.0) - permuted = pauli_op.permute(permutation) - - with self.subTest(msg="test permutated object"): - self.assertEqual(permuted, expected) - - with self.subTest(msg="test original object is unchanged"): - original = PauliOp(Pauli("XYZ")) - self.assertEqual(pauli_op, original) - - def test_primitive_strings(self): - """primitive strings test""" - target = (2 * X ^ Z).primitive_strings() - expected = {"Pauli"} - self.assertEqual(target, expected) - - def test_tensor(self): - """tensor test""" - pauli_op = X ^ Y ^ Z - tensored_op = PauliOp(Pauli("XYZ")) - self.assertEqual(pauli_op, tensored_op) - - def test_to_instruction(self): - """to_instruction test""" - target = (X ^ Z).to_instruction() - qc = QuantumCircuit(2) - qc.u(0, 0, np.pi, 0) - qc.u(np.pi, 0, np.pi, 1) - qc_out = QuantumCircuit(2) - qc_out.append(target, qc_out.qubits) - qc_out = transpile(qc_out, basis_gates=["u"]) - self.assertEqual(qc, qc_out) - - def test_to_matrix(self): - """to_matrix test""" - target = (X ^ Y).to_matrix() - expected = np.kron(np.array([[0.0, 1.0], [1.0, 0.0]]), np.array([[0.0, -1j], [1j, 0.0]])) - np.testing.assert_array_equal(target, expected) - - def test_to_spmatrix(self): - """to_spmatrix test""" - target = X ^ Y - expected = csr_matrix( - np.kron(np.array([[0.0, 1.0], [1.0, 0.0]]), np.array([[0.0, -1j], [1j, 0.0]])) - ) - self.assertEqual((target.to_spmatrix() - expected).nnz, 0) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_pauli_basis_change.py b/test/python/opflow/test_pauli_basis_change.py deleted file mode 100644 index 5b9f9f324c97..000000000000 --- a/test/python/opflow/test_pauli_basis_change.py +++ /dev/null @@ -1,156 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Pauli Change of Basis Converter""" - -import itertools -import unittest -from functools import reduce -from test.python.opflow import QiskitOpflowTestCase - -import numpy as np - -from qiskit import QuantumCircuit -from qiskit.opflow import ( - ComposedOp, - I, - OperatorStateFn, - PauliSumOp, - SummedOp, - X, - Y, - Z, -) -from qiskit.opflow.converters import PauliBasisChange -from qiskit.quantum_info import Pauli, SparsePauliOp - - -class TestPauliCoB(QiskitOpflowTestCase): - """Pauli Change of Basis Converter tests.""" - - def test_pauli_cob_singles(self): - """from to file test""" - singles = [X, Y, Z] - dests = [None, Y] - for pauli, dest in itertools.product(singles, dests): - # print(pauli) - converter = PauliBasisChange(destination_basis=dest) - inst, dest = converter.get_cob_circuit(pauli.primitive) - cob = converter.convert(pauli) - np.testing.assert_array_almost_equal( - pauli.to_matrix(), inst.adjoint().to_matrix() @ dest.to_matrix() @ inst.to_matrix() - ) - np.testing.assert_array_almost_equal(pauli.to_matrix(), cob.to_matrix()) - np.testing.assert_array_almost_equal( - inst.compose(pauli).compose(inst.adjoint()).to_matrix(), dest.to_matrix() - ) - - def test_pauli_cob_two_qubit(self): - """pauli cob two qubit test""" - multis = [Y ^ X, Z ^ Y, I ^ Z, Z ^ I, X ^ X, I ^ X] - for pauli, dest in itertools.product(multis, reversed(multis)): - converter = PauliBasisChange(destination_basis=dest) - inst, dest = converter.get_cob_circuit(pauli.primitive) - cob = converter.convert(pauli) - np.testing.assert_array_almost_equal( - pauli.to_matrix(), inst.adjoint().to_matrix() @ dest.to_matrix() @ inst.to_matrix() - ) - np.testing.assert_array_almost_equal(pauli.to_matrix(), cob.to_matrix()) - np.testing.assert_array_almost_equal( - inst.compose(pauli).compose(inst.adjoint()).to_matrix(), dest.to_matrix() - ) - - def test_pauli_cob_multiqubit(self): - """pauli cob multi qubit test""" - # Helpful prints for debugging commented out below. - multis = [Y ^ X ^ I ^ I, I ^ Z ^ Y ^ X, X ^ Y ^ I ^ Z, I ^ I ^ I ^ X, X ^ X ^ X ^ X] - for pauli, dest in itertools.product(multis, reversed(multis)): - # print(pauli) - # print(dest) - converter = PauliBasisChange(destination_basis=dest) - inst, dest = converter.get_cob_circuit(pauli.primitive) - cob = converter.convert(pauli) - # print(inst) - # print(pauli.to_matrix()) - # print(np.round(inst.adjoint().to_matrix() @ cob.to_matrix())) - np.testing.assert_array_almost_equal( - pauli.to_matrix(), inst.adjoint().to_matrix() @ dest.to_matrix() @ inst.to_matrix() - ) - np.testing.assert_array_almost_equal(pauli.to_matrix(), cob.to_matrix()) - np.testing.assert_array_almost_equal( - inst.compose(pauli).compose(inst.adjoint()).to_matrix(), dest.to_matrix() - ) - - def test_pauli_cob_traverse(self): - """pauli cob traverse test""" - # Helpful prints for debugging commented out below. - multis = [(X ^ Y) + (I ^ Z) + (Z ^ Z), (Y ^ X ^ I ^ I) + (I ^ Z ^ Y ^ X)] - dests = [Y ^ Y, I ^ I ^ I ^ Z] - for paulis, dest in zip(multis, dests): - converter = PauliBasisChange(destination_basis=dest, traverse=True) - - cob = converter.convert(paulis) - self.assertIsInstance(cob, SummedOp) - inst = [None] * len(paulis) - ret_dest = [None] * len(paulis) - cob_mat = [None] * len(paulis) - for i, pauli in enumerate(paulis): - inst[i], ret_dest[i] = converter.get_cob_circuit(pauli.to_pauli_op().primitive) - self.assertEqual(dest, ret_dest[i]) - - self.assertIsInstance(cob.oplist[i], ComposedOp) - cob_mat[i] = cob.oplist[i].to_matrix() - np.testing.assert_array_almost_equal(pauli.to_matrix(), cob_mat[i]) - np.testing.assert_array_almost_equal(paulis.to_matrix(), sum(cob_mat)) - - def test_grouped_pauli(self): - """grouped pauli test""" - pauli = 2 * (I ^ I) + (X ^ I) + 3 * (X ^ Y) - grouped_pauli = PauliSumOp(pauli.primitive, grouping_type="TPB") - - converter = PauliBasisChange() - cob = converter.convert(grouped_pauli) - np.testing.assert_array_almost_equal(pauli.to_matrix(), cob.to_matrix()) - - origin_x = reduce(np.logical_or, pauli.primitive.paulis.x) - origin_z = reduce(np.logical_or, pauli.primitive.paulis.z) - origin_pauli = Pauli((origin_z, origin_x)) - inst, dest = converter.get_cob_circuit(origin_pauli) - self.assertEqual(str(dest), "ZZ") - expected_inst = np.array( - [ - [0.5, -0.5j, 0.5, -0.5j], - [0.5, 0.5j, 0.5, 0.5j], - [0.5, -0.5j, -0.5, 0.5j], - [0.5, 0.5j, -0.5, -0.5j], - ] - ) - np.testing.assert_array_almost_equal(inst.to_matrix(), expected_inst) - - def test_grouped_pauli_statefn(self): - """grouped pauli test with statefn""" - grouped_pauli = PauliSumOp(SparsePauliOp(["Y"]), grouping_type="TPB") - observable = OperatorStateFn(grouped_pauli, is_measurement=True) - - converter = PauliBasisChange(replacement_fn=PauliBasisChange.measurement_replacement_fn) - cob = converter.convert(observable) - - expected = PauliSumOp(SparsePauliOp(["Z"]), grouping_type="TPB") - self.assertEqual(cob[0].primitive, expected) - circuit = QuantumCircuit(1) - circuit.sdg(0) - circuit.h(0) - self.assertEqual(cob[1].primitive, circuit) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_pauli_expectation.py b/test/python/opflow/test_pauli_expectation.py deleted file mode 100644 index 19821aab56da..000000000000 --- a/test/python/opflow/test_pauli_expectation.py +++ /dev/null @@ -1,317 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test PauliExpectation""" - -import itertools -import unittest -import warnings -from test.python.opflow import QiskitOpflowTestCase - -import numpy as np - -from qiskit import BasicAer -from qiskit.opflow import ( - CX, - CircuitSampler, - H, - I, - ListOp, - Minus, - One, - PauliExpectation, - PauliSumOp, - Plus, - S, - StateFn, - X, - Y, - Z, - Zero, -) -from qiskit.utils import QuantumInstance, algorithm_globals - - -# pylint: disable=invalid-name - - -class TestPauliExpectation(QiskitOpflowTestCase): - """Pauli Change of Basis Expectation tests.""" - - def setUp(self) -> None: - super().setUp() - self.seed = 97 - backend = BasicAer.get_backend("qasm_simulator") - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - backend, seed_simulator=self.seed, seed_transpiler=self.seed - ) - self.sampler = CircuitSampler(q_instance, attach_results=True) - self.expect = PauliExpectation() - - def test_pauli_expect_pair(self): - """pauli expect pair test""" - - op = Z ^ Z - # wf = (Pl^Pl) + (Ze^Ze) - wf = CX @ (H ^ I) @ Zero - - converted_meas = self.expect.convert(~StateFn(op) @ wf) - self.assertAlmostEqual(converted_meas.eval(), 0, delta=0.1) - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - self.assertAlmostEqual(sampled.eval(), 0, delta=0.1) - - def test_pauli_expect_single(self): - """pauli expect single test""" - - paulis = [Z, X, Y, I] - states = [Zero, One, Plus, Minus, S @ Plus, S @ Minus] - for pauli, state in itertools.product(paulis, states): - converted_meas = self.expect.convert(~StateFn(pauli) @ state) - matmulmean = state.adjoint().to_matrix() @ pauli.to_matrix() @ state.to_matrix() - self.assertAlmostEqual(converted_meas.eval(), matmulmean, delta=0.1) - - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - - self.assertAlmostEqual(sampled.eval(), matmulmean, delta=0.1) - - def test_pauli_expect_op_vector(self): - """pauli expect op vector test""" - - paulis_op = ListOp([X, Y, Z, I]) - converted_meas = self.expect.convert(~StateFn(paulis_op)) - - plus_mean = converted_meas @ Plus - np.testing.assert_array_almost_equal(plus_mean.eval(), [1, 0, 0, 1], decimal=1) - - with self.assertWarns(DeprecationWarning): - sampled_plus = self.sampler.convert(plus_mean) - np.testing.assert_array_almost_equal(sampled_plus.eval(), [1, 0, 0, 1], decimal=1) - - minus_mean = converted_meas @ Minus - np.testing.assert_array_almost_equal(minus_mean.eval(), [-1, 0, 0, 1], decimal=1) - - sampled_minus = self.sampler.convert(minus_mean) - np.testing.assert_array_almost_equal(sampled_minus.eval(), [-1, 0, 0, 1], decimal=1) - - zero_mean = converted_meas @ Zero - np.testing.assert_array_almost_equal(zero_mean.eval(), [0, 0, 1, 1], decimal=1) - - sampled_zero = self.sampler.convert(zero_mean) - np.testing.assert_array_almost_equal(sampled_zero.eval(), [0, 0, 1, 1], decimal=1) - - sum_zero = (Plus + Minus) * (0.5**0.5) - sum_zero_mean = converted_meas @ sum_zero - np.testing.assert_array_almost_equal(sum_zero_mean.eval(), [0, 0, 1, 1], decimal=1) - - sampled_zero_mean = self.sampler.convert(sum_zero_mean) - - # !!NOTE!!: Depolarizing channel (Sampling) means interference - # does not happen between circuits in sum, so expectation does - # not equal expectation for Zero!! - np.testing.assert_array_almost_equal(sampled_zero_mean.eval(), [0, 0, 0, 1], decimal=1) - - for i, op in enumerate(paulis_op.oplist): - mat_op = op.to_matrix() - np.testing.assert_array_almost_equal( - zero_mean.eval()[i], - Zero.adjoint().to_matrix() @ mat_op @ Zero.to_matrix(), - decimal=1, - ) - np.testing.assert_array_almost_equal( - plus_mean.eval()[i], - Plus.adjoint().to_matrix() @ mat_op @ Plus.to_matrix(), - decimal=1, - ) - np.testing.assert_array_almost_equal( - minus_mean.eval()[i], - Minus.adjoint().to_matrix() @ mat_op @ Minus.to_matrix(), - decimal=1, - ) - - def test_pauli_expect_state_vector(self): - """pauli expect state vector test""" - - states_op = ListOp([One, Zero, Plus, Minus]) - - paulis_op = X - converted_meas = self.expect.convert(~StateFn(paulis_op) @ states_op) - np.testing.assert_array_almost_equal(converted_meas.eval(), [0, 0, 1, -1], decimal=1) - - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - - np.testing.assert_array_almost_equal(sampled.eval(), [0, 0, 1, -1], decimal=1) - - # Small test to see if execution results are accessible - for composed_op in sampled: - self.assertIn("counts", composed_op[1].execution_results) - - def test_pauli_expect_op_vector_state_vector(self): - """pauli expect op vector state vector test""" - - paulis_op = ListOp([X, Y, Z, I]) - states_op = ListOp([One, Zero, Plus, Minus]) - - valids = [[+0, 0, 1, -1], [+0, 0, 0, 0], [-1, 1, 0, -0], [+1, 1, 1, 1]] - converted_meas = self.expect.convert(~StateFn(paulis_op) @ states_op) - np.testing.assert_array_almost_equal(converted_meas.eval(), valids, decimal=1) - - with self.assertWarns(DeprecationWarning): - sampled = self.sampler.convert(converted_meas) - - np.testing.assert_array_almost_equal(sampled.eval(), valids, decimal=1) - - def test_to_matrix_called(self): - """test to matrix called in different situations""" - qs = 45 - - states_op = ListOp([Zero ^ qs, One ^ qs, (Zero ^ qs) + (One ^ qs)]) - paulis_op = ListOp([Z ^ qs, (I ^ Z ^ I) ^ int(qs / 3)]) - - # 45 qubit calculation - throws exception if to_matrix is called - # massive is False - with self.assertRaises(ValueError): - states_op.to_matrix() - paulis_op.to_matrix() - - # now set global variable or argument - try: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.massive = True - with self.assertRaises(MemoryError): - states_op.to_matrix() - paulis_op.to_matrix() - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.massive = False - with self.assertRaises(MemoryError): - states_op.to_matrix(massive=True) - paulis_op.to_matrix(massive=True) - finally: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=DeprecationWarning) - algorithm_globals.massive = False - - def test_not_to_matrix_called(self): - """45 qubit calculation - literally will not work if to_matrix is - somehow called (in addition to massive=False throwing an error)""" - - qs = 45 - states_op = ListOp([Zero ^ qs, One ^ qs, (Zero ^ qs) + (One ^ qs)]) - paulis_op = ListOp([Z ^ qs, (I ^ Z ^ I) ^ int(qs / 3)]) - - converted_meas = self.expect.convert(~StateFn(paulis_op) @ states_op) - np.testing.assert_array_almost_equal(converted_meas.eval(), [[1, -1, 0], [1, -1, 0]]) - - def test_grouped_pauli_expectation(self): - """grouped pauli expectation test""" - - two_qubit_H2 = ( - (-1.052373245772859 * I ^ I) - + (0.39793742484318045 * I ^ Z) - + (-0.39793742484318045 * Z ^ I) - + (-0.01128010425623538 * Z ^ Z) - + (0.18093119978423156 * X ^ X) - ) - wf = CX @ (H ^ I) @ Zero - expect_op = PauliExpectation(group_paulis=False).convert(~StateFn(two_qubit_H2) @ wf) - self.sampler._extract_circuitstatefns(expect_op) - num_circuits_ungrouped = len(self.sampler._circuit_ops_cache) - self.assertEqual(num_circuits_ungrouped, 5) - - expect_op_grouped = PauliExpectation(group_paulis=True).convert(~StateFn(two_qubit_H2) @ wf) - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - seed_simulator=self.seed, - seed_transpiler=self.seed, - ) - - sampler = CircuitSampler(q_instance) - sampler._extract_circuitstatefns(expect_op_grouped) - num_circuits_grouped = len(sampler._circuit_ops_cache) - - self.assertEqual(num_circuits_grouped, 2) - - @unittest.skip(reason="IBMQ testing not available in general.") - def test_ibmq_grouped_pauli_expectation(self): - """pauli expect op vector state vector test""" - from qiskit import IBMQ - - p = IBMQ.load_account() - backend = p.get_backend("ibmq_qasm_simulator") - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance( - backend, seed_simulator=self.seed, seed_transpiler=self.seed - ) - paulis_op = ListOp([X, Y, Z, I]) - states_op = ListOp([One, Zero, Plus, Minus]) - - valids = [[+0, 0, 1, -1], [+0, 0, 0, 0], [-1, 1, 0, -0], [+1, 1, 1, 1]] - converted_meas = self.expect.convert(~StateFn(paulis_op) @ states_op) - - with self.assertWarns(DeprecationWarning): - sampled = CircuitSampler(q_instance).convert(converted_meas) - np.testing.assert_array_almost_equal(sampled.eval(), valids, decimal=1) - - def test_multi_representation_ops(self): - """Test observables with mixed representations""" - - mixed_ops = ListOp([X.to_matrix_op(), H, H + I, X]) - converted_meas = self.expect.convert(~StateFn(mixed_ops)) - plus_mean = converted_meas @ Plus - - with self.assertWarns(DeprecationWarning): - sampled_plus = self.sampler.convert(plus_mean) - np.testing.assert_array_almost_equal( - sampled_plus.eval(), [1, 0.5**0.5, (1 + 0.5**0.5), 1], decimal=1 - ) - - def test_pauli_expectation_non_hermite_op(self): - """Test PauliExpectation for non hermitian operator""" - - exp = ~StateFn(1j * Z) @ One - self.assertEqual(self.expect.convert(exp).eval(), 1j) - - def test_list_pauli_sum_op(self): - """Test PauliExpectation for List[PauliSumOp]""" - - test_op = ListOp([~StateFn(PauliSumOp.from_list([("XX", 1), ("ZI", 3), ("ZZ", 5)]))]) - observable = self.expect.convert(test_op) - self.assertIsInstance(observable, ListOp) - self.assertIsInstance(observable[0][0][0].primitive, PauliSumOp) - self.assertIsInstance(observable[0][1][0].primitive, PauliSumOp) - - def test_expectation_with_coeff(self): - """Test PauliExpectation with coefficients.""" - - with self.subTest("integer coefficients"): - exp = 3 * ~StateFn(X) @ (2 * Minus) - with self.assertWarns(DeprecationWarning): - target = self.sampler.convert(self.expect.convert(exp)).eval() - self.assertEqual(target, -12) - - with self.subTest("complex coefficients"): - exp = 3j * ~StateFn(X) @ (2j * Minus) - with self.assertWarns(DeprecationWarning): - target = self.sampler.convert(self.expect.convert(exp)).eval() - self.assertEqual(target, -12j) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_pauli_sum_op.py b/test/python/opflow/test_pauli_sum_op.py deleted file mode 100644 index 7b97756ae3a6..000000000000 --- a/test/python/opflow/test_pauli_sum_op.py +++ /dev/null @@ -1,365 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test PauliSumOp.""" - -import unittest -from itertools import product -from test.python.opflow import QiskitOpflowTestCase - -import numpy as np -from ddt import data, ddt, unpack -from scipy.sparse import csr_matrix -from sympy import Symbol - -from qiskit import QuantumCircuit, transpile -from qiskit.circuit import Parameter, ParameterExpression, ParameterVector -from qiskit.opflow import ( - CX, - CircuitStateFn, - DictStateFn, - H, - I, - One, - OperatorStateFn, - OpflowError, - PauliSumOp, - SummedOp, - X, - Y, - Z, - Zero, -) -from qiskit.quantum_info import Pauli, PauliList, SparsePauliOp - - -@ddt -class TestPauliSumOp(QiskitOpflowTestCase): - """PauliSumOp tests.""" - - def test_construct(self): - """constructor test""" - sparse_pauli = SparsePauliOp(Pauli("XYZX"), coeffs=[2.0]) - coeff = 3.0 - pauli_sum = PauliSumOp(sparse_pauli, coeff=coeff) - self.assertIsInstance(pauli_sum, PauliSumOp) - self.assertEqual(pauli_sum.primitive, sparse_pauli) - self.assertEqual(pauli_sum.coeff, coeff) - self.assertEqual(pauli_sum.num_qubits, 4) - - def test_coeffs(self): - """ListOp.coeffs test""" - sum1 = SummedOp( - [(0 + 1j) * X, (1 / np.sqrt(2) + 1j / np.sqrt(2)) * Z], 0.5 - ).collapse_summands() - self.assertAlmostEqual(sum1.coeffs[0], 0.5j) - self.assertAlmostEqual(sum1.coeffs[1], (1 + 1j) / (2 * np.sqrt(2))) - - a_param = Parameter("a") - b_param = Parameter("b") - param_exp = ParameterExpression({a_param: 1, b_param: 0}, Symbol("a") ** 2 + Symbol("b")) - sum2 = SummedOp([X, (1 / np.sqrt(2) - 1j / np.sqrt(2)) * Y], param_exp).collapse_summands() - self.assertIsInstance(sum2.coeffs[0], ParameterExpression) - self.assertIsInstance(sum2.coeffs[1], ParameterExpression) - - # Nested ListOp - sum_nested = SummedOp([X, sum1]) - self.assertRaises(TypeError, lambda: sum_nested.coeffs) - - def test_add(self): - """add test""" - pauli_sum = 3 * X + Y - self.assertIsInstance(pauli_sum, PauliSumOp) - expected = PauliSumOp(3.0 * SparsePauliOp(Pauli("X")) + SparsePauliOp(Pauli("Y"))) - self.assertEqual(pauli_sum, expected) - - pauli_sum = X + Y - summed_op = SummedOp([X, Y]) - self.assertEqual(pauli_sum, summed_op) - - a = Parameter("a") - b = Parameter("b") - actual = a * PauliSumOp.from_list([("X", 2)]) + b * PauliSumOp.from_list([("Y", 1)]) - expected = SummedOp( - [PauliSumOp.from_list([("X", 2)], a), PauliSumOp.from_list([("Y", 1)], b)] - ) - self.assertEqual(actual, expected) - - def test_mul(self): - """multiplication test""" - target = 2 * (X + Z) - self.assertEqual(target.coeff, 1) - self.assertListEqual(target.primitive.to_list(), [("X", (2 + 0j)), ("Z", (2 + 0j))]) - - target = 0 * (X + Z) - self.assertEqual(target.coeff, 0) - self.assertListEqual(target.primitive.to_list(), [("X", (1 + 0j)), ("Z", (1 + 0j))]) - - beta = Parameter("β") - target = beta * (X + Z) - self.assertEqual(target.coeff, 1.0 * beta) - self.assertListEqual(target.primitive.to_list(), [("X", (1 + 0j)), ("Z", (1 + 0j))]) - - def test_adjoint(self): - """adjoint test""" - pauli_sum = PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), coeff=3) - expected = PauliSumOp(SparsePauliOp(Pauli("XYZX")), coeff=6) - - self.assertEqual(pauli_sum.adjoint(), expected) - - pauli_sum = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]), coeff=3j) - expected = PauliSumOp(SparsePauliOp(Pauli("XYZY")), coeff=-6j) - self.assertEqual(pauli_sum.adjoint(), expected) - - pauli_sum = PauliSumOp(SparsePauliOp(Pauli("X"), coeffs=[1])) - self.assertEqual(pauli_sum.adjoint(), pauli_sum) - - pauli_sum = PauliSumOp(SparsePauliOp(Pauli("Y"), coeffs=[1])) - self.assertEqual(pauli_sum.adjoint(), pauli_sum) - - pauli_sum = PauliSumOp(SparsePauliOp(Pauli("Z"), coeffs=[1])) - self.assertEqual(pauli_sum.adjoint(), pauli_sum) - - pauli_sum = (Z ^ Z) + (Y ^ I) - self.assertEqual(pauli_sum.adjoint(), pauli_sum) - - def test_equals(self): - """equality test""" - - self.assertNotEqual((X ^ X) + (Y ^ Y), X + Y) - self.assertEqual((X ^ X) + (Y ^ Y), (Y ^ Y) + (X ^ X)) - self.assertEqual(0 * X + I, I) - self.assertEqual(I, 0 * X + I) - - theta = ParameterVector("theta", 2) - pauli_sum0 = theta[0] * (X + Z) - pauli_sum1 = theta[1] * (X + Z) - expected = PauliSumOp( - SparsePauliOp(Pauli("X")) + SparsePauliOp(Pauli("Z")), - coeff=1.0 * theta[0], - ) - self.assertEqual(pauli_sum0, expected) - self.assertNotEqual(pauli_sum1, expected) - - def test_tensor(self): - """Test for tensor operation""" - with self.subTest("Test 1"): - pauli_sum = ((I - Z) ^ (I - Z)) + ((X - Y) ^ (X + Y)) - expected = (I ^ I) - (I ^ Z) - (Z ^ I) + (Z ^ Z) + (X ^ X) + (X ^ Y) - (Y ^ X) - (Y ^ Y) - self.assertEqual(pauli_sum, expected) - - with self.subTest("Test 2"): - pauli_sum = (Z + I) ^ Z - expected = (Z ^ Z) + (I ^ Z) - self.assertEqual(pauli_sum, expected) - - with self.subTest("Test 3"): - pauli_sum = Z ^ (Z + I) - expected = (Z ^ Z) + (Z ^ I) - self.assertEqual(pauli_sum, expected) - - @data(([1, 2, 4], "XIYZI"), ([2, 1, 0], "ZYX")) - @unpack - def test_permute(self, permutation, expected_pauli): - """Test the permute method.""" - pauli_sum = PauliSumOp(SparsePauliOp.from_list([("XYZ", 1)])) - expected = PauliSumOp(SparsePauliOp.from_list([(expected_pauli, 1)])) - permuted = pauli_sum.permute(permutation) - - with self.subTest(msg="test permutated object"): - self.assertEqual(permuted, expected) - - with self.subTest(msg="test original object is unchanged"): - original = PauliSumOp(SparsePauliOp.from_list([("XYZ", 1)])) - self.assertEqual(pauli_sum, original) - - @data([1, 2, 1], [1, 2, -1]) - def test_permute_invalid(self, permutation): - """Test the permute method raises an error on invalid permutations.""" - pauli_sum = PauliSumOp(SparsePauliOp((X ^ Y ^ Z).primitive)) - - with self.assertRaises(OpflowError): - pauli_sum.permute(permutation) - - def test_compose(self): - """compose test""" - target = (X + Z) @ (Y + Z) - expected = 1j * Z - 1j * Y - 1j * X + I - self.assertEqual(target, expected) - - observable = (X ^ X) + (Y ^ Y) + (Z ^ Z) - state = CircuitStateFn((CX @ (X ^ H @ X)).to_circuit()) - self.assertAlmostEqual((~OperatorStateFn(observable) @ state).eval(), -3) - - def test_to_matrix(self): - """test for to_matrix method""" - target = (Z + Y).to_matrix() - expected = np.array([[1.0, -1j], [1j, -1]]) - np.testing.assert_array_equal(target, expected) - - def test_str(self): - """str test""" - target = 3.0 * (X + 2.0 * Y - 4.0 * Z) - expected = "3.0 * X\n+ 6.0 * Y\n- 12.0 * Z" - self.assertEqual(str(target), expected) - - alpha = Parameter("α") - target = alpha * (X + 2.0 * Y - 4.0 * Z) - expected = "1.0*α * (\n 1.0 * X\n + 2.0 * Y\n - 4.0 * Z\n)" - self.assertEqual(str(target), expected) - - def test_eval(self): - """eval test""" - target0 = (2 * (X ^ Y ^ Z) + 3 * (X ^ X ^ Z)).eval("000") - target1 = (2 * (X ^ Y ^ Z) + 3 * (X ^ X ^ Z)).eval(Zero ^ 3) - expected = DictStateFn({"110": (3 + 2j)}) - self.assertEqual(target0, expected) - self.assertEqual(target1, expected) - - phi = 0.5 * ((One + Zero) ^ 2) - zero_op = (Z + I) / 2 - one_op = (I - Z) / 2 - h1 = one_op ^ I - h2 = one_op ^ (one_op + zero_op) - h2a = one_op ^ one_op - h2b = one_op ^ zero_op - self.assertEqual((~OperatorStateFn(h1) @ phi).eval(), 0.5) - self.assertEqual((~OperatorStateFn(h2) @ phi).eval(), 0.5) - self.assertEqual((~OperatorStateFn(h2a) @ phi).eval(), 0.25) - self.assertEqual((~OperatorStateFn(h2b) @ phi).eval(), 0.25) - - pauli_op = (Z ^ I ^ X) + (I ^ I ^ Y) - mat_op = pauli_op.to_matrix_op() - full_basis = ["".join(b) for b in product("01", repeat=pauli_op.num_qubits)] - for bstr1, bstr2 in product(full_basis, full_basis): - self.assertEqual(pauli_op.eval(bstr1).eval(bstr2), mat_op.eval(bstr1).eval(bstr2)) - - def test_exp_i(self): - """exp_i test""" - # TODO: add tests when special methods are added - pass - - def test_to_instruction(self): - """test for to_instruction""" - target = ((X + Z) / np.sqrt(2)).to_instruction() - qc = QuantumCircuit(1) - qc.u(np.pi / 2, 0, np.pi, 0) - qc_out = transpile(target.definition, basis_gates=["u"]) - self.assertEqual(qc_out, qc) - - def test_to_pauli_op(self): - """test to_pauli_op method""" - target = X + Y - self.assertIsInstance(target, PauliSumOp) - expected = SummedOp([X, Y]) - self.assertEqual(target.to_pauli_op(), expected) - - def test_getitem(self): - """test get item method""" - target = X + Z - self.assertEqual(target[0], PauliSumOp(SparsePauliOp(X.primitive))) - self.assertEqual(target[1], PauliSumOp(SparsePauliOp(Z.primitive))) - - def test_len(self): - """test len""" - target = X + Y + Z - self.assertEqual(len(target), 3) - - def test_reduce(self): - """test reduce""" - target = X + X + Z - self.assertEqual(len(target.reduce()), 2) - - def test_to_spmatrix(self): - """test to_spmatrix""" - target = X + Y - expected = csr_matrix([[0, 1 - 1j], [1 + 1j, 0]]) - self.assertEqual((target.to_spmatrix() - expected).nnz, 0) - - def test_from_list(self): - """test from_list""" - target = PauliSumOp.from_list( - [ - ("II", -1.052373245772859), - ("IZ", 0.39793742484318045), - ("ZI", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] - ) - expected = ( - -1.052373245772859 * (I ^ I) - + 0.39793742484318045 * (I ^ Z) - - 0.39793742484318045 * (Z ^ I) - - 0.01128010425623538 * (Z ^ Z) - + 0.18093119978423156 * (X ^ X) - ) - self.assertEqual(target, expected) - - a = Parameter("a") - target = PauliSumOp.from_list([("X", 0.5 * a), ("Y", -0.5j * a)], dtype=object) - expected = PauliSumOp( - SparsePauliOp.from_list([("X", 0.5 * a), ("Y", -0.5j * a)], dtype=object) - ) - self.assertEqual(target.primitive, expected.primitive) - - def test_matrix_iter(self): - """Test PauliSumOp dense matrix_iter method.""" - labels = ["III", "IXI", "IYY", "YIZ", "XYZ", "III"] - coeffs = np.array([1, 2, 3, 4, 5, 6]) - paulis = PauliList(labels) - coeff = 10 - op = PauliSumOp(SparsePauliOp(paulis, coeffs), coeff) - for idx, i in enumerate(op.matrix_iter()): - self.assertTrue(np.array_equal(i, coeff * coeffs[idx] * Pauli(labels[idx]).to_matrix())) - - def test_matrix_iter_sparse(self): - """Test PauliSumOp sparse matrix_iter method.""" - labels = ["III", "IXI", "IYY", "YIZ", "XYZ", "III"] - coeffs = np.array([1, 2, 3, 4, 5, 6]) - coeff = 10 - paulis = PauliList(labels) - op = PauliSumOp(SparsePauliOp(paulis, coeffs), coeff) - for idx, i in enumerate(op.matrix_iter(sparse=True)): - self.assertTrue( - np.array_equal(i.toarray(), coeff * coeffs[idx] * Pauli(labels[idx]).to_matrix()) - ) - - def test_is_hermitian(self): - """Test is_hermitian method""" - with self.subTest("True test"): - target = PauliSumOp.from_list( - [ - ("II", -1.052373245772859), - ("IZ", 0.39793742484318045), - ("ZI", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] - ) - self.assertTrue(target.is_hermitian()) - - with self.subTest("False test"): - target = PauliSumOp.from_list( - [ - ("II", -1.052373245772859), - ("IZ", 0.39793742484318045j), - ("ZI", -0.39793742484318045), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423156), - ] - ) - self.assertFalse(target.is_hermitian()) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_state_construction.py b/test/python/opflow/test_state_construction.py deleted file mode 100644 index 05d595c18da4..000000000000 --- a/test/python/opflow/test_state_construction.py +++ /dev/null @@ -1,250 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Operator construction, including OpPrimitives and singletons.""" - -import unittest -from test.python.opflow import QiskitOpflowTestCase -import numpy as np - -from qiskit import QuantumCircuit, BasicAer, execute -from qiskit.circuit import ParameterVector -from qiskit.quantum_info import Statevector - -from qiskit.opflow import ( - StateFn, - Zero, - One, - Plus, - Minus, - PrimitiveOp, - CircuitOp, - SummedOp, - H, - I, - Z, - X, - Y, - CX, - CircuitStateFn, - DictToCircuitSum, -) - - -class TestStateConstruction(QiskitOpflowTestCase): - """State Construction tests.""" - - def test_state_singletons(self): - """state singletons test""" - self.assertEqual(Zero.primitive, {"0": 1}) - self.assertEqual(One.primitive, {"1": 1}) - - self.assertEqual((Zero ^ 5).primitive, {"00000": 1}) - self.assertEqual((One ^ 5).primitive, {"11111": 1}) - self.assertEqual(((Zero ^ One) ^ 3).primitive, {"010101": 1}) - - def test_zero_broadcast(self): - """zero broadcast test""" - np.testing.assert_array_almost_equal(((H ^ 5) @ Zero).to_matrix(), (Plus ^ 5).to_matrix()) - - def test_state_to_matrix(self): - """state to matrix test""" - np.testing.assert_array_equal(Zero.to_matrix(), np.array([1, 0])) - np.testing.assert_array_equal(One.to_matrix(), np.array([0, 1])) - np.testing.assert_array_almost_equal( - Plus.to_matrix(), (Zero.to_matrix() + One.to_matrix()) / (np.sqrt(2)) - ) - np.testing.assert_array_almost_equal( - Minus.to_matrix(), (Zero.to_matrix() - One.to_matrix()) / (np.sqrt(2)) - ) - - # TODO Not a great test because doesn't test against validated values - # or test internal representation. Fix this. - gnarly_state = (One ^ Plus ^ Zero ^ Minus * 0.3) @ StateFn( - Statevector.from_label("r0+l") - ) + (StateFn(X ^ Z ^ Y ^ I) * 0.1j) - gnarly_mat = gnarly_state.to_matrix() - gnarly_mat_separate = (One ^ Plus ^ Zero ^ Minus * 0.3).to_matrix() - gnarly_mat_separate = np.dot( - gnarly_mat_separate, StateFn(Statevector.from_label("r0+l")).to_matrix() - ) - gnarly_mat_separate = gnarly_mat_separate + (StateFn(X ^ Z ^ Y ^ I) * 0.1j).to_matrix() - np.testing.assert_array_almost_equal(gnarly_mat, gnarly_mat_separate) - - def test_qiskit_result_instantiation(self): - """qiskit result instantiation test""" - qc = QuantumCircuit(3) - # REMEMBER: This is Qubit 2 in Operator land. - qc.h(0) - sv_res = execute(qc, BasicAer.get_backend("statevector_simulator")).result() - sv_vector = sv_res.get_statevector() - qc_op = PrimitiveOp(qc) @ Zero - - qasm_res = execute( - qc_op.to_circuit(meas=True), BasicAer.get_backend("qasm_simulator") - ).result() - - np.testing.assert_array_almost_equal( - StateFn(sv_res).to_matrix(), [0.5**0.5, 0.5**0.5, 0, 0, 0, 0, 0, 0] - ) - np.testing.assert_array_almost_equal( - StateFn(sv_vector).to_matrix(), [0.5**0.5, 0.5**0.5, 0, 0, 0, 0, 0, 0] - ) - np.testing.assert_array_almost_equal( - StateFn(qasm_res).to_matrix(), [0.5**0.5, 0.5**0.5, 0, 0, 0, 0, 0, 0], decimal=1 - ) - - np.testing.assert_array_almost_equal( - ((I ^ I ^ H) @ Zero).to_matrix(), [0.5**0.5, 0.5**0.5, 0, 0, 0, 0, 0, 0] - ) - np.testing.assert_array_almost_equal( - qc_op.to_matrix(), [0.5**0.5, 0.5**0.5, 0, 0, 0, 0, 0, 0] - ) - - def test_state_meas_composition(self): - """state meas composition test""" - pass - # print((~Zero^4).eval(Zero^4)) - # print((~One^4).eval(Zero^4)) - # print((~One ^ 4).eval(One ^ 4)) - # print(StateFn(I^Z, is_measurement=True).eval(One^2)) - - def test_add_direct(self): - """add direct test""" - wf = StateFn({"101010": 0.5, "111111": 0.3}) + (Zero ^ 6) - self.assertEqual(wf.primitive, {"101010": 0.5, "111111": 0.3, "000000": 1.0}) - wf = (4 * StateFn({"101010": 0.5, "111111": 0.3})) + ((3 + 0.1j) * (Zero ^ 6)) - self.assertEqual( - wf.primitive, {"000000": (3 + 0.1j), "101010": (2 + 0j), "111111": (1.2 + 0j)} - ) - - def test_circuit_state_fn_from_dict_as_sum(self): - """state fn circuit from dict as sum test""" - statedict = {"1010101": 0.5, "1000000": 0.1, "0000000": 0.2j, "1111111": 0.5j} - sfc_sum = CircuitStateFn.from_dict(statedict) - self.assertIsInstance(sfc_sum, SummedOp) - for sfc_op in sfc_sum.oplist: - self.assertIsInstance(sfc_op, CircuitStateFn) - samples = sfc_op.sample() - self.assertIn(list(samples.keys())[0], statedict) - self.assertEqual(sfc_op.coeff, statedict[list(samples.keys())[0]]) - np.testing.assert_array_almost_equal(StateFn(statedict).to_matrix(), sfc_sum.to_matrix()) - - def test_circuit_state_fn_from_dict_initialize(self): - """state fn circuit from dict initialize test""" - statedict = {"101": 0.5, "100": 0.1, "000": 0.2, "111": 0.5} - sfc = CircuitStateFn.from_dict(statedict) - self.assertIsInstance(sfc, CircuitStateFn) - samples = sfc.sample() - np.testing.assert_array_almost_equal( - StateFn(statedict).to_matrix(), np.round(sfc.to_matrix(), decimals=1) - ) - for k, v in samples.items(): - self.assertIn(k, statedict) - # It's ok if these are far apart because the dict is sampled. - self.assertAlmostEqual(v, np.abs(statedict[k]) ** 0.5, delta=0.5) - - # Follows same code path as above, but testing to be thorough - sfc_vector = CircuitStateFn.from_vector(StateFn(statedict).to_matrix()) - np.testing.assert_array_almost_equal(StateFn(statedict).to_matrix(), sfc_vector.to_matrix()) - - # #1276 - def test_circuit_state_fn_from_complex_vector_initialize(self): - """state fn circuit from complex vector initialize test""" - sfc = CircuitStateFn.from_vector(np.array([1j / np.sqrt(2), 0, 1j / np.sqrt(2), 0])) - self.assertIsInstance(sfc, CircuitStateFn) - - def test_sampling(self): - """state fn circuit from dict initialize test""" - statedict = {"101": 0.5 + 1.0j, "100": 0.1 + 2.0j, "000": 0.2 + 0.0j, "111": 0.5 + 1.0j} - sfc = CircuitStateFn.from_dict(statedict) - circ_samples = sfc.sample() - dict_samples = StateFn(statedict).sample() - vec_samples = StateFn(statedict).to_matrix_op().sample() - for k, v in circ_samples.items(): - self.assertIn(k, dict_samples) - self.assertIn(k, vec_samples) - # It's ok if these are far apart because the dict is sampled. - self.assertAlmostEqual(v, np.abs(dict_samples[k]) ** 0.5, delta=0.5) - self.assertAlmostEqual(v, np.abs(vec_samples[k]) ** 0.5, delta=0.5) - - def test_dict_to_circuit_sum(self): - """Test DictToCircuitSum converter.""" - # Test qubits < entires, so dict is converted to Initialize CircuitStateFn - dict_state_3q = StateFn({"101": 0.5, "100": 0.1, "000": 0.2, "111": 0.5}) - circuit_state_3q = DictToCircuitSum().convert(dict_state_3q) - self.assertIsInstance(circuit_state_3q, CircuitStateFn) - np.testing.assert_array_almost_equal( - dict_state_3q.to_matrix(), circuit_state_3q.to_matrix() - ) - - # Test qubits >= entires, so dict is converted to Initialize CircuitStateFn - dict_state_4q = dict_state_3q ^ Zero - circuit_state_4q = DictToCircuitSum().convert(dict_state_4q) - self.assertIsInstance(circuit_state_4q, SummedOp) - np.testing.assert_array_almost_equal( - dict_state_4q.to_matrix(), circuit_state_4q.to_matrix() - ) - - # Test VectorStateFn conversion - vect_state_3q = dict_state_3q.to_matrix_op() - circuit_state_3q_vect = DictToCircuitSum().convert(vect_state_3q) - self.assertIsInstance(circuit_state_3q_vect, CircuitStateFn) - np.testing.assert_array_almost_equal( - vect_state_3q.to_matrix(), circuit_state_3q_vect.to_matrix() - ) - - def test_circuit_permute(self): - r"""Test the CircuitStateFn's .permute method""" - perm = range(7)[::-1] - c_op = ( - ((CX ^ 3) ^ X) - @ (H ^ 7) - @ (X ^ Y ^ Z ^ I ^ X ^ X ^ X) - @ (Y ^ (CX ^ 3)) - @ (X ^ Y ^ Z ^ I ^ X ^ X ^ X) - ) @ Zero - c_op_perm = c_op.permute(perm) - self.assertNotEqual(c_op, c_op_perm) - c_op_id = c_op_perm.permute(perm) - self.assertEqual(c_op, c_op_id) - - def test_primitive_param_binding(self): - """Test that assign_parameters binds parameters of both the underlying primitive and coeffs.""" - theta = ParameterVector("theta", 2) - # only OperatorStateFn can have a primitive with a parameterized coefficient - op = StateFn(theta[0] * X) * theta[1] - bound = op.assign_parameters(dict(zip(theta, [0.2, 0.3]))) - self.assertEqual(bound.coeff, 0.3) - self.assertEqual(bound.primitive.coeff, 0.2) - - # #6003 - def test_flatten_statefn_composed_with_composed_op(self): - """Test that composing a StateFn with a ComposedOp constructs a single ComposedOp""" - circuit = QuantumCircuit(1) - vector = [1, 0] - ex = ~StateFn(I) @ (CircuitOp(circuit) @ StateFn(vector)) - self.assertEqual(len(ex), 3) - self.assertEqual(ex.eval(), 1) - - def test_tensorstate_to_matrix(self): - """Test tensored states to matrix works correctly with a global coefficient. - - Regression test of Qiskit/qiskit-terra#9398. - """ - state = 0.5 * (Plus ^ Zero) - expected = 1 / (2 * np.sqrt(2)) * np.array([1, 0, 1, 0]) - np.testing.assert_almost_equal(state.to_matrix(), expected) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_state_op_meas_evals.py b/test/python/opflow/test_state_op_meas_evals.py deleted file mode 100644 index e6d23390ea78..000000000000 --- a/test/python/opflow/test_state_op_meas_evals.py +++ /dev/null @@ -1,248 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=no-name-in-module - - -"""Test Operator construction, including OpPrimitives and singletons.""" - -import unittest -from test.python.opflow import QiskitOpflowTestCase -from ddt import ddt, data -import numpy - -from qiskit.circuit import QuantumCircuit, Parameter -from qiskit.utils import QuantumInstance -from qiskit.opflow import StateFn, Zero, One, H, X, I, Z, Plus, Minus, CircuitSampler, ListOp -from qiskit.opflow.exceptions import OpflowError -from qiskit.quantum_info import Statevector - - -@ddt -class TestStateOpMeasEvals(QiskitOpflowTestCase): - """Tests of evals of Meas-Operator-StateFn combos.""" - - def test_statefn_overlaps(self): - """state functions overlaps test""" - wf = (4 * StateFn({"101010": 0.5, "111111": 0.3})) + ((3 + 0.1j) * (Zero ^ 6)) - wf_vec = StateFn(wf.to_matrix()) - self.assertAlmostEqual(wf.adjoint().eval(wf), 14.45) - self.assertAlmostEqual(wf_vec.adjoint().eval(wf_vec), 14.45) - self.assertAlmostEqual(wf_vec.adjoint().eval(wf), 14.45) - self.assertAlmostEqual(wf.adjoint().eval(wf_vec), 14.45) - - def test_wf_evals_x(self): - """wf evals x test""" - qbits = 4 - - wf = ((Zero ^ qbits) + (One ^ qbits)) * (1 / 2**0.5) - # Note: wf = Plus^qbits fails because TensoredOp can't handle it. - wf_vec = StateFn(wf.to_matrix()) - op = X ^ qbits - # op = I^6 - self.assertAlmostEqual(wf.adjoint().eval(op.eval(wf)), 1) - self.assertAlmostEqual(wf_vec.adjoint().eval(op.eval(wf)), 1) - self.assertAlmostEqual(wf.adjoint().eval(op.eval(wf_vec)), 1) - self.assertAlmostEqual(wf_vec.adjoint().eval(op.eval(wf_vec)), 1) - - # op = (H^X^Y)^2 - op = H ^ 6 - wf = ((Zero ^ 6) + (One ^ 6)) * (1 / 2**0.5) - wf_vec = StateFn(wf.to_matrix()) - # print(wf.adjoint().to_matrix() @ op.to_matrix() @ wf.to_matrix()) - self.assertAlmostEqual(wf.adjoint().eval(op.eval(wf)), 0.25) - self.assertAlmostEqual(wf_vec.adjoint().eval(op.eval(wf)), 0.25) - self.assertAlmostEqual(wf.adjoint().eval(op.eval(wf_vec)), 0.25) - self.assertAlmostEqual(wf_vec.adjoint().eval(op.eval(wf_vec)), 0.25) - - def test_coefficients_correctly_propagated(self): - """Test that the coefficients in SummedOp and states are correctly used.""" - try: - from qiskit.providers.aer import Aer - except Exception as ex: # pylint: disable=broad-except - self.skipTest(f"Aer doesn't appear to be installed. Error: '{str(ex)}'") - return - with self.subTest("zero coeff in SummedOp"): - op = 0 * (I + Z) - state = Plus - self.assertEqual((~StateFn(op) @ state).eval(), 0j) - - backend = Aer.get_backend("aer_simulator") - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance(backend, seed_simulator=97, seed_transpiler=97) - op = I - with self.subTest("zero coeff in summed StateFn and CircuitSampler"): - with self.assertWarns(DeprecationWarning): - state = 0 * (Plus + Minus) - sampler = CircuitSampler(q_instance).convert(~StateFn(op) @ state) - self.assertEqual(sampler.eval(), 0j) - - with self.subTest("coeff gets squared in CircuitSampler shot-based readout"): - with self.assertWarns(DeprecationWarning): - state = (Plus + Minus) / numpy.sqrt(2) - sampler = CircuitSampler(q_instance).convert(~StateFn(op) @ state) - self.assertAlmostEqual(sampler.eval(), 1 + 0j) - - def test_is_measurement_correctly_propagated(self): - """Test if is_measurement property of StateFn is propagated to converted StateFn.""" - try: - from qiskit.providers.aer import Aer - except Exception as ex: # pylint: disable=broad-except - self.skipTest(f"Aer doesn't appear to be installed. Error: '{str(ex)}'") - return - backend = Aer.get_backend("aer_simulator") - - with self.assertWarns(DeprecationWarning): - q_instance = QuantumInstance(backend) # no seeds needed since no values are compared - state = Plus - sampler = CircuitSampler(q_instance).convert(~state @ state) - self.assertTrue(sampler.oplist[0].is_measurement) - - def test_parameter_binding_on_listop(self): - """Test passing a ListOp with differing parameters works with the circuit sampler.""" - try: - from qiskit.providers.aer import Aer - except Exception as ex: # pylint: disable=broad-except - self.skipTest(f"Aer doesn't appear to be installed. Error: '{str(ex)}'") - return - x, y = Parameter("x"), Parameter("y") - - circuit1 = QuantumCircuit(1) - circuit1.p(0.2, 0) - circuit2 = QuantumCircuit(1) - circuit2.p(x, 0) - circuit3 = QuantumCircuit(1) - circuit3.p(y, 0) - - with self.assertWarns(DeprecationWarning): - bindings = {x: -0.4, y: 0.4} - listop = ListOp([StateFn(circuit) for circuit in [circuit1, circuit2, circuit3]]) - sampler = CircuitSampler(Aer.get_backend("aer_simulator")) - sampled = sampler.convert(listop, params=bindings) - - self.assertTrue(all(len(op.parameters) == 0 for op in sampled.oplist)) - - def test_list_op_eval_coeff_with_nonlinear_combofn(self): - """Test evaluating a ListOp with non-linear combo function works with coefficients.""" - - state = One - op = ListOp(5 * [I], coeff=2, combo_fn=numpy.prod) - expr1 = ~StateFn(op) @ state - - expr2 = ListOp(5 * [~state @ I @ state], coeff=2, combo_fn=numpy.prod) - - self.assertEqual(expr1.eval(), 2) # if the coeff is propagated too far the result is 4 - self.assertEqual(expr2.eval(), 2) - - def test_single_parameter_binds(self): - """Test passing parameter binds as a dictionary to the circuit sampler.""" - try: - from qiskit.providers.aer import Aer - except Exception as ex: # pylint: disable=broad-except - self.skipTest(f"Aer doesn't appear to be installed. Error: '{str(ex)}'") - return - - x = Parameter("x") - circuit = QuantumCircuit(1) - circuit.ry(x, 0) - - with self.assertWarns(DeprecationWarning): - expr = ~StateFn(H) @ StateFn(circuit) - sampler = CircuitSampler(Aer.get_backend("aer_simulator_statevector")) - res = sampler.convert(expr, params={x: 0}).eval() - - self.assertIsInstance(res, complex) - - @data("all", "last") - def test_circuit_sampler_caching(self, caching): - """Test caching all operators works.""" - try: - from qiskit.providers.aer import Aer - except Exception as ex: # pylint: disable=broad-except - self.skipTest(f"Aer doesn't appear to be installed. Error: '{str(ex)}'") - return - - x = Parameter("x") - circuit = QuantumCircuit(1) - circuit.ry(x, 0) - - with self.assertWarns(DeprecationWarning): - - expr1 = ~StateFn(H) @ StateFn(circuit) - expr2 = ~StateFn(X) @ StateFn(circuit) - sampler = CircuitSampler(Aer.get_backend("aer_simulator_statevector"), caching=caching) - - res1 = sampler.convert(expr1, params={x: 0}).eval() - res2 = sampler.convert(expr2, params={x: 0}).eval() - res3 = sampler.convert(expr1, params={x: 0}).eval() - res4 = sampler.convert(expr2, params={x: 0}).eval() - - self.assertEqual(res1, res3) - self.assertEqual(res2, res4) - if caching == "last": - self.assertEqual(len(sampler._cached_ops.keys()), 1) - else: - self.assertEqual(len(sampler._cached_ops.keys()), 2) - - def test_adjoint_nonunitary_circuit_raises(self): - """Test adjoint on a non-unitary circuit raises a OpflowError instead of CircuitError.""" - circuit = QuantumCircuit(1) - circuit.reset(0) - - with self.assertRaises(OpflowError): - _ = StateFn(circuit).adjoint() - - def test_evaluating_nonunitary_circuit_state(self): - """Test evaluating a circuit works even if it contains non-unitary instruction (resets). - - TODO: allow this for (~StateFn(circuit) @ op @ StateFn(circuit)), but this requires - refactoring how the AerPauliExpectation works, since that currently relies on - composing with CircuitMeasurements - """ - circuit = QuantumCircuit(1) - circuit.initialize([0, 1], [0]) - - op = Z - res = (~StateFn(op) @ StateFn(circuit)).eval() - - self.assertAlmostEqual(-1 + 0j, res) - - def test_quantum_instance_with_backend_shots(self): - """Test sampling a circuit where the backend has shots attached.""" - try: - from qiskit.providers.aer import AerSimulator - except Exception as ex: # pylint: disable=broad-except - self.skipTest(f"Aer doesn't appear to be installed. Error: '{str(ex)}'") - - backend = AerSimulator(shots=10) - - with self.assertWarns(DeprecationWarning): - sampler = CircuitSampler(backend) - res = sampler.convert(~Plus @ Plus).eval() - self.assertAlmostEqual(res, 1 + 0j, places=2) - - def test_adjoint_vector_to_circuit_fn(self): - """Test it is possible to adjoint a VectorStateFn that was converted to a CircuitStateFn.""" - - left = StateFn([0, 1]) - left_circuit = left.to_circuit_op().primitive - - right_circuit = QuantumCircuit(1) - right_circuit.x(0) - - circuit = left_circuit.inverse().compose(right_circuit) - - self.assertTrue(Statevector(circuit).equiv([1, 0])) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_tapered_pauli.py b/test/python/opflow/test_tapered_pauli.py deleted file mode 100644 index 967ca82373b8..000000000000 --- a/test/python/opflow/test_tapered_pauli.py +++ /dev/null @@ -1,57 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test TaperedPauliSumOp""" - -import unittest -from test.python.opflow import QiskitOpflowTestCase - -from qiskit.circuit import Parameter -from qiskit.opflow import PauliSumOp, TaperedPauliSumOp, Z2Symmetries -from qiskit.quantum_info import Pauli, SparsePauliOp - - -class TestZ2Symmetries(QiskitOpflowTestCase): - """Z2Symmetries tests.""" - - def setUp(self): - super().setUp() - z2_symmetries = Z2Symmetries( - [Pauli("IIZI"), Pauli("ZIII")], [Pauli("IIXI"), Pauli("XIII")], [1, 3], [-1, 1] - ) - self.primitive = SparsePauliOp.from_list( - [ - ("II", (-1.052373245772859)), - ("ZI", (-0.39793742484318007)), - ("IZ", (0.39793742484318007)), - ("ZZ", (-0.01128010425623538)), - ("XX", (0.18093119978423142)), - ] - ) - self.tapered_qubit_op = TaperedPauliSumOp(self.primitive, z2_symmetries) - - def test_multiply_parameter(self): - """test for multiplication of parameter""" - param = Parameter("c") - expected = PauliSumOp(self.primitive, coeff=param) - self.assertEqual(param * self.tapered_qubit_op, expected) - - def test_assign_parameters(self): - """test assign_parameters""" - param = Parameter("c") - parameterized_op = param * self.tapered_qubit_op - expected = PauliSumOp(self.primitive, coeff=46) - self.assertEqual(parameterized_op.assign_parameters({param: 46}), expected) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/opflow/test_two_qubit_reduction.py b/test/python/opflow/test_two_qubit_reduction.py deleted file mode 100644 index 859d63446ebc..000000000000 --- a/test/python/opflow/test_two_qubit_reduction.py +++ /dev/null @@ -1,64 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test TwoQubitReduction""" - -from test.python.opflow import QiskitOpflowTestCase - -from qiskit.opflow import PauliSumOp, TwoQubitReduction, TaperedPauliSumOp, Z2Symmetries -from qiskit.quantum_info import Pauli, SparsePauliOp - - -class TestTwoQubitReduction(QiskitOpflowTestCase): - """TwoQubitReduction tests.""" - - def test_convert(self): - """convert test""" - - qubit_op = PauliSumOp.from_list( - [ - ("IIII", -0.8105479805373266), - ("IIIZ", 0.17218393261915552), - ("IIZZ", -0.22575349222402472), - ("IZZI", 0.1721839326191556), - ("ZZII", -0.22575349222402466), - ("IIZI", 0.1209126326177663), - ("IZZZ", 0.16892753870087912), - ("IXZX", -0.045232799946057854), - ("ZXIX", 0.045232799946057854), - ("IXIX", 0.045232799946057854), - ("ZXZX", -0.045232799946057854), - ("ZZIZ", 0.16614543256382414), - ("IZIZ", 0.16614543256382414), - ("ZZZZ", 0.17464343068300453), - ("ZIZI", 0.1209126326177663), - ] - ) - tapered_qubit_op = TwoQubitReduction(num_particles=2).convert(qubit_op) - self.assertIsInstance(tapered_qubit_op, TaperedPauliSumOp) - - primitive = SparsePauliOp.from_list( - [ - ("II", -1.052373245772859), - ("ZI", -0.39793742484318007), - ("IZ", 0.39793742484318007), - ("ZZ", -0.01128010425623538), - ("XX", 0.18093119978423142), - ] - ) - symmetries = [Pauli("IIZI"), Pauli("ZIII")] - sq_paulis = [Pauli("IIXI"), Pauli("XIII")] - sq_list = [1, 3] - tapering_values = [-1, 1] - z2_symmetries = Z2Symmetries(symmetries, sq_paulis, sq_list, tapering_values) - expected_op = TaperedPauliSumOp(primitive, z2_symmetries) - self.assertEqual(tapered_qubit_op, expected_op) diff --git a/test/python/opflow/test_z2_symmetries.py b/test/python/opflow/test_z2_symmetries.py deleted file mode 100644 index 37e8e0e16a27..000000000000 --- a/test/python/opflow/test_z2_symmetries.py +++ /dev/null @@ -1,112 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test Z2Symmetries""" - -from test.python.opflow import QiskitOpflowTestCase - -from qiskit.opflow import PauliSumOp, TaperedPauliSumOp, Z2Symmetries -from qiskit.quantum_info import Pauli, SparsePauliOp - - -class TestZ2Symmetries(QiskitOpflowTestCase): - """Z2Symmetries tests.""" - - def test_find_Z2_symmetries(self): - """test for find_Z2_symmetries""" - - qubit_op = PauliSumOp.from_list( - [ - ("II", -1.0537076071291125), - ("IZ", 0.393983679438514), - ("ZI", -0.39398367943851387), - ("ZZ", -0.01123658523318205), - ("XX", 0.1812888082114961), - ] - ) - z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) - self.assertEqual(z2_symmetries.symmetries, [Pauli("ZZ")]) - self.assertEqual(z2_symmetries.sq_paulis, [Pauli("IX")]) - self.assertEqual(z2_symmetries.sq_list, [0]) - self.assertEqual(z2_symmetries.tapering_values, None) - - tapered_op = z2_symmetries.taper(qubit_op)[1] - self.assertEqual(tapered_op.z2_symmetries.symmetries, [Pauli("ZZ")]) - self.assertEqual(tapered_op.z2_symmetries.sq_paulis, [Pauli("IX")]) - self.assertEqual(tapered_op.z2_symmetries.sq_list, [0]) - self.assertEqual(tapered_op.z2_symmetries.tapering_values, [-1]) - - z2_symmetries.tapering_values = [-1] - primitive = SparsePauliOp.from_list( - [ - ("I", -1.0424710218959303), - ("Z", -0.7879673588770277), - ("X", -0.18128880821149604), - ] - ) - expected_op = TaperedPauliSumOp(primitive, z2_symmetries) - self.assertEqual(tapered_op, expected_op) - - def test_taper_empty_operator(self): - """Test tapering of empty operator""" - z2_symmetries = Z2Symmetries( - symmetries=[Pauli("IIZI"), Pauli("IZIZ"), Pauli("ZIII")], - sq_paulis=[Pauli("IIXI"), Pauli("IIIX"), Pauli("XIII")], - sq_list=[1, 0, 3], - tapering_values=[1, -1, -1], - ) - empty_op = PauliSumOp.from_list([("IIII", 0.0)]) - tapered_op = z2_symmetries.taper(empty_op) - expected_op = PauliSumOp.from_list([("I", 0.0)]) - self.assertEqual(tapered_op, expected_op) - - def test_truncate_tapered_op(self): - """Test setting cutoff tolerances for the tapered operator works.""" - qubit_op = PauliSumOp.from_list( - [ - ("II", -1.0537076071291125), - ("IZ", 0.393983679438514), - ("ZI", -0.39398367943851387), - ("ZZ", -0.01123658523318205), - ("XX", 0.1812888082114961), - ] - ) - z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) - z2_symmetries.tol = 0.2 # removes the X part of the tapered op which is < 0.2 - - tapered_op = z2_symmetries.taper(qubit_op)[1] - primitive = SparsePauliOp.from_list( - [ - ("I", -1.0424710218959303), - ("Z", -0.7879673588770277), - ] - ) - expected_op = TaperedPauliSumOp(primitive, z2_symmetries) - self.assertEqual(tapered_op, expected_op) - - def test_twostep_tapering(self): - """Test the two-step tapering""" - qubit_op = PauliSumOp.from_list( - [ - ("II", -1.0537076071291125), - ("IZ", 0.393983679438514), - ("ZI", -0.39398367943851387), - ("ZZ", -0.01123658523318205), - ("XX", 0.1812888082114961), - ] - ) - z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) - tapered_op = z2_symmetries.taper(qubit_op) - - tapered_op_firststep = z2_symmetries.convert_clifford(qubit_op) - tapered_op_secondstep = z2_symmetries.taper_clifford(tapered_op_firststep) - self.assertEqual(tapered_op, tapered_op_secondstep) diff --git a/test/python/passmanager/__init__.py b/test/python/passmanager/__init__.py new file mode 100644 index 000000000000..d5b924250dc4 --- /dev/null +++ b/test/python/passmanager/__init__.py @@ -0,0 +1,54 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023 +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Pass manager test cases.""" + +import contextlib +import logging +import re +from itertools import zip_longest +from logging import getLogger + +from qiskit.test import QiskitTestCase + + +class PassManagerTestCase(QiskitTestCase): + """Test case for the pass manager module.""" + + @contextlib.contextmanager + def assertLogContains(self, expected_lines): + """A context manager that capture pass manager log. + + Args: + expected_lines (List[str]): Expected logs. Each element can be regular expression. + """ + try: + logger = getLogger() + with self.assertLogs(logger=logger, level=logging.DEBUG) as cm: + yield cm + finally: + recorded_lines = cm.output + for i, (expected, recorded) in enumerate(zip_longest(expected_lines, recorded_lines)): + expected = expected or "" + recorded = recorded or "" + if not re.search(expected, recorded): + raise AssertionError( + f"Log didn't match. Mismatch found at line #{i}.\n\n" + f"Expected:\n{self._format_log(expected_lines)}\n" + f"Recorded:\n{self._format_log(recorded_lines)}" + ) + + def _format_log(self, lines): + out = "" + for i, line in enumerate(lines): + out += f"#{i:02d}: {line}\n" + return out diff --git a/test/python/passmanager/test_generic_pass.py b/test/python/passmanager/test_generic_pass.py new file mode 100644 index 000000000000..c0071b966d65 --- /dev/null +++ b/test/python/passmanager/test_generic_pass.py @@ -0,0 +1,143 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023 +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +# pylint: disable=missing-class-docstring + +"""Pass manager test cases.""" + +from test.python.passmanager import PassManagerTestCase + +from logging import getLogger + +from qiskit.passmanager import GenericPass +from qiskit.passmanager import PassManagerState, WorkflowStatus, PropertySet +from qiskit.passmanager.compilation_status import RunState + + +class TestGenericPass(PassManagerTestCase): + """Tests for the GenericPass subclass.""" + + def setUp(self): + super().setUp() + + self.state = PassManagerState( + workflow_status=WorkflowStatus(), + property_set=PropertySet(), + ) + + def test_run_task(self): + """Test case: Simple successful task execution.""" + + class Task(GenericPass): + def run(self, passmanager_ir): + return passmanager_ir + + task = Task() + data = "test_data" + expected = [r"Pass: Task - (\d*\.)?\d+ \(ms\)"] + + with self.assertLogContains(expected): + task.execute(passmanager_ir=data, state=self.state) + self.assertEqual(self.state.workflow_status.count, 1) + self.assertIn(task, self.state.workflow_status.completed_passes) + self.assertEqual(self.state.workflow_status.previous_run, RunState.SUCCESS) + + def test_failure_task(self): + """Test case: Log is created regardless of success.""" + + class TestError(Exception): + pass + + class RaiseError(GenericPass): + def run(self, passmanager_ir): + raise TestError() + + task = RaiseError() + data = "test_data" + expected = [r"Pass: RaiseError - (\d*\.)?\d+ \(ms\)"] + + with self.assertLogContains(expected): + with self.assertRaises(TestError): + task.execute(passmanager_ir=data, state=self.state) + self.assertEqual(self.state.workflow_status.count, 0) + self.assertNotIn(task, self.state.workflow_status.completed_passes) + self.assertEqual(self.state.workflow_status.previous_run, RunState.FAIL) + + def test_requires(self): + """Test case: Dependency tasks are run in advance to user provided task.""" + + class TaskA(GenericPass): + def run(self, passmanager_ir): + return passmanager_ir + + class TaskB(GenericPass): + def __init__(self): + super().__init__() + self.requires = [TaskA()] + + def run(self, passmanager_ir): + return passmanager_ir + + task = TaskB() + data = "test_data" + expected = [ + r"Pass: TaskA - (\d*\.)?\d+ \(ms\)", + r"Pass: TaskB - (\d*\.)?\d+ \(ms\)", + ] + with self.assertLogContains(expected): + task.execute(passmanager_ir=data, state=self.state) + self.assertEqual(self.state.workflow_status.count, 2) + + def test_requires_in_list(self): + """Test case: Dependency tasks are not executed multiple times.""" + + class TaskA(GenericPass): + def run(self, passmanager_ir): + return passmanager_ir + + class TaskB(GenericPass): + def __init__(self): + super().__init__() + self.requires = [TaskA()] + + def run(self, passmanager_ir): + return passmanager_ir + + task = TaskB() + data = "test_data" + expected = [ + r"Pass: TaskB - (\d*\.)?\d+ \(ms\)", + ] + self.state.workflow_status.completed_passes.add(task.requires[0]) # already done + with self.assertLogContains(expected): + task.execute(passmanager_ir=data, state=self.state) + self.assertEqual(self.state.workflow_status.count, 1) + + def test_run_with_callable(self): + """Test case: Callable is called after generic pass is run.""" + + # pylint: disable=unused-argument + def test_callable(task, passmanager_ir, property_set, running_time, count): + logger = getLogger() + logger.info("%s is running on %s", task.name(), passmanager_ir) + + class Task(GenericPass): + def run(self, passmanager_ir): + return passmanager_ir + + task = Task() + data = "test_data" + expected = [ + r"Pass: Task - (\d*\.)?\d+ \(ms\)", + r"Task is running on test_data", + ] + with self.assertLogContains(expected): + task.execute(passmanager_ir=data, state=self.state, callback=test_callable) diff --git a/test/python/passmanager/test_passmanager.py b/test/python/passmanager/test_passmanager.py new file mode 100644 index 000000000000..b7ffdbd6029b --- /dev/null +++ b/test/python/passmanager/test_passmanager.py @@ -0,0 +1,126 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023 +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +# pylint: disable=missing-class-docstring + +"""Pass manager test cases.""" + +from test.python.passmanager import PassManagerTestCase + +from qiskit.passmanager import GenericPass, BasePassManager +from qiskit.passmanager.flow_controllers import DoWhileController, ConditionalController + + +class RemoveFive(GenericPass): + def run(self, passmanager_ir): + return passmanager_ir.replace("5", "") + + +class AddDigit(GenericPass): + def run(self, passmanager_ir): + return passmanager_ir + "0" + + +class CountDigits(GenericPass): + def run(self, passmanager_ir): + self.property_set["ndigits"] = len(passmanager_ir) + + +class ToyPassManager(BasePassManager): + def _passmanager_frontend(self, input_program, **kwargs): + return str(input_program) + + def _passmanager_backend(self, passmanager_ir, in_program, **kwargs): + return int(passmanager_ir) + + +class TestPassManager(PassManagerTestCase): + def test_single_task(self): + """Test case: Pass manager with a single task.""" + + task = RemoveFive() + data = 12345 + pm = ToyPassManager(task) + expected = [r"Pass: RemoveFive - (\d*\.)?\d+ \(ms\)"] + with self.assertLogContains(expected): + out = pm.run(data) + self.assertEqual(out, 1234) + + def test_property_set(self): + """Test case: Pass manager can access property set.""" + + task = CountDigits() + data = 12345 + pm = ToyPassManager(task) + pm.run(data) + self.assertDictEqual(pm.property_set, {"ndigits": 5}) + + def test_do_while_controller(self): + """Test case: Do while controller that repeats tasks until the condition is met.""" + + def _condition(property_set): + return property_set["ndigits"] < 7 + + controller = DoWhileController([AddDigit(), CountDigits()], do_while=_condition) + data = 12345 + pm = ToyPassManager(controller) + pm.property_set["ndigits"] = 5 + expected = [ + r"Pass: AddDigit - (\d*\.)?\d+ \(ms\)", + r"Pass: CountDigits - (\d*\.)?\d+ \(ms\)", + r"Pass: AddDigit - (\d*\.)?\d+ \(ms\)", + r"Pass: CountDigits - (\d*\.)?\d+ \(ms\)", + ] + with self.assertLogContains(expected): + out = pm.run(data) + self.assertEqual(out, 1234500) + + def test_conditional_controller(self): + """Test case: Conditional controller that run task when the condition is met.""" + + def _condition(property_set): + return property_set["ndigits"] > 6 + + controller = ConditionalController([RemoveFive()], condition=_condition) + data = [123456789, 45654, 36785554] + pm = ToyPassManager([CountDigits(), controller]) + out = pm.run(data) + self.assertListEqual(out, [12346789, 45654, 36784]) + + def test_string_input(self): + """Test case: Running tasks once for a single string input. + + Details: + When the pass manager receives a sequence of input values, + it duplicates itself and run the tasks on each input element in parallel. + If the input is string, this can be accidentally recognized as a sequence. + """ + + class StringPassManager(BasePassManager): + def _passmanager_frontend(self, input_program, **kwargs): + return input_program + + def _passmanager_backend(self, passmanager_ir, in_program, **kwargs): + return passmanager_ir + + class Task(GenericPass): + def run(self, passmanager_ir): + return passmanager_ir + + task = Task() + data = "12345" + pm = StringPassManager(task) + + # Should be run only one time + expected = [r"Pass: Task - (\d*\.)?\d+ \(ms\)"] + with self.assertLogContains(expected): + out = pm.run(data) + self.assertEqual(out, data) diff --git a/test/python/primitives/test_backend_estimator.py b/test/python/primitives/test_backend_estimator.py index 01f8c1fa251a..0df15f590728 100644 --- a/test/python/primitives/test_backend_estimator.py +++ b/test/python/primitives/test_backend_estimator.py @@ -13,9 +13,11 @@ """Tests for Estimator.""" import unittest -from test import combine -from test.python.transpiler._dummy_passes import DummyTP from unittest.mock import patch +from multiprocessing import Manager + +from test import combine +from test.python.transpiler._dummy_passes import DummyAP import numpy as np from ddt import ddt @@ -34,6 +36,18 @@ BACKENDS = [FakeNairobi(), FakeNairobiV2(), FakeBackendSimple()] +class CallbackPass(DummyAP): + """A dummy analysis pass that calls a callback when executed""" + + def __init__(self, message, callback): + super().__init__() + self.message = message + self.callback = callback + + def run(self, dag): + self.callback(self.message) + + @ddt class TestBackendEstimator(QiskitTestCase): """Test Estimator""" @@ -328,24 +342,40 @@ def test_bound_pass_manager(self): op = SparsePauliOp.from_list([("II", 1)]) with self.subTest("Test single circuit"): + messages = [] - dummy_pass = DummyTP() + def callback(msg): + messages.append(msg) - with patch.object(DummyTP, "run", wraps=dummy_pass.run) as mock_pass: - bound_pass = PassManager(dummy_pass) - estimator = BackendEstimator(backend=FakeNairobi(), bound_pass_manager=bound_pass) - _ = estimator.run(qc, op).result() - self.assertEqual(mock_pass.call_count, 1) + bound_counter = CallbackPass("bound_pass_manager", callback) + bound_pass = PassManager(bound_counter) + estimator = BackendEstimator(backend=FakeNairobi(), bound_pass_manager=bound_pass) + _ = estimator.run(qc, op).result() + expected = [ + "bound_pass_manager", + ] + self.assertEqual(messages, expected) with self.subTest("Test circuit batch"): - - dummy_pass = DummyTP() - - with patch.object(DummyTP, "run", wraps=dummy_pass.run) as mock_pass: - bound_pass = PassManager(dummy_pass) + with Manager() as manager: + # The multiprocessing manager is used to share data + # between different processes. Pass Managers parallelize + # execution for batches of circuits, so this is necessary + # to keep track of the callback calls for num_circuits > 1 + messages = manager.list() + + def callback(msg): # pylint: disable=function-redefined + messages.append(msg) + + bound_counter = CallbackPass("bound_pass_manager", callback) + bound_pass = PassManager(bound_counter) estimator = BackendEstimator(backend=FakeNairobi(), bound_pass_manager=bound_pass) _ = estimator.run([qc, qc], [op, op]).result() - self.assertEqual(mock_pass.call_count, 2) + expected = [ + "bound_pass_manager", + "bound_pass_manager", + ] + self.assertEqual(list(messages), expected) @combine(backend=BACKENDS) def test_layout(self, backend): diff --git a/test/python/primitives/test_backend_sampler.py b/test/python/primitives/test_backend_sampler.py index 86dc709f8983..b4b8d79e32a3 100644 --- a/test/python/primitives/test_backend_sampler.py +++ b/test/python/primitives/test_backend_sampler.py @@ -14,9 +14,10 @@ import math import unittest -from unittest.mock import patch +from multiprocessing import Manager + from test import combine -from test.python.transpiler._dummy_passes import DummyTP +from test.python.transpiler._dummy_passes import DummyAP import numpy as np from ddt import ddt @@ -34,6 +35,18 @@ BACKENDS = [FakeNairobi(), FakeNairobiV2()] +class CallbackPass(DummyAP): + """A dummy analysis pass that calls a callback when executed""" + + def __init__(self, message, callback): + super().__init__() + self.message = message + self.callback = callback + + def run(self, dag): + self.callback(self.message) + + @ddt class TestBackendSampler(QiskitTestCase): """Test BackendSampler""" @@ -117,7 +130,6 @@ def test_sample_run_multiple_circuits(self, backend): bell = self._circuit[1] sampler = BackendSampler(backend=backend) result = sampler.run([bell, bell, bell]).result() - # print([q.binary_probabilities() for q in result.quasi_dists]) self._compare_probs(result.quasi_dists[0], self._target[1]) self._compare_probs(result.quasi_dists[1], self._target[1]) self._compare_probs(result.quasi_dists[2], self._target[1]) @@ -387,24 +399,40 @@ def test_bound_pass_manager(self): """Test bound pass manager.""" with self.subTest("Test single circuit"): + messages = [] - dummy_pass = DummyTP() + def callback(msg): + messages.append(msg) - with patch.object(DummyTP, "run", wraps=dummy_pass.run) as mock_pass: - bound_pass = PassManager(dummy_pass) - sampler = BackendSampler(backend=FakeNairobi(), bound_pass_manager=bound_pass) - _ = sampler.run(self._circuit[0]).result() - self.assertEqual(mock_pass.call_count, 1) + bound_counter = CallbackPass("bound_pass_manager", callback) + bound_pass = PassManager(bound_counter) + sampler = BackendSampler(backend=FakeNairobi(), bound_pass_manager=bound_pass) + _ = sampler.run([self._circuit[0]]).result() + expected = [ + "bound_pass_manager", + ] + self.assertEqual(messages, expected) with self.subTest("Test circuit batch"): - - dummy_pass = DummyTP() - - with patch.object(DummyTP, "run", wraps=dummy_pass.run) as mock_pass: - bound_pass = PassManager(dummy_pass) + with Manager() as manager: + # The multiprocessing manager is used to share data + # between different processes. Pass Managers parallelize + # execution for batches of circuits, so this is necessary + # to keep track of the callback calls for num_circuits > 1 + messages = manager.list() + + def callback(msg): # pylint: disable=function-redefined + messages.append(msg) + + bound_counter = CallbackPass("bound_pass_manager", callback) + bound_pass = PassManager(bound_counter) sampler = BackendSampler(backend=FakeNairobi(), bound_pass_manager=bound_pass) _ = sampler.run([self._circuit[0], self._circuit[0]]).result() - self.assertEqual(mock_pass.call_count, 2) + expected = [ + "bound_pass_manager", + "bound_pass_manager", + ] + self.assertEqual(list(messages), expected) if __name__ == "__main__": diff --git a/test/python/primitives/test_estimator.py b/test/python/primitives/test_estimator.py index 32c7bfe5b85c..7a190cbe0034 100644 --- a/test/python/primitives/test_estimator.py +++ b/test/python/primitives/test_estimator.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -20,8 +20,8 @@ from qiskit.circuit import Parameter, QuantumCircuit from qiskit.circuit.library import RealAmplitudes from qiskit.exceptions import QiskitError -from qiskit.opflow import PauliSumOp -from qiskit.primitives import BaseEstimator, Estimator, EstimatorResult +from qiskit.primitives import Estimator, EstimatorResult +from qiskit.primitives.base import validation from qiskit.primitives.utils import _observable_key from qiskit.providers import JobV1 from qiskit.quantum_info import Operator, Pauli, PauliList, SparsePauliOp @@ -348,9 +348,7 @@ class TestObservableValidation(QiskitTestCase): @data( ("IXYZ", (SparsePauliOp("IXYZ"),)), (Pauli("IXYZ"), (SparsePauliOp("IXYZ"),)), - (PauliList("IXYZ"), (SparsePauliOp("IXYZ"),)), (SparsePauliOp("IXYZ"), (SparsePauliOp("IXYZ"),)), - (PauliSumOp(SparsePauliOp("IXYZ")), (SparsePauliOp("IXYZ"),)), ( ["IXYZ", "ZYXI"], (SparsePauliOp("IXYZ"), SparsePauliOp("ZYXI")), @@ -359,35 +357,40 @@ class TestObservableValidation(QiskitTestCase): [Pauli("IXYZ"), Pauli("ZYXI")], (SparsePauliOp("IXYZ"), SparsePauliOp("ZYXI")), ), - ( - [PauliList("IXYZ"), PauliList("ZYXI")], - (SparsePauliOp("IXYZ"), SparsePauliOp("ZYXI")), - ), ( [SparsePauliOp("IXYZ"), SparsePauliOp("ZYXI")], (SparsePauliOp("IXYZ"), SparsePauliOp("ZYXI")), ), + ) + @unpack + def test_validate_observables(self, obsevables, expected): + """Test obsevables standardization.""" + self.assertEqual(validation._validate_observables(obsevables), expected) + + @data( + (PauliList("IXYZ"), (SparsePauliOp("IXYZ"),)), ( - [PauliSumOp(SparsePauliOp("IXYZ")), PauliSumOp(SparsePauliOp("ZYXI"))], + [PauliList("IXYZ"), PauliList("ZYXI")], (SparsePauliOp("IXYZ"), SparsePauliOp("ZYXI")), ), ) @unpack - def test_validate_observables(self, obsevables, expected): + def test_validate_observables_deprecated(self, obsevables, expected): """Test obsevables standardization.""" - self.assertEqual(BaseEstimator._validate_observables(obsevables), expected) + with self.assertRaises(DeprecationWarning): + self.assertEqual(validation._validate_observables(obsevables), expected) @data(None, "ERROR") def test_qiskit_error(self, observables): """Test qiskit error if invalid input.""" with self.assertRaises(QiskitError): - BaseEstimator._validate_observables(observables) + validation._validate_observables(observables) @data((), []) def test_value_error(self, observables): """Test value error if no obsevables are provided.""" with self.assertRaises(ValueError): - BaseEstimator._validate_observables(observables) + validation._validate_observables(observables) if __name__ == "__main__": diff --git a/test/python/primitives/test_primitive.py b/test/python/primitives/test_primitive.py index cc60a17abc7e..5ae93df82a51 100644 --- a/test/python/primitives/test_primitive.py +++ b/test/python/primitives/test_primitive.py @@ -19,7 +19,7 @@ from qiskit import QuantumCircuit, pulse, transpile from qiskit.circuit.random import random_circuit -from qiskit.primitives.base.base_primitive import BasePrimitive +from qiskit.primitives.base import validation from qiskit.primitives.utils import _circuit_key from qiskit.providers.fake_provider import FakeAlmaden from qiskit.test import QiskitTestCase @@ -39,19 +39,19 @@ class TestCircuitValidation(QiskitTestCase): @unpack def test_validate_circuits(self, circuits, expected): """Test circuits standardization.""" - self.assertEqual(BasePrimitive._validate_circuits(circuits), expected) + self.assertEqual(validation._validate_circuits(circuits), expected) @data(None, "ERROR", True, 0, 1.0, 1j, [0.0]) def test_type_error(self, circuits): """Test type error if invalid input.""" with self.assertRaises(TypeError): - BasePrimitive._validate_circuits(circuits) + validation._validate_circuits(circuits) @data((), [], "") def test_value_error(self, circuits): """Test value error if no circuits are provided.""" with self.assertRaises(ValueError): - BasePrimitive._validate_circuits(circuits) + validation._validate_circuits(circuits) @ddt @@ -87,9 +87,9 @@ class TestParameterValuesValidation(QiskitTestCase): def test_validate_parameter_values(self, _parameter_values, expected): """Test parameter_values standardization.""" for parameter_values in [_parameter_values, array(_parameter_values)]: # Numpy - self.assertEqual(BasePrimitive._validate_parameter_values(parameter_values), expected) + self.assertEqual(validation._validate_parameter_values(parameter_values), expected) self.assertEqual( - BasePrimitive._validate_parameter_values(None, default=parameter_values), expected + validation._validate_parameter_values(None, default=parameter_values), expected ) @data( @@ -108,12 +108,12 @@ def test_validate_parameter_values(self, _parameter_values, expected): def test_type_error(self, parameter_values): """Test type error if invalid input.""" with self.assertRaises(TypeError): - BasePrimitive._validate_parameter_values(parameter_values) + validation._validate_parameter_values(parameter_values) def test_value_error(self): """Test value error if no parameter_values or default are provided.""" with self.assertRaises(ValueError): - BasePrimitive._validate_parameter_values(None) + validation._validate_parameter_values(None) class TestCircuitKey(QiskitTestCase): diff --git a/test/python/primitives/test_sampler.py b/test/python/primitives/test_sampler.py index 2dc2aac11098..fac9a250a9e9 100644 --- a/test/python/primitives/test_sampler.py +++ b/test/python/primitives/test_sampler.py @@ -93,7 +93,6 @@ def test_sampler_run(self): self.assertIsInstance(job, JobV1) result = job.result() self.assertIsInstance(result, SamplerResult) - # print([q.binary_probabilities() for q in result.quasi_dists]) self._compare_probs(result.quasi_dists, self._target[1]) def test_sample_run_multiple_circuits(self): @@ -103,7 +102,6 @@ def test_sample_run_multiple_circuits(self): bell = self._circuit[1] sampler = Sampler() result = sampler.run([bell, bell, bell]).result() - # print([q.binary_probabilities() for q in result.quasi_dists]) self._compare_probs(result.quasi_dists[0], self._target[1]) self._compare_probs(result.quasi_dists[1], self._target[1]) self._compare_probs(result.quasi_dists[2], self._target[1]) diff --git a/test/python/providers/test_fake_backends.py b/test/python/providers/test_fake_backends.py index 84fd607a544e..21a18e635d89 100644 --- a/test/python/providers/test_fake_backends.py +++ b/test/python/providers/test_fake_backends.py @@ -416,6 +416,9 @@ def __init__(self, num_ctrl_qubits, ctrl_state=None): from qiskit_aer.noise.noise_model import QuantumErrorLocation sim = AerSimulator() + # test only if simulator's backend is V1 + if sim.version > 1: + return phi = Parameter("phi") lam = Parameter("lam") backend = BackendV2Converter( @@ -494,7 +497,7 @@ def test_filter_faulty_qubits_backend_v2_converter(self): props_dict = backend.properties().to_dict() for i in range(62, 67): non_operational = { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "operational", "unit": "", "value": 0, @@ -515,7 +518,7 @@ def test_filter_faulty_qubits_backend_v2_converter_with_delay(self): props_dict = backend.properties().to_dict() for i in range(62, 67): non_operational = { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "operational", "unit": "", "value": 0, @@ -527,6 +530,34 @@ def test_filter_faulty_qubits_backend_v2_converter_with_delay(self): for qarg in v2_backend.target.qargs: self.assertNotIn(i, qarg) + def test_backend_v2_converter_without_delay(self): + """Test setting :code:`add_delay`argument of :func:`.BackendV2Converter` + to :code:`False`.""" + + expected = { + (0,), + (0, 1), + (0, 2), + (1,), + (1, 0), + (1, 2), + (2,), + (2, 0), + (2, 1), + (2, 3), + (2, 4), + (3,), + (3, 2), + (3, 4), + (4,), + (4, 2), + (4, 3), + } + + backend = BackendV2Converter(backend=FakeYorktown(), filter_faulty=True, add_delay=False) + + self.assertEqual(backend.target.qargs, expected) + def test_filter_faulty_qubits_and_gates_backend_v2_converter(self): """Test faulty gates and qubits.""" backend = FakeWashington() @@ -536,7 +567,7 @@ def test_filter_faulty_qubits_and_gates_backend_v2_converter(self): props_dict = backend.properties().to_dict() for i in range(62, 67): non_operational = { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "operational", "unit": "", "value": 0, @@ -553,7 +584,7 @@ def test_filter_faulty_qubits_and_gates_backend_v2_converter(self): (34, 24), } non_operational_gate = { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "operational", "unit": "", "value": 0, @@ -588,7 +619,7 @@ def test_filter_faulty_gates_v2_converter(self): (34, 24), } non_operational_gate = { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "operational", "unit": "", "value": 0, @@ -615,7 +646,7 @@ def test_filter_faulty_no_faults_v2_converter(self): def test_faulty_full_path_transpile_connected_cmap(self, opt_level): backend = FakeYorktown() non_operational_gate = { - "date": datetime.datetime.utcnow(), + "date": datetime.datetime.now(datetime.timezone.utc), "name": "operational", "unit": "", "value": 0, diff --git a/test/python/pulse/test_block.py b/test/python/pulse/test_block.py index 0dc56fbe6b27..11c71027645d 100644 --- a/test/python/pulse/test_block.py +++ b/test/python/pulse/test_block.py @@ -14,14 +14,12 @@ """Test cases for the pulse schedule block.""" import re -import unittest from typing import List, Any from qiskit import pulse, circuit from qiskit.pulse import transforms from qiskit.pulse.exceptions import PulseError from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeOpenPulse2Q, FakeArmonk -from qiskit.utils import has_aer +from qiskit.providers.fake_provider import FakeOpenPulse2Q class BaseTestBlock(QiskitTestCase): @@ -372,20 +370,6 @@ def test_inherit_from(self): self.assertEqual(new_sched.name, ref_name) self.assertDictEqual(new_sched.metadata, ref_metadata) - @unittest.skipUnless(has_aer(), "qiskit-aer doesn't appear to be installed.") - def test_execute_block(self): - """Test executing a ScheduleBlock on a Pulse backend""" - - with pulse.build(name="test_block") as sched_block: - pulse.play(pulse.Constant(160, 1.0), pulse.DriveChannel(0)) - pulse.acquire(50, pulse.AcquireChannel(0), pulse.MemorySlot(0)) - - backend = FakeArmonk() - # TODO: Rewrite test to simulate with qiskit-dynamics - with self.assertWarns(DeprecationWarning): - test_result = backend.run(sched_block).result() - self.assertDictEqual(test_result.get_counts(), {"0": 1024}) - class TestBlockEquality(BaseTestBlock): """Test equality of blocks. @@ -791,11 +775,11 @@ def test_filter_channels_nested_block(self): with pulse.align_sequential(): pulse.play(self.test_waveform0, self.d0) pulse.delay(5, self.d0) - - with pulse.build(self.backend) as cx_blk: - pulse.cx(0, 1) - - pulse.call(cx_blk) + pulse.call( + self.backend.defaults() + .instruction_schedule_map._get_calibration_entry("cx", (0, 1)) + .get_schedule() + ) for ch in [self.d0, self.d1, pulse.ControlChannel(0)]: filtered_blk = self._filter_and_test_consistency(blk, channels=[ch]) diff --git a/test/python/pulse/test_builder.py b/test/python/pulse/test_builder.py index d02bafa889d7..f888182f490b 100644 --- a/test/python/pulse/test_builder.py +++ b/test/python/pulse/test_builder.py @@ -184,47 +184,6 @@ def test_align_right(self): self.assertScheduleEqual(schedule, reference) - def test_transpiler_settings(self): - """Test the transpiler settings context. - - Tests that two cx gates are optimized away with higher optimization level. - """ - twice_cx_qc = circuit.QuantumCircuit(2) - twice_cx_qc.cx(0, 1) - twice_cx_qc.cx(0, 1) - - with pulse.build(self.backend) as schedule: - with pulse.transpiler_settings(optimization_level=0): - builder.call(twice_cx_qc) - self.assertNotEqual(len(schedule.instructions), 0) - - with pulse.build(self.backend) as schedule: - with pulse.transpiler_settings(optimization_level=3): - builder.call(twice_cx_qc) - self.assertEqual(len(schedule.instructions), 0) - - def test_scheduler_settings(self): - """Test the circuit scheduler settings context.""" - inst_map = pulse.InstructionScheduleMap() - d0 = pulse.DriveChannel(0) - test_x_sched = pulse.Schedule() - test_x_sched += instructions.Delay(10, d0) - inst_map.add("x", (0,), test_x_sched) - - ref_sched = pulse.Schedule() - with self.assertWarns(DeprecationWarning): - ref_sched += pulse.instructions.Call(test_x_sched) - - x_qc = circuit.QuantumCircuit(2) - x_qc.x(0) - - with pulse.build(backend=self.backend) as schedule: - with pulse.transpiler_settings(basis_gates=["x"]): - with pulse.circuit_scheduler_settings(inst_map=inst_map): - builder.call(x_qc) - - self.assertScheduleEqual(schedule, ref_sched) - def test_phase_offset(self): """Test the phase offset context.""" d0 = pulse.DriveChannel(0) @@ -603,20 +562,6 @@ def test_qubit_channels(self): }, ) - def test_active_transpiler_settings(self): - """Test setting settings of active builder's transpiler.""" - with pulse.build(self.backend): - self.assertFalse(pulse.active_transpiler_settings()) - with pulse.transpiler_settings(test_setting=1): - self.assertEqual(pulse.active_transpiler_settings()["test_setting"], 1) - - def test_active_circuit_scheduler_settings(self): - """Test setting settings of active builder's circuit scheduler.""" - with pulse.build(self.backend): - self.assertFalse(pulse.active_circuit_scheduler_settings()) - with pulse.circuit_scheduler_settings(test_setting=1): - self.assertEqual(pulse.active_circuit_scheduler_settings()["test_setting"], 1) - def test_num_qubits(self): """Test builder utility to get number of qubits.""" with pulse.build(self.backend): @@ -781,100 +726,6 @@ def test_delay_qubits(self): self.assertScheduleEqual(schedule, reference) -class TestGates(TestBuilder): - """Test builder gates.""" - - def test_cx(self): - """Test cx gate.""" - with pulse.build(self.backend) as schedule: - pulse.cx(0, 1) - - reference_qc = circuit.QuantumCircuit(2) - reference_qc.cx(0, 1) - reference = compiler.schedule(reference_qc, self.backend) - - self.assertScheduleEqual(schedule, reference) - - def test_u1(self): - """Test u1 gate.""" - with pulse.build(self.backend) as schedule: - with pulse.transpiler_settings(layout_method="trivial"): - pulse.u1(np.pi / 2, 0) - - reference_qc = circuit.QuantumCircuit(1) - reference_qc.append(circuit.library.U1Gate(np.pi / 2), [0]) - reference = compiler.schedule(reference_qc, self.backend) - - self.assertScheduleEqual(schedule, reference) - - def test_u2(self): - """Test u2 gate.""" - with pulse.build(self.backend) as schedule: - pulse.u2(np.pi / 2, 0, 0) - - reference_qc = circuit.QuantumCircuit(1) - reference_qc.append(circuit.library.U2Gate(np.pi / 2, 0), [0]) - reference = compiler.schedule(reference_qc, self.backend) - - self.assertScheduleEqual(schedule, reference) - - def test_u3(self): - """Test u3 gate.""" - with pulse.build(self.backend) as schedule: - pulse.u3(np.pi / 8, np.pi / 16, np.pi / 4, 0) - - reference_qc = circuit.QuantumCircuit(1) - reference_qc.append(circuit.library.U3Gate(np.pi / 8, np.pi / 16, np.pi / 4), [0]) - reference = compiler.schedule(reference_qc, self.backend) - - self.assertScheduleEqual(schedule, reference) - - def test_x(self): - """Test x gate.""" - with pulse.build(self.backend) as schedule: - pulse.x(0) - - reference_qc = circuit.QuantumCircuit(1) - reference_qc.x(0) - reference_qc = compiler.transpile(reference_qc, self.backend) - reference = compiler.schedule(reference_qc, self.backend) - - self.assertScheduleEqual(schedule, reference) - - def test_lazy_evaluation_with_transpiler(self): - """Test that the two cx gates are optimizied away by the transpiler.""" - with pulse.build(self.backend) as schedule: - pulse.cx(0, 1) - pulse.cx(0, 1) - - reference_qc = circuit.QuantumCircuit(2) - reference = compiler.schedule(reference_qc, self.backend) - - self.assertScheduleEqual(schedule, reference) - - def test_measure(self): - """Test pulse measurement macro against circuit measurement and - ensure agreement.""" - with pulse.build(self.backend) as schedule: - with pulse.align_sequential(): - pulse.x(0) - pulse.measure(0) - - reference_qc = circuit.QuantumCircuit(1, 1) - reference_qc.x(0) - reference_qc.measure(0, 0) - reference_qc = compiler.transpile(reference_qc, self.backend) - reference = compiler.schedule(reference_qc, self.backend) - - self.assertScheduleEqual(schedule, reference) - - def test_backend_require(self): - """Test that a backend is required to use a gate.""" - with self.assertRaises(exceptions.BackendNotSet): - with pulse.build(): - pulse.x(0) - - class TestBuilderComposition(TestBuilder): """Test more sophisticated composite builder examples.""" @@ -888,18 +739,26 @@ def test_complex_build(self): short_dur = 20 long_dur = 49 + def get_sched(qubit_idx: [int], backend): + qc = circuit.QuantumCircuit(2) + for idx in qubit_idx: + qc.append(circuit.library.U2Gate(0, pi / 2), [idx]) + return compiler.schedule(compiler.transpile(qc, backend=backend), backend) + with pulse.build(self.backend) as schedule: + with pulse.align_sequential(): pulse.delay(delay_dur, d0) - pulse.u2(0, pi / 2, 1) + pulse.call(get_sched([1], self.backend)) + with pulse.align_right(): pulse.play(library.Constant(short_dur, 0.1), d1) pulse.play(library.Constant(long_dur, 0.1), d2) - pulse.u2(0, pi / 2, 1) + pulse.call(get_sched([1], self.backend)) + with pulse.align_left(): - pulse.u2(0, pi / 2, 0) - pulse.u2(0, pi / 2, 1) - pulse.u2(0, pi / 2, 0) + pulse.call(get_sched([0, 1, 0], self.backend)) + pulse.measure(0) # prepare and schedule circuits that will be used. @@ -977,94 +836,6 @@ def test_call(self): self.assertScheduleEqual(schedule, ref_sched) - def test_call_circuit(self): - """Test calling circuit instruction.""" - inst_map = self.inst_map - reference = inst_map.get("u1", (0,), 0.0) - - ref_sched = pulse.Schedule() - with self.assertWarns(DeprecationWarning): - ref_sched += pulse.instructions.Call(reference) - - u1_qc = circuit.QuantumCircuit(2) - u1_qc.append(circuit.library.U1Gate(0.0), [0]) - - transpiler_settings = {"optimization_level": 0} - - with pulse.build(self.backend, default_transpiler_settings=transpiler_settings) as schedule: - with pulse.align_right(): - builder.call(u1_qc) - - self.assertScheduleEqual(schedule, ref_sched) - - def test_call_circuit_with_cregs(self): - """Test calling of circuit wiht classical registers.""" - - qc = circuit.QuantumCircuit(2, 2) - qc.h(0) - qc.cx(0, 1) - qc.measure([0, 1], [0, 1]) - - with pulse.build(self.backend) as schedule: - pulse.call(qc) - - reference_qc = compiler.transpile(qc, self.backend) - reference = compiler.schedule(reference_qc, self.backend) - - ref_sched = pulse.Schedule() - with self.assertWarns(DeprecationWarning): - ref_sched += pulse.instructions.Call(reference) - - self.assertScheduleEqual(schedule, ref_sched) - - def test_call_gate_and_circuit(self): - """Test calling circuit with gates.""" - h_control = circuit.QuantumCircuit(2) - h_control.h(0) - - with pulse.build(self.backend) as schedule: - with pulse.align_sequential(): - # this is circuit, a subroutine stored as Call instruction - pulse.call(h_control) - # this is instruction, not subroutine - pulse.cx(0, 1) - # this is macro, not subroutine - pulse.measure([0, 1]) - - # subroutine - h_reference = compiler.schedule(compiler.transpile(h_control, self.backend), self.backend) - - # gate - cx_circ = circuit.QuantumCircuit(2) - cx_circ.cx(0, 1) - cx_reference = compiler.schedule(compiler.transpile(cx_circ, self.backend), self.backend) - - # measurement - measure_reference = macros.measure( - qubits=[0, 1], inst_map=self.inst_map, meas_map=self.configuration.meas_map - ) - - reference = pulse.Schedule() - with self.assertWarns(DeprecationWarning): - reference += pulse.instructions.Call(h_reference) - reference += cx_reference - reference += measure_reference << reference.duration - - self.assertScheduleEqual(schedule, reference) - - def test_subroutine_not_transpiled(self): - """Test called circuit is frozen as a subroutine.""" - subprogram = circuit.QuantumCircuit(1) - subprogram.x(0) - - transpiler_settings = {"optimization_level": 2} - - with pulse.build(self.backend, default_transpiler_settings=transpiler_settings) as schedule: - pulse.call(subprogram) - pulse.call(subprogram) - - self.assertNotEqual(len(target_qobj_transform(schedule).instructions), 0) - def test_subroutine_not_transformed(self): """Test called schedule is not transformed.""" d0 = pulse.DriveChannel(0) diff --git a/test/python/pulse/test_builder_v2.py b/test/python/pulse/test_builder_v2.py index 1f194a9d3c29..71ef5986f07f 100644 --- a/test/python/pulse/test_builder_v2.py +++ b/test/python/pulse/test_builder_v2.py @@ -14,8 +14,8 @@ import numpy as np -from qiskit import circuit, pulse -from qiskit.pulse import builder, macros +from qiskit import pulse +from qiskit.pulse import macros from qiskit.pulse.instructions import directives from qiskit.pulse.transforms import target_qobj_transform @@ -42,46 +42,6 @@ def assertScheduleEqual(self, program, target): class TestContextsV2(TestBuilderV2): """Test builder contexts.""" - def test_transpiler_settings(self): - """Test the transpiler settings context. - - Tests that two cx gates are optimized away with higher optimization level. - """ - twice_cx_qc = circuit.QuantumCircuit(2) - twice_cx_qc.cx(0, 1) - twice_cx_qc.cx(0, 1) - - with pulse.build(self.backend) as schedule: - with pulse.transpiler_settings(optimization_level=0): - builder.call(twice_cx_qc) - self.assertNotEqual(len(schedule.instructions), 0) - - with pulse.build(self.backend) as schedule: - with pulse.transpiler_settings(optimization_level=3): - builder.call(twice_cx_qc) - self.assertEqual(len(schedule.instructions), 0) - - def test_scheduler_settings(self): - """Test the circuit scheduler settings context.""" - inst_map = pulse.InstructionScheduleMap() - d0 = pulse.DriveChannel(0) - test_x_sched = pulse.Schedule() - test_x_sched += instructions.Delay(10, d0) - inst_map.add("x", (0,), test_x_sched) - - ref_sched = pulse.Schedule() - ref_sched += pulse.instructions.Call(test_x_sched) - - x_qc = circuit.QuantumCircuit(2) - x_qc.x(0) - - with pulse.build(backend=self.backend) as schedule: - with pulse.transpiler_settings(basis_gates=["x"]): - with pulse.circuit_scheduler_settings(inst_map=inst_map): - builder.call(x_qc) - - self.assertScheduleEqual(schedule, ref_sched) - def test_phase_compensated_frequency_offset(self): """Test that the phase offset context properly compensates for phase accumulation with backendV2.""" @@ -179,20 +139,6 @@ def test_qubit_channels(self): }, ) - def test_active_transpiler_settings(self): - """Test setting settings of active builder's transpiler.""" - with pulse.build(self.backend): - self.assertFalse(pulse.active_transpiler_settings()) - with pulse.transpiler_settings(test_setting=1): - self.assertEqual(pulse.active_transpiler_settings()["test_setting"], 1) - - def test_active_circuit_scheduler_settings(self): - """Test setting settings of active builder's circuit scheduler.""" - with pulse.build(self.backend): - self.assertFalse(pulse.active_circuit_scheduler_settings()) - with pulse.circuit_scheduler_settings(test_setting=1): - self.assertEqual(pulse.active_circuit_scheduler_settings()["test_setting"], 1) - def test_num_qubits(self): """Test builder utility to get number of qubits with backendV2.""" with pulse.build(self.backend): diff --git a/test/python/pulse/test_discrete_pulses.py b/test/python/pulse/test_discrete_pulses.py deleted file mode 100644 index 53157d54c5f8..000000000000 --- a/test/python/pulse/test_discrete_pulses.py +++ /dev/null @@ -1,255 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests discrete sampled pulse functions.""" - -import numpy as np - -from qiskit.test import QiskitTestCase -from qiskit.pulse import Waveform, PulseError, library -from qiskit.pulse.library import continuous - - -class TestDiscretePulses(QiskitTestCase): - """Test discreted sampled pulses.""" - - def test_constant(self): - """Test discrete sampled constant pulse.""" - amp = 0.5j - duration = 10 - times = np.arange(0, duration) + 0.5 # to match default midpoint sampling strategy - constant_ref = continuous.constant(times, amp=amp) - constant_pulse = library.constant(duration, amp=amp) - self.assertIsInstance(constant_pulse, Waveform) - np.testing.assert_array_almost_equal(constant_pulse.samples, constant_ref) - - def test_zero(self): - """Test discrete sampled constant pulse.""" - duration = 10 - times = np.arange(0, duration) + 0.5 - zero_ref = continuous.zero(times) - zero_pulse = library.zero(duration) - self.assertIsInstance(zero_pulse, Waveform) - np.testing.assert_array_almost_equal(zero_pulse.samples, zero_ref) - - def test_square(self): - """Test discrete sampled square wave.""" - amp = 0.5 - freq = 0.2 - duration = 10 - times = np.arange(0, duration) + 0.5 - square_ref = continuous.square(times, amp=amp, freq=freq) - square_pulse = library.square(duration, amp=amp, freq=freq) - self.assertIsInstance(square_pulse, Waveform) - np.testing.assert_array_almost_equal(square_pulse.samples, square_ref) - - # test single cycle - cycle_freq = 1.0 / duration - square_cycle_ref = continuous.square(times, amp=amp, freq=cycle_freq) - square_cycle_pulse = library.square(duration, amp=amp) - np.testing.assert_array_almost_equal(square_cycle_pulse.samples, square_cycle_ref) - - def test_sawtooth(self): - """Test discrete sampled sawtooth wave.""" - amp = 0.5 - freq = 0.2 - duration = 10 - times = np.arange(0, duration) + 0.5 - sawtooth_ref = continuous.sawtooth(times, amp=amp, freq=freq) - sawtooth_pulse = library.sawtooth(duration, amp=amp, freq=freq) - self.assertIsInstance(sawtooth_pulse, Waveform) - np.testing.assert_array_equal(sawtooth_pulse.samples, sawtooth_ref) - - # test single cycle - cycle_freq = 1.0 / duration - sawtooth_cycle_ref = continuous.sawtooth(times, amp=amp, freq=cycle_freq) - sawtooth_cycle_pulse = library.sawtooth(duration, amp=amp) - np.testing.assert_array_almost_equal(sawtooth_cycle_pulse.samples, sawtooth_cycle_ref) - - def test_triangle(self): - """Test discrete sampled triangle wave.""" - amp = 0.5 - freq = 0.2 - duration = 10 - times = np.arange(0, duration) + 0.5 - triangle_ref = continuous.triangle(times, amp=amp, freq=freq) - triangle_pulse = library.triangle(duration, amp=amp, freq=freq) - self.assertIsInstance(triangle_pulse, Waveform) - np.testing.assert_array_almost_equal(triangle_pulse.samples, triangle_ref) - - # test single cycle - cycle_freq = 1.0 / duration - triangle_cycle_ref = continuous.triangle(times, amp=amp, freq=cycle_freq) - triangle_cycle_pulse = library.triangle(duration, amp=amp) - np.testing.assert_array_equal(triangle_cycle_pulse.samples, triangle_cycle_ref) - - def test_cos(self): - """Test discrete sampled cosine wave.""" - amp = 0.5 - period = 5 - freq = 1 / period - duration = 10 - times = np.arange(0, duration) + 0.5 - cos_ref = continuous.cos(times, amp=amp, freq=freq) - cos_pulse = library.cos(duration, amp=amp, freq=freq) - self.assertIsInstance(cos_pulse, Waveform) - np.testing.assert_array_almost_equal(cos_pulse.samples, cos_ref) - - # test single cycle - cycle_freq = 1 / duration - cos_cycle_ref = continuous.cos(times, amp=amp, freq=cycle_freq) - cos_cycle_pulse = library.cos(duration, amp=amp) - np.testing.assert_array_almost_equal(cos_cycle_pulse.samples, cos_cycle_ref) - - def test_sin(self): - """Test discrete sampled sine wave.""" - amp = 0.5 - period = 5 - freq = 1 / period - duration = 10 - times = np.arange(0, duration) + 0.5 - sin_ref = continuous.sin(times, amp=amp, freq=freq) - sin_pulse = library.sin(duration, amp=amp, freq=freq) - self.assertIsInstance(sin_pulse, Waveform) - np.testing.assert_array_equal(sin_pulse.samples, sin_ref) - - # test single cycle - cycle_freq = 1 / duration - sin_cycle_ref = continuous.sin(times, amp=amp, freq=cycle_freq) - sin_cycle_pulse = library.sin(duration, amp=amp) - np.testing.assert_array_almost_equal(sin_cycle_pulse.samples, sin_cycle_ref) - - def test_gaussian(self): - """Test gaussian pulse.""" - amp = 0.5 - sigma = 2 - duration = 10 - center = duration / 2 - times = np.arange(0, duration) + 0.5 - gaussian_ref = continuous.gaussian( - times, amp, center, sigma, zeroed_width=2 * (center + 1), rescale_amp=True - ) - gaussian_pulse = library.gaussian(duration, amp, sigma) - self.assertIsInstance(gaussian_pulse, Waveform) - np.testing.assert_array_almost_equal(gaussian_pulse.samples, gaussian_ref) - - def test_gaussian_deriv(self): - """Test discrete sampled gaussian derivative pulse.""" - amp = 0.5 - sigma = 2 - duration = 10 - center = duration / 2 - times = np.arange(0, duration) + 0.5 - gaussian_deriv_ref = continuous.gaussian_deriv(times, amp, center, sigma) - gaussian_deriv_pulse = library.gaussian_deriv(duration, amp, sigma) - self.assertIsInstance(gaussian_deriv_pulse, Waveform) - np.testing.assert_array_almost_equal(gaussian_deriv_pulse.samples, gaussian_deriv_ref) - - def test_sech(self): - """Test sech pulse.""" - amp = 0.5 - sigma = 2 - duration = 10 - center = duration / 2 - times = np.arange(0, duration) + 0.5 - sech_ref = continuous.sech( - times, amp, center, sigma, zeroed_width=2 * (center + 1), rescale_amp=True - ) - sech_pulse = library.sech(duration, amp, sigma) - self.assertIsInstance(sech_pulse, Waveform) - np.testing.assert_array_almost_equal(sech_pulse.samples, sech_ref) - - def test_sech_deriv(self): - """Test discrete sampled sech derivative pulse.""" - amp = 0.5 - sigma = 2 - duration = 10 - center = duration / 2 - times = np.arange(0, duration) + 0.5 - sech_deriv_ref = continuous.sech_deriv(times, amp, center, sigma) - sech_deriv_pulse = library.sech_deriv(duration, amp, sigma) - self.assertIsInstance(sech_deriv_pulse, Waveform) - np.testing.assert_array_almost_equal(sech_deriv_pulse.samples, sech_deriv_ref) - - def test_gaussian_square(self): - """Test discrete sampled gaussian square pulse.""" - amp = 0.5 - sigma = 0.1 - risefall = 2 - duration = 10 - center = duration / 2 - width = duration - 2 * risefall - center = duration / 2 - times = np.arange(0, duration) + 0.5 - gaussian_square_ref = continuous.gaussian_square(times, amp, center, width, sigma) - gaussian_square_pulse = library.gaussian_square(duration, amp, sigma, risefall) - self.assertIsInstance(gaussian_square_pulse, Waveform) - np.testing.assert_array_almost_equal(gaussian_square_pulse.samples, gaussian_square_ref) - - def test_gaussian_square_args(self): - """Gaussian square allows the user to specify risefall or width. Test this.""" - amp = 0.5 - sigma = 0.1 - duration = 10 - # risefall and width consistent: no error - library.gaussian_square(duration, amp, sigma, 2, width=6) - # supply width instead: no error - library.gaussian_square(duration, amp, sigma, width=6) - with self.assertRaises(PulseError): - library.gaussian_square(duration, amp, sigma, width=2, risefall=2) - with self.assertRaises(PulseError): - library.gaussian_square(duration, amp, sigma) - - def test_drag(self): - """Test discrete sampled drag pulse.""" - amp = 0.5 - sigma = 0.1 - beta = 0 - duration = 10 - center = 10 / 2 - times = np.arange(0, duration) + 0.5 - # reference drag pulse - drag_ref = continuous.drag( - times, amp, center, sigma, beta=beta, zeroed_width=2 * (center + 1), rescale_amp=True - ) - drag_pulse = library.drag(duration, amp, sigma, beta=beta) - self.assertIsInstance(drag_pulse, Waveform) - np.testing.assert_array_almost_equal(drag_pulse.samples, drag_ref) - - def test_pending_deprecation_warnings(self): - """Test that pending deprecation warnings are raised when the discrete library is used.""" - with self.assertWarns(PendingDeprecationWarning): - library.drag(duration=10, amp=0.5, sigma=0.1, beta=0.1) - with self.assertWarns(PendingDeprecationWarning): - library.gaussian_square(duration=10, amp=0.5, sigma=0.1, risefall=2, width=6) - with self.assertWarns(PendingDeprecationWarning): - library.gaussian(duration=10, amp=0.5, sigma=0.1) - with self.assertWarns(PendingDeprecationWarning): - library.sin(duration=10, amp=0.5) - with self.assertWarns(PendingDeprecationWarning): - library.cos(duration=10, amp=0.5) - with self.assertWarns(PendingDeprecationWarning): - library.sawtooth(duration=10, amp=0.5) - with self.assertWarns(PendingDeprecationWarning): - library.zero(duration=10) - with self.assertWarns(PendingDeprecationWarning): - library.constant(duration=10, amp=0.5) - with self.assertWarns(PendingDeprecationWarning): - library.triangle(duration=10, amp=0.5) - with self.assertWarns(PendingDeprecationWarning): - library.gaussian_deriv(duration=10, amp=0.5, sigma=3) - with self.assertWarns(PendingDeprecationWarning): - library.sech_deriv(duration=10, amp=0.5, sigma=3) - with self.assertWarns(PendingDeprecationWarning): - library.sech(duration=10, amp=0.5, sigma=3) - with self.assertWarns(PendingDeprecationWarning): - library.square(duration=10, amp=0.5) diff --git a/test/python/pulse/test_frames.py b/test/python/pulse/test_frames.py deleted file mode 100644 index 1a9b99e1139f..000000000000 --- a/test/python/pulse/test_frames.py +++ /dev/null @@ -1,77 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test pulse logical elements and frames""" - -from qiskit.pulse import ( - PulseError, - GenericFrame, - QubitFrame, - MeasurementFrame, -) -from qiskit.test import QiskitTestCase - - -class TestFrames(QiskitTestCase): - """Test frames.""" - - def test_generic_frame_initialization(self): - """Test that Frame objects are created correctly""" - frame = GenericFrame(name="frame1") - self.assertEqual(frame.name, "frame1") - self.assertEqual(str(frame), "GenericFrame(frame1)") - - def test_generic_frame_comparison(self): - """Test that GenericFrame objects are compared correctly""" - frame1 = GenericFrame(name="frame1") - - self.assertEqual(frame1, GenericFrame(name="frame1")) - self.assertNotEqual(frame1, GenericFrame(name="frame2")) - self.assertNotEqual(frame1, QubitFrame(3)) - - def test_qubit_frame_initialization(self): - """Test that QubitFrame type frames are created and validated correctly""" - frame = QubitFrame(2) - self.assertEqual(frame.index, 2) - self.assertEqual(str(frame), "QubitFrame(2)") - - with self.assertRaises(PulseError): - QubitFrame(0.5) - with self.assertRaises(PulseError): - QubitFrame(-0.5) - with self.assertRaises(PulseError): - QubitFrame(-1) - - def test_qubit_frame_comparison(self): - """Test the comparison of QubitFrame""" - self.assertEqual(QubitFrame(0), QubitFrame(0)) - self.assertNotEqual(QubitFrame(0), QubitFrame(1)) - self.assertNotEqual(MeasurementFrame(0), QubitFrame(0)) - - def test_measurement_frame_initialization(self): - """Test that MeasurementFrame type frames are created and validated correctly""" - frame = MeasurementFrame(2) - self.assertEqual(frame.index, 2) - self.assertEqual(str(frame), "MeasurementFrame(2)") - - with self.assertRaises(PulseError): - MeasurementFrame(0.5) - with self.assertRaises(PulseError): - MeasurementFrame(-0.5) - with self.assertRaises(PulseError): - MeasurementFrame(-1) - - def test_measurement_frame_comparison(self): - """Test the comparison of measurement frames""" - self.assertEqual(MeasurementFrame(0), MeasurementFrame(0)) - self.assertNotEqual(MeasurementFrame(0), MeasurementFrame(1)) - self.assertNotEqual(MeasurementFrame(0), QubitFrame(0)) diff --git a/test/python/pulse/test_instruction_schedule_map.py b/test/python/pulse/test_instruction_schedule_map.py index 414a1d6566a5..8c4fd3cb7784 100644 --- a/test/python/pulse/test_instruction_schedule_map.py +++ b/test/python/pulse/test_instruction_schedule_map.py @@ -16,7 +16,6 @@ import numpy as np -from qiskit.pulse import library from qiskit.circuit.library.standard_gates import U1Gate, U3Gate, CXGate, XGate from qiskit.circuit.parameter import Parameter from qiskit.circuit.parameterexpression import ParameterExpression @@ -350,11 +349,13 @@ def test_schedule_generator(self): def test_func(dur: int): sched = Schedule() - sched += Play(library.constant(int(dur), amp), DriveChannel(0)) + waveform = Constant(int(dur), amp).get_waveform() + sched += Play(waveform, DriveChannel(0)) return sched expected_sched = Schedule() - expected_sched += Play(library.constant(dur_val, amp), DriveChannel(0)) + cons_waveform = Constant(dur_val, amp).get_waveform() + expected_sched += Play(cons_waveform, DriveChannel(0)) inst_map = InstructionScheduleMap() inst_map.add("f", (0,), test_func) @@ -371,11 +372,13 @@ def test_schedule_generator_supports_parameter_expressions(self): def test_func(dur: ParameterExpression, t_val: int): dur_bound = dur.bind({t_param: t_val}) sched = Schedule() - sched += Play(library.constant(int(float(dur_bound)), amp), DriveChannel(0)) + waveform = Constant(int(float(dur_bound)), amp).get_waveform() + sched += Play(waveform, DriveChannel(0)) return sched expected_sched = Schedule() - expected_sched += Play(library.constant(10, amp), DriveChannel(0)) + cons_waveform = Constant(10, amp).get_waveform() + expected_sched += Play(cons_waveform, DriveChannel(0)) inst_map = InstructionScheduleMap() inst_map.add("f", (0,), test_func) diff --git a/test/python/pulse/test_logical_elements.py b/test/python/pulse/test_logical_elements.py deleted file mode 100644 index bea1841fff3a..000000000000 --- a/test/python/pulse/test_logical_elements.py +++ /dev/null @@ -1,66 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test pulse logical elements and frames""" - -from qiskit.pulse import ( - PulseError, - Qubit, - Coupler, -) -from qiskit.test import QiskitTestCase - - -class TestLogicalElements(QiskitTestCase): - """Test logical elements.""" - - def test_qubit_initialization(self): - """Test that Qubit type logical elements are created and validated correctly""" - qubit = Qubit(0) - self.assertEqual(qubit.index, (0,)) - self.assertEqual(qubit.qubit_index, 0) - self.assertEqual(str(qubit), "Qubit(0)") - - with self.assertRaises(PulseError): - Qubit(0.5) - with self.assertRaises(PulseError): - Qubit(-0.5) - with self.assertRaises(PulseError): - Qubit(-1) - - def test_coupler_initialization(self): - """Test that Coupler type logical elements are created and validated correctly""" - coupler = Coupler(0, 3) - self.assertEqual(coupler.index, (0, 3)) - self.assertEqual(str(coupler), "Coupler(0, 3)") - - coupler = Coupler(0, 3, 2) - self.assertEqual(coupler.index, (0, 3, 2)) - - with self.assertRaises(PulseError): - Coupler(-1, 0) - with self.assertRaises(PulseError): - Coupler(2, -0.5) - with self.assertRaises(PulseError): - Coupler(3, -1) - with self.assertRaises(PulseError): - Coupler(0, 0, 1) - with self.assertRaises(PulseError): - Coupler(0) - - def test_logical_elements_comparison(self): - """Test the comparison of various logical elements""" - self.assertEqual(Qubit(0), Qubit(0)) - self.assertNotEqual(Qubit(0), Qubit(1)) - - self.assertEqual(Coupler(0, 1), Coupler(0, 1)) - self.assertNotEqual(Coupler(0, 1), Coupler(0, 2)) diff --git a/test/python/pulse/test_mixed_frames.py b/test/python/pulse/test_mixed_frames.py deleted file mode 100644 index ea681878f1cf..000000000000 --- a/test/python/pulse/test_mixed_frames.py +++ /dev/null @@ -1,55 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test pulse logical elements and frames""" - -from qiskit.pulse import ( - Qubit, - GenericFrame, - MixedFrame, -) -from qiskit.test import QiskitTestCase - - -class TestMixedFrames(QiskitTestCase): - """Test mixed frames.""" - - def test_mixed_frame_initialization(self): - """Test that MixedFrame objects are created correctly""" - frame = GenericFrame("frame1") - qubit = Qubit(1) - mixed_frame = MixedFrame(qubit, frame) - self.assertEqual(mixed_frame.logical_element, qubit) - self.assertEqual(mixed_frame.frame, frame) - - def test_mixed_frames_comparison(self): - """Test the comparison of various mixed frames""" - self.assertEqual( - MixedFrame(Qubit(1), GenericFrame("a")), - MixedFrame(Qubit(1), GenericFrame("a")), - ) - - self.assertNotEqual( - MixedFrame(Qubit(1), GenericFrame("a")), - MixedFrame(Qubit(2), GenericFrame("a")), - ) - self.assertNotEqual( - MixedFrame(Qubit(1), GenericFrame("a")), - MixedFrame(Qubit(1), GenericFrame("b")), - ) - - def test_mixed_frame_repr(self): - """Test MixedFrame __repr__""" - frame = GenericFrame("frame1") - qubit = Qubit(1) - mixed_frame = MixedFrame(qubit, frame) - self.assertEqual(str(mixed_frame), f"MixedFrame({qubit},{frame})") diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 524861020d22..c0a4a43e7440 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -34,17 +34,6 @@ Square, Sech, SechDeriv, - gaussian, - gaussian_square, - gaussian_deriv, - drag as pl_drag, - sin, - cos, - triangle, - sawtooth, - square, - sech, - sech_deriv, ) from qiskit.pulse import functional_pulse, PulseError @@ -138,11 +127,11 @@ def test_pulse_limits(self): self.fail("Waveform incorrectly failed to approximately unit norm samples.") -class TestParametricPulses(QiskitTestCase): - """Tests for all subclasses of ParametricPulse.""" +class TestSymbolicPulses(QiskitTestCase): + """Tests for all subclasses of SymbolicPulse.""" def test_construction(self): - """Test that parametric pulses can be constructed without error.""" + """Test that symbolic pulses can be constructed without error.""" Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2) GaussianSquare(duration=150, amp=0.2, sigma=8, width=140) GaussianSquare(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=2.5) @@ -184,33 +173,6 @@ def test_complex_amp_deprecation(self): gauss_pulse_complex_amp.get_waveform().samples, ) - def test_gaussian_pulse(self): - """Test that Gaussian sample pulse matches the pulse library.""" - gauss = Gaussian(duration=25, sigma=4, amp=0.5, angle=np.pi / 2) - sample_pulse = gauss.get_waveform() - self.assertIsInstance(sample_pulse, Waveform) - pulse_lib_gauss = gaussian(duration=25, sigma=4, amp=0.5j, zero_ends=True).samples - np.testing.assert_almost_equal(sample_pulse.samples, pulse_lib_gauss) - - def test_gaussian_square_pulse(self): - """Test that GaussianSquare sample pulse matches the pulse library.""" - gauss_sq = GaussianSquare(duration=125, sigma=4, amp=0.5, width=100, angle=np.pi / 2) - sample_pulse = gauss_sq.get_waveform() - self.assertIsInstance(sample_pulse, Waveform) - pulse_lib_gauss_sq = gaussian_square( - duration=125, sigma=4, amp=0.5j, width=100, zero_ends=True - ).samples - np.testing.assert_almost_equal(sample_pulse.samples, pulse_lib_gauss_sq) - gauss_sq = GaussianSquare( - duration=125, sigma=4, amp=0.5, risefall_sigma_ratio=3.125, angle=np.pi / 2 - ) - sample_pulse = gauss_sq.get_waveform() - self.assertIsInstance(sample_pulse, Waveform) - pulse_lib_gauss_sq = gaussian_square( - duration=125, sigma=4, amp=0.5j, width=100, zero_ends=True - ).samples - np.testing.assert_almost_equal(sample_pulse.samples, pulse_lib_gauss_sq) - def test_gauss_square_extremes(self): """Test that the gaussian square pulse can build a gaussian.""" duration = 125 @@ -369,14 +331,6 @@ def test_gaussian_square_echo_active_amp_validation(self): with self.assertRaises(PulseError): gaussian_square_echo(duration=50, width=0, sigma=4, amp=-0.8, active_amp=-0.3) - def test_drag_pulse(self): - """Test that the Drag sample pulse matches the pulse library.""" - drag = Drag(duration=25, sigma=4, amp=0.5, beta=1, angle=np.pi / 2) - sample_pulse = drag.get_waveform() - self.assertIsInstance(sample_pulse, Waveform) - pulse_lib_drag = pl_drag(duration=25, sigma=4, amp=0.5j, beta=1, zero_ends=True).samples - np.testing.assert_almost_equal(sample_pulse.samples, pulse_lib_drag) - def test_drag_validation(self): """Test drag parameter validation, specifically the beta validation.""" duration = 25 @@ -419,29 +373,24 @@ def check_drag(duration, sigma, amp, beta, angle=0): check_drag(duration=50, sigma=4, amp=0.8, beta=-20) def test_sin_pulse(self): - """Test that Sin sample pulse matches expectations, and parameter validation""" + """Test that Sin creation""" duration = 100 amp = 0.5 freq = 0.1 phase = 0 - sin_pulse = Sin(duration=duration, amp=amp, freq=freq, phase=phase) - sin_waveform = sin(duration=duration, amp=amp, freq=freq, phase=phase) - - np.testing.assert_almost_equal(sin_pulse.get_waveform().samples, sin_waveform.samples) + Sin(duration=duration, amp=amp, freq=freq, phase=phase) with self.assertRaises(PulseError): Sin(duration=duration, amp=amp, freq=5, phase=phase) def test_cos_pulse(self): - """Test that Cos sample pulse matches expectations, and parameter validation""" + """Test that Cos creation""" duration = 100 amp = 0.5 freq = 0.1 phase = 0 cos_pulse = Cos(duration=duration, amp=amp, freq=freq, phase=phase) - cos_waveform = cos(duration=duration, amp=amp, freq=freq, phase=phase) - np.testing.assert_almost_equal(cos_pulse.get_waveform().samples, cos_waveform.samples) shifted_sin_pulse = Sin(duration=duration, amp=amp, freq=freq, phase=phase + np.pi / 2) np.testing.assert_almost_equal( @@ -451,31 +400,24 @@ def test_cos_pulse(self): Cos(duration=duration, amp=amp, freq=5, phase=phase) def test_square_pulse(self): - """Test that Square sample pulse matches expectations, and parameter validation""" + """Test that Square pulse creation""" duration = 100 amp = 0.5 freq = 0.1 phase = 0.3 - square_pulse = Square(duration=duration, amp=amp, freq=freq, phase=phase) - square_waveform = square(duration=duration, amp=amp, freq=freq, phase=phase / 2) - - np.testing.assert_almost_equal(square_pulse.get_waveform().samples, square_waveform.samples) + Square(duration=duration, amp=amp, freq=freq, phase=phase) with self.assertRaises(PulseError): Square(duration=duration, amp=amp, freq=5, phase=phase) def test_sawtooth_pulse(self): - """Test that Sawtooth sample pulse matches expectations, and parameter validation""" + """Test that Sawtooth pulse creation""" duration = 100 amp = 0.5 freq = 0.1 phase = 0.5 sawtooth_pulse = Sawtooth(duration=duration, amp=amp, freq=freq, phase=phase) - sawtooth_waveform = sawtooth(duration=duration, amp=amp, freq=freq, phase=phase / 2) - # Note that the phase definition in `Sawtooth` was changed compared to `sawtooth` - np.testing.assert_almost_equal( - sawtooth_pulse.get_waveform().samples, sawtooth_waveform.samples - ) + sawtooth_pulse_2 = Sawtooth(duration=duration, amp=amp, freq=freq, phase=phase + 2 * np.pi) np.testing.assert_almost_equal( sawtooth_pulse.get_waveform().samples, sawtooth_pulse_2.get_waveform().samples @@ -485,16 +427,13 @@ def test_sawtooth_pulse(self): Sawtooth(duration=duration, amp=amp, freq=5, phase=phase) def test_triangle_pulse(self): - """Test that Triangle sample pulse matches expectations, and parameter validation""" + """Test that Triangle pulse creation""" duration = 100 amp = 0.5 freq = 0.1 phase = 0.5 triangle_pulse = Triangle(duration=duration, amp=amp, freq=freq, phase=phase) - triangle_waveform = triangle(duration=duration, amp=amp, freq=freq, phase=phase) - np.testing.assert_almost_equal( - triangle_pulse.get_waveform().samples, triangle_waveform.samples - ) + triangle_pulse_2 = Triangle(duration=duration, amp=amp, freq=freq, phase=phase + 2 * np.pi) np.testing.assert_almost_equal( triangle_pulse.get_waveform().samples, triangle_pulse_2.get_waveform().samples @@ -504,46 +443,35 @@ def test_triangle_pulse(self): Triangle(duration=duration, amp=amp, freq=5, phase=phase) def test_gaussian_deriv_pulse(self): - """Test that GaussianDeriv sample pulse matches expectations""" + """Test that GaussianDeriv pulse creation""" duration = 300 amp = 0.5 sigma = 100 - gaussian_deriv_pulse = GaussianDeriv(duration=duration, amp=amp, sigma=sigma) - gaussian_deriv_waveform = gaussian_deriv(duration=duration, amp=amp, sigma=sigma) - np.testing.assert_almost_equal( - gaussian_deriv_pulse.get_waveform().samples, gaussian_deriv_waveform.samples - ) + GaussianDeriv(duration=duration, amp=amp, sigma=sigma) + with self.assertRaises(PulseError): Sech(duration=duration, amp=amp, sigma=0) def test_sech_pulse(self): - """Test that Sech sample pulse matches expectations, and parameter validation""" + """Test that Sech pulse creation""" duration = 100 amp = 0.5 sigma = 10 # Zero ends = True - sech_pulse = Sech(duration=duration, amp=amp, sigma=sigma) - sech_waveform = sech(duration=duration, amp=amp, sigma=sigma) - np.testing.assert_almost_equal(sech_pulse.get_waveform().samples, sech_waveform.samples) + Sech(duration=duration, amp=amp, sigma=sigma) # Zero ends = False - sech_pulse = Sech(duration=duration, amp=amp, sigma=sigma, zero_ends=False) - sech_waveform = sech(duration=duration, amp=amp, sigma=sigma, zero_ends=False) - np.testing.assert_almost_equal(sech_pulse.get_waveform().samples, sech_waveform.samples) + Sech(duration=duration, amp=amp, sigma=sigma, zero_ends=False) with self.assertRaises(PulseError): Sech(duration=duration, amp=amp, sigma=-5) def test_sech_deriv_pulse(self): - """Test that SechDeriv sample pulse matches expectations, and parameter validation""" + """Test that SechDeriv pulse creation""" duration = 100 amp = 0.5 sigma = 10 - sech_deriv_pulse = SechDeriv(duration=duration, amp=amp, sigma=sigma) - sech_deriv_waveform = sech_deriv(duration=duration, amp=amp, sigma=sigma) - np.testing.assert_almost_equal( - sech_deriv_pulse.get_waveform().samples, sech_deriv_waveform.samples - ) + SechDeriv(duration=duration, amp=amp, sigma=sigma) with self.assertRaises(PulseError): SechDeriv(duration=duration, amp=amp, sigma=-5) @@ -565,7 +493,7 @@ def test_parameters(self): self.assertEqual(set(const.parameters.keys()), {"duration", "amp", "angle"}) def test_repr(self): - """Test the repr methods for parametric pulses.""" + """Test the repr methods for symbolic pulses.""" gaus = Gaussian(duration=25, amp=0.7, sigma=4, angle=0.3) self.assertEqual(repr(gaus), "Gaussian(duration=25, sigma=4, amp=0.7, angle=0.3)") gaus_square = GaussianSquare(duration=20, sigma=30, amp=1.0, width=3) @@ -637,7 +565,7 @@ def test_repr(self): ) def test_param_validation(self): - """Test that parametric pulse parameters are validated when initialized.""" + """Test that symbolic pulse parameters are validated when initialized.""" with self.assertRaises(PulseError): Gaussian(duration=25, sigma=0, amp=0.5, angle=np.pi / 2) with self.assertRaises(PulseError): diff --git a/test/python/pulse/test_schedule.py b/test/python/pulse/test_schedule.py index 654b4ffe39f9..3ed4bde91c56 100644 --- a/test/python/pulse/test_schedule.py +++ b/test/python/pulse/test_schedule.py @@ -117,8 +117,8 @@ def test_fail_to_insert_instruction_into_occupied_timing(self): def test_can_create_valid_schedule(self): """Test valid schedule creation without error.""" - gp0 = library.gaussian(duration=20, amp=0.7, sigma=3) - gp1 = library.gaussian(duration=20, amp=0.7, sigma=3) + gp0 = library.Gaussian(duration=20, amp=0.7, sigma=3) + gp1 = library.Gaussian(duration=20, amp=0.7, sigma=3) sched = Schedule() sched = sched.append(Play(gp0, self.config.drive(0))) @@ -147,8 +147,8 @@ def test_can_create_valid_schedule(self): def test_can_create_valid_schedule_with_syntax_sugar(self): """Test that in place operations on schedule are still immutable and return equivalent schedules.""" - gp0 = library.gaussian(duration=20, amp=0.7, sigma=3) - gp1 = library.gaussian(duration=20, amp=0.5, sigma=3) + gp0 = library.Gaussian(duration=20, amp=0.7, sigma=3) + gp1 = library.Gaussian(duration=20, amp=0.5, sigma=3) sched = Schedule() sched += Play(gp0, self.config.drive(0)) @@ -162,8 +162,8 @@ def test_can_create_valid_schedule_with_syntax_sugar(self): def test_immutability(self): """Test that operations are immutable.""" - gp0 = library.gaussian(duration=100, amp=0.7, sigma=3) - gp1 = library.gaussian(duration=20, amp=0.5, sigma=3) + gp0 = library.Gaussian(duration=100, amp=0.7, sigma=3) + gp1 = library.Gaussian(duration=20, amp=0.5, sigma=3) sched = Play(gp1, self.config.drive(0)) << 100 # if schedule was mutable the next two sequences would overlap and an error @@ -173,8 +173,8 @@ def test_immutability(self): def test_inplace(self): """Test that in place operations on schedule are still immutable.""" - gp0 = library.gaussian(duration=100, amp=0.7, sigma=3) - gp1 = library.gaussian(duration=20, amp=0.5, sigma=3) + gp0 = library.Gaussian(duration=100, amp=0.7, sigma=3) + gp1 = library.Gaussian(duration=20, amp=0.5, sigma=3) sched = Schedule() sched = sched + Play(gp1, self.config.drive(0)) @@ -300,7 +300,7 @@ def test_auto_naming(self, is_main_process_mock): def test_name_inherited(self): """Test that schedule keeps name if an instruction is added.""" - gp0 = library.gaussian(duration=100, amp=0.7, sigma=3, name="pulse_name") + gp0 = library.Gaussian(duration=100, amp=0.7, sigma=3, name="pulse_name") snapshot = Snapshot("snapshot_label", "state") sched1 = Schedule(name="test_name") diff --git a/test/python/qasm2/test_circuit_methods.py b/test/python/qasm2/test_circuit_methods.py index 9027fb4d54b9..e6b35e582ccb 100644 --- a/test/python/qasm2/test_circuit_methods.py +++ b/test/python/qasm2/test_circuit_methods.py @@ -21,6 +21,7 @@ from qiskit.test import QiskitTestCase from qiskit.transpiler.passes import Unroller from qiskit.converters.circuit_to_dag import circuit_to_dag +from qiskit.qasm2 import dumps class LoadFromQasmTest(QiskitTestCase): @@ -254,18 +255,15 @@ def test_qasm_example_file(self): def test_qasm_qas_string_order(self): """Test that gates are returned in qasm in ascending order.""" - expected_qasm = ( - "\n".join( - [ - "OPENQASM 2.0;", - 'include "qelib1.inc";', - "qreg q[3];", - "h q[0];", - "h q[1];", - "h q[2];", - ] - ) - + "\n" + expected_qasm = "\n".join( + [ + "OPENQASM 2.0;", + 'include "qelib1.inc";', + "qreg q[3];", + "h q[0];", + "h q[1];", + "h q[2];", + ] ) qasm_string = """OPENQASM 2.0; include "qelib1.inc"; @@ -273,7 +271,7 @@ def test_qasm_qas_string_order(self): h q;""" q_circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(q_circuit.qasm(), expected_qasm) + self.assertEqual(dumps(q_circuit), expected_qasm) def test_from_qasm_str_custom_gate1(self): """Test load custom gates (simple case)""" diff --git a/test/python/qasm2/test_legacy_importer.py b/test/python/qasm2/test_legacy_importer.py deleted file mode 100644 index dea4d53f8fef..000000000000 --- a/test/python/qasm2/test_legacy_importer.py +++ /dev/null @@ -1,508 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - - -"""Test cases for the legacy OpenQASM 2 parser.""" - -# pylint: disable=missing-function-docstring - - -import os - -from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister -from qiskit.circuit import Gate, Parameter -from qiskit.converters import ast_to_dag, dag_to_circuit -from qiskit.exceptions import QiskitError -from qiskit.qasm import Qasm -from qiskit.test import QiskitTestCase -from qiskit.transpiler.passes import Unroller -from qiskit.converters.circuit_to_dag import circuit_to_dag - - -def from_qasm_str(qasm_str): - return dag_to_circuit(ast_to_dag(Qasm(data=qasm_str).parse())) - - -def from_qasm_file(path): - return dag_to_circuit(ast_to_dag(Qasm(filename=path).parse())) - - -class LoadFromQasmTest(QiskitTestCase): - """Test circuit.from_qasm_* set of methods.""" - - def setUp(self): - super().setUp() - self.qasm_file_name = "entangled_registers.qasm" - self.qasm_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "qasm" - ) - self.qasm_file_path = os.path.join(self.qasm_dir, self.qasm_file_name) - - def test_qasm_file(self): - """ - Test qasm_file and get_circuit. - - If all is correct we should get the qasm file loaded in _qasm_file_path - """ - q_circuit = from_qasm_file(self.qasm_file_path) - qr_a = QuantumRegister(4, "a") - qr_b = QuantumRegister(4, "b") - cr_c = ClassicalRegister(4, "c") - cr_d = ClassicalRegister(4, "d") - q_circuit_2 = QuantumCircuit(qr_a, qr_b, cr_c, cr_d) - q_circuit_2.h(qr_a) - q_circuit_2.cx(qr_a, qr_b) - q_circuit_2.barrier(qr_a) - q_circuit_2.barrier(qr_b) - q_circuit_2.measure(qr_a, cr_c) - q_circuit_2.measure(qr_b, cr_d) - self.assertEqual(q_circuit, q_circuit_2) - - def test_loading_all_qelib1_gates(self): - """Test setting up a circuit with all gates defined in qiskit/qasm/libs/qelib1.inc.""" - from qiskit.circuit.library import U1Gate, U2Gate, U3Gate, CU1Gate, CU3Gate, UGate - - all_gates_qasm = os.path.join(self.qasm_dir, "all_gates.qasm") - qasm_circuit = from_qasm_file(all_gates_qasm) - - ref_circuit = QuantumCircuit(3, 3) - - # abstract gates (legacy) - ref_circuit.append(UGate(0.2, 0.1, 0.6), [0]) - ref_circuit.cx(0, 1) - # the hardware primitives - ref_circuit.append(U3Gate(0.2, 0.1, 0.6), [0]) - ref_circuit.append(U2Gate(0.1, 0.6), [0]) - ref_circuit.append(U1Gate(0.6), [0]) - ref_circuit.id(0) - ref_circuit.cx(0, 1) - # the standard single qubit gates - ref_circuit.u(0.2, 0.1, 0.6, 0) - ref_circuit.p(0.6, 0) - ref_circuit.x(0) - ref_circuit.y(0) - ref_circuit.z(0) - ref_circuit.h(0) - ref_circuit.s(0) - ref_circuit.t(0) - ref_circuit.sdg(0) - ref_circuit.tdg(0) - ref_circuit.sx(0) - ref_circuit.sxdg(0) - # the standard rotations - ref_circuit.rx(0.1, 0) - ref_circuit.ry(0.1, 0) - ref_circuit.rz(0.1, 0) - # the barrier - ref_circuit.barrier() - # the standard user-defined gates - ref_circuit.swap(0, 1) - ref_circuit.cswap(0, 1, 2) - ref_circuit.cy(0, 1) - ref_circuit.cz(0, 1) - ref_circuit.ch(0, 1) - ref_circuit.csx(0, 1) - ref_circuit.append(CU1Gate(0.6), [0, 1]) - ref_circuit.append(CU3Gate(0.2, 0.1, 0.6), [0, 1]) - ref_circuit.cp(0.6, 0, 1) - ref_circuit.cu(0.2, 0.1, 0.6, 0, 0, 1) - ref_circuit.ccx(0, 1, 2) - ref_circuit.crx(0.6, 0, 1) - ref_circuit.cry(0.6, 0, 1) - ref_circuit.crz(0.6, 0, 1) - ref_circuit.rxx(0.2, 0, 1) - ref_circuit.rzz(0.2, 0, 1) - ref_circuit.measure([0, 1, 2], [0, 1, 2]) - - self.assertEqual(qasm_circuit, ref_circuit) - - def test_fail_qasm_file(self): - """ - Test fail_qasm_file. - - If all is correct we should get a QiskitError - """ - self.assertRaises(QiskitError, from_qasm_file, "") - - def test_qasm_text(self): - """ - Test qasm_text and get_circuit. - - If all is correct we should get the qasm file loaded from the string - """ - qasm_string = "// A simple 8 qubit example\nOPENQASM 2.0;\n" - qasm_string += 'include "qelib1.inc";\nqreg a[4];\n' - qasm_string += "qreg b[4];\ncreg c[4];\ncreg d[4];\nh a;\ncx a, b;\n" - qasm_string += "barrier a;\nbarrier b;\nmeasure a[0]->c[0];\n" - qasm_string += "measure a[1]->c[1];\nmeasure a[2]->c[2];\n" - qasm_string += "measure a[3]->c[3];\nmeasure b[0]->d[0];\n" - qasm_string += "measure b[1]->d[1];\nmeasure b[2]->d[2];\n" - qasm_string += "measure b[3]->d[3];" - q_circuit = from_qasm_str(qasm_string) - - qr_a = QuantumRegister(4, "a") - qr_b = QuantumRegister(4, "b") - cr_c = ClassicalRegister(4, "c") - cr_d = ClassicalRegister(4, "d") - ref = QuantumCircuit(qr_a, qr_b, cr_c, cr_d) - ref.h(qr_a[3]) - ref.cx(qr_a[3], qr_b[3]) - ref.h(qr_a[2]) - ref.cx(qr_a[2], qr_b[2]) - ref.h(qr_a[1]) - ref.cx(qr_a[1], qr_b[1]) - ref.h(qr_a[0]) - ref.cx(qr_a[0], qr_b[0]) - ref.barrier(qr_b) - ref.measure(qr_b, cr_d) - ref.barrier(qr_a) - ref.measure(qr_a, cr_c) - - self.assertEqual(len(q_circuit.cregs), 2) - self.assertEqual(len(q_circuit.qregs), 2) - self.assertEqual(q_circuit, ref) - - def test_qasm_text_conditional(self): - """ - Test qasm_text and get_circuit when conditionals are present. - """ - qasm_string = ( - "\n".join( - [ - "OPENQASM 2.0;", - 'include "qelib1.inc";', - "qreg q[1];", - "creg c0[4];", - "creg c1[4];", - "x q[0];", - "if(c1==4) x q[0];", - ] - ) - + "\n" - ) - q_circuit = from_qasm_str(qasm_string) - - qr = QuantumRegister(1, "q") - cr0 = ClassicalRegister(4, "c0") - cr1 = ClassicalRegister(4, "c1") - ref = QuantumCircuit(qr, cr0, cr1) - ref.x(qr[0]) - ref.x(qr[0]).c_if(cr1, 4) - - self.assertEqual(len(q_circuit.cregs), 2) - self.assertEqual(len(q_circuit.qregs), 1) - self.assertEqual(q_circuit, ref) - - def test_opaque_gate(self): - """ - Test parse an opaque gate - - See https://github.com/Qiskit/qiskit-terra/issues/1566. - """ - - qasm_string = ( - "\n".join( - [ - "OPENQASM 2.0;", - 'include "qelib1.inc";', - "opaque my_gate(theta,phi,lambda) a,b;", - "qreg q[3];", - "my_gate(1,2,3) q[1],q[2];", - ] - ) - + "\n" - ) - circuit = from_qasm_str(qasm_string) - - qr = QuantumRegister(3, "q") - expected = QuantumCircuit(qr) - expected.append(Gate(name="my_gate", num_qubits=2, params=[1, 2, 3]), [qr[1], qr[2]]) - - self.assertEqual(circuit, expected) - - def test_qasm_example_file(self): - """Loads qasm/example.qasm.""" - qasm_filename = os.path.join(self.qasm_dir, "example.qasm") - expected_circuit = from_qasm_str( - "\n".join( - [ - "OPENQASM 2.0;", - 'include "qelib1.inc";', - "qreg q[3];", - "qreg r[3];", - "creg c[3];", - "creg d[3];", - "h q[2];", - "cx q[2],r[2];", - "measure r[2] -> d[2];", - "h q[1];", - "cx q[1],r[1];", - "measure r[1] -> d[1];", - "h q[0];", - "cx q[0],r[0];", - "measure r[0] -> d[0];", - "barrier q[0],q[1],q[2];", - "measure q[2] -> c[2];", - "measure q[1] -> c[1];", - "measure q[0] -> c[0];", - ] - ) - + "\n" - ) - - q_circuit = from_qasm_file(qasm_filename) - - self.assertEqual(q_circuit, expected_circuit) - self.assertEqual(len(q_circuit.cregs), 2) - self.assertEqual(len(q_circuit.qregs), 2) - - def test_qasm_qas_string_order(self): - """Test that gates are returned in qasm in ascending order.""" - expected_qasm = ( - "\n".join( - [ - "OPENQASM 2.0;", - 'include "qelib1.inc";', - "qreg q[3];", - "h q[0];", - "h q[1];", - "h q[2];", - ] - ) - + "\n" - ) - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - qreg q[3]; - h q;""" - q_circuit = from_qasm_str(qasm_string) - - self.assertEqual(q_circuit.qasm(), expected_qasm) - - def test_from_qasm_str_custom_gate1(self): - """Test load custom gates (simple case)""" - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - gate rinv q {sdg q; h q; sdg q; h q; } - qreg qr[1]; - rinv qr[0];""" - circuit = from_qasm_str(qasm_string) - - rinv_q = QuantumRegister(1, name="q") - rinv_gate = QuantumCircuit(rinv_q, name="rinv") - rinv_gate.sdg(rinv_q) - rinv_gate.h(rinv_q) - rinv_gate.sdg(rinv_q) - rinv_gate.h(rinv_q) - rinv = rinv_gate.to_instruction() - qr = QuantumRegister(1, name="qr") - expected = QuantumCircuit(qr, name="circuit") - expected.append(rinv, [qr[0]]) - - self.assertEqualUnroll(["sdg", "h"], circuit, expected) - - def test_from_qasm_str_custom_gate2(self): - """Test load custom gates (no so simple case, different bit order) - See: https://github.com/Qiskit/qiskit-terra/pull/3393#issuecomment-551307250 - """ - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - gate swap2 a,b { - cx a,b; - cx b,a; // different bit order - cx a,b; - } - qreg qr[3]; - swap2 qr[0], qr[1]; - swap2 qr[1], qr[2];""" - circuit = from_qasm_str(qasm_string) - - ab_args = QuantumRegister(2, name="ab") - swap_gate = QuantumCircuit(ab_args, name="swap2") - swap_gate.cx(ab_args[0], ab_args[1]) - swap_gate.cx(ab_args[1], ab_args[0]) - swap_gate.cx(ab_args[0], ab_args[1]) - swap = swap_gate.to_instruction() - - qr = QuantumRegister(3, name="qr") - expected = QuantumCircuit(qr, name="circuit") - expected.append(swap, [qr[0], qr[1]]) - expected.append(swap, [qr[1], qr[2]]) - - self.assertEqualUnroll(["cx"], expected, circuit) - - def test_from_qasm_str_custom_gate3(self): - """Test load custom gates (no so simple case, different bit count) - See: https://github.com/Qiskit/qiskit-terra/pull/3393#issuecomment-551307250 - """ - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - gate cswap2 a,b,c - { - cx c,b; // different bit count - ccx a,b,c; //previously defined gate - cx c,b; - } - qreg qr[3]; - cswap2 qr[1], qr[0], qr[2];""" - circuit = from_qasm_str(qasm_string) - - abc_args = QuantumRegister(3, name="abc") - cswap_gate = QuantumCircuit(abc_args, name="cswap2") - cswap_gate.cx(abc_args[2], abc_args[1]) - cswap_gate.ccx(abc_args[0], abc_args[1], abc_args[2]) - cswap_gate.cx(abc_args[2], abc_args[1]) - cswap = cswap_gate.to_instruction() - - qr = QuantumRegister(3, name="qr") - expected = QuantumCircuit(qr, name="circuit") - expected.append(cswap, [qr[1], qr[0], qr[2]]) - - self.assertEqualUnroll(["cx", "h", "tdg", "t"], circuit, expected) - - def test_from_qasm_str_custom_gate4(self): - """Test load custom gates (parameterized) - See: https://github.com/Qiskit/qiskit-terra/pull/3393#issuecomment-551307250 - """ - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - gate my_gate(phi,lambda) q {u(1.5707963267948966,phi,lambda) q;} - qreg qr[1]; - my_gate(pi, pi) qr[0];""" - circuit = from_qasm_str(qasm_string) - - my_gate_circuit = QuantumCircuit(1, name="my_gate") - phi = Parameter("phi") - lam = Parameter("lambda") - my_gate_circuit.u(1.5707963267948966, phi, lam, 0) - my_gate = my_gate_circuit.to_gate() - - qr = QuantumRegister(1, name="qr") - expected = QuantumCircuit(qr, name="circuit") - expected.append(my_gate, [qr[0]]) - expected = expected.assign_parameters({phi: 3.141592653589793, lam: 3.141592653589793}) - - self.assertEqualUnroll("u", circuit, expected) - - def test_from_qasm_str_custom_gate5(self): - """Test load custom gates (parameterized, with biop and constant) - See: https://github.com/Qiskit/qiskit-terra/pull/3393#issuecomment-551307250 - """ - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - gate my_gate(phi,lambda) q {u(pi/2,phi,lambda) q;} // biop with pi - qreg qr[1]; - my_gate(pi, pi) qr[0];""" - circuit = from_qasm_str(qasm_string) - - my_gate_circuit = QuantumCircuit(1, name="my_gate") - phi = Parameter("phi") - lam = Parameter("lambda") - my_gate_circuit.u(1.5707963267948966, phi, lam, 0) - my_gate = my_gate_circuit.to_gate() - - qr = QuantumRegister(1, name="qr") - expected = QuantumCircuit(qr, name="circuit") - expected.append(my_gate, [qr[0]]) - expected = expected.assign_parameters({phi: 3.141592653589793, lam: 3.141592653589793}) - - self.assertEqualUnroll("u", circuit, expected) - - def test_from_qasm_str_custom_gate6(self): - """Test load custom gates (parameters used in expressions) - See: https://github.com/Qiskit/qiskit-terra/pull/3393#issuecomment-591668924 - """ - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - gate my_gate(phi,lambda) q - {rx(phi+pi) q; ry(lambda/2) q;} // parameters used in expressions - qreg qr[1]; - my_gate(pi, pi) qr[0];""" - circuit = from_qasm_str(qasm_string) - - my_gate_circuit = QuantumCircuit(1, name="my_gate") - phi = Parameter("phi") - lam = Parameter("lambda") - my_gate_circuit.rx(phi + 3.141592653589793, 0) - my_gate_circuit.ry(lam / 2, 0) - my_gate = my_gate_circuit.to_gate() - - qr = QuantumRegister(1, name="qr") - expected = QuantumCircuit(qr, name="circuit") - expected.append(my_gate, [qr[0]]) - expected = expected.assign_parameters({phi: 3.141592653589793, lam: 3.141592653589793}) - - self.assertEqualUnroll(["rx", "ry"], circuit, expected) - - def test_from_qasm_str_custom_gate7(self): - """Test load custom gates (build in functions) - See: https://github.com/Qiskit/qiskit-terra/pull/3393#issuecomment-592208951 - """ - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - gate my_gate(phi,lambda) q - {u(asin(cos(phi)/2), phi+pi, lambda/2) q;} // build func - qreg qr[1]; - my_gate(pi, pi) qr[0];""" - circuit = from_qasm_str(qasm_string) - - qr = QuantumRegister(1, name="qr") - expected = QuantumCircuit(qr, name="circuit") - expected.u(-0.5235987755982988, 6.283185307179586, 1.5707963267948966, qr[0]) - self.assertEqualUnroll("u", circuit, expected) - - def test_from_qasm_str_nested_custom_gate(self): - """Test chain of custom gates - See: https://github.com/Qiskit/qiskit-terra/pull/3393#issuecomment-592261942 - """ - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - gate my_other_gate(phi,lambda) q - {u(asin(cos(phi)/2), phi+pi, lambda/2) q;} - gate my_gate(phi) r - {my_other_gate(phi, phi+pi) r;} - qreg qr[1]; - my_gate(pi) qr[0];""" - circuit = from_qasm_str(qasm_string) - - qr = QuantumRegister(1, name="qr") - expected = QuantumCircuit(qr, name="circuit") - expected.u(-0.5235987755982988, 6.283185307179586, 3.141592653589793, qr[0]) - self.assertEqualUnroll("u", circuit, expected) - - def test_from_qasm_str_delay(self): - """Test delay instruction/opaque-gate - See: https://github.com/Qiskit/qiskit-terra/issues/6510 - """ - qasm_string = """OPENQASM 2.0; - include "qelib1.inc"; - - opaque delay(time) q; - - qreg q[1]; - delay(172) q[0];""" - circuit = from_qasm_str(qasm_string) - - qr = QuantumRegister(1, name="q") - expected = QuantumCircuit(qr, name="circuit") - expected.delay(172, qr[0]) - self.assertEqualUnroll("u", circuit, expected) - - def assertEqualUnroll(self, basis, circuit, expected): - """Compares the dags after unrolling to basis""" - circuit_dag = circuit_to_dag(circuit) - expected_dag = circuit_to_dag(expected) - with self.assertWarns(DeprecationWarning): - circuit_result = Unroller(basis).run(circuit_dag) - expected_result = Unroller(basis).run(expected_dag) - - self.assertEqual(circuit_result, expected_result) diff --git a/test/python/qasm2/test_structure.py b/test/python/qasm2/test_structure.py index 805e5c3bf8f0..0b9774357a5d 100644 --- a/test/python/qasm2/test_structure.py +++ b/test/python/qasm2/test_structure.py @@ -12,6 +12,7 @@ # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring +import copy import io import math import os @@ -696,6 +697,32 @@ def test_qpy_double_call_roundtrip(self): loaded = qpy.load(fptr)[0] self.assertEqual(loaded, qc) + def test_deepcopy_conditioned_defined_gate(self): + program = """ + include "qelib1.inc"; + gate my_gate a { + x a; + } + qreg q[1]; + creg c[1]; + if (c == 1) my_gate q[0]; + """ + parsed = qiskit.qasm2.loads(program) + my_gate = parsed.data[0].operation + + self.assertEqual(my_gate.name, "my_gate") + self.assertEqual(my_gate.condition, (parsed.cregs[0], 1)) + + copied = copy.deepcopy(parsed) + copied_gate = copied.data[0].operation + self.assertEqual(copied_gate.name, "my_gate") + self.assertEqual(copied_gate.condition, (copied.cregs[0], 1)) + + pickled = pickle.loads(pickle.dumps(parsed)) + pickled_gate = pickled.data[0].operation + self.assertEqual(pickled_gate.name, "my_gate") + self.assertEqual(pickled_gate.condition, (pickled.cregs[0], 1)) + class TestOpaque(QiskitTestCase): def test_simple(self): diff --git a/test/python/qpy/test_block_load_from_qpy.py b/test/python/qpy/test_block_load_from_qpy.py index e68ec48d4a00..321e018d5a64 100644 --- a/test/python/qpy/test_block_load_from_qpy.py +++ b/test/python/qpy/test_block_load_from_qpy.py @@ -37,7 +37,6 @@ ) from qiskit.pulse.instructions import Play, TimeBlockade from qiskit.circuit import Parameter, QuantumCircuit, Gate -from qiskit.exceptions import MissingOptionalLibraryError from qiskit.test import QiskitTestCase from qiskit.qpy import dump, load from qiskit.utils import optionals as _optional @@ -53,10 +52,10 @@ class QpyScheduleTestCase(QiskitTestCase): """QPY schedule testing platform.""" - def assert_roundtrip_equal(self, block): + def assert_roundtrip_equal(self, block, use_symengine=False): """QPY roundtrip equal test.""" qpy_file = io.BytesIO() - dump(block, qpy_file) + dump(block, qpy_file, use_symengine=use_symengine) qpy_file.seek(0) new_block = load(qpy_file)[0] @@ -278,6 +277,31 @@ def test_bell_schedule(self): self.assert_roundtrip_equal(test_sched) + @unittest.skipUnless(_optional.HAS_SYMENGINE, "Symengine required for this test") + def test_bell_schedule_use_symengine(self): + """Test complex schedule to create a Bell state.""" + with builder.build() as test_sched: + with builder.align_sequential(): + # H + builder.shift_phase(-1.57, DriveChannel(0)) + builder.play(Drag(160, 0.05, 40, 1.3), DriveChannel(0)) + builder.shift_phase(-1.57, DriveChannel(0)) + # ECR + with builder.align_left(): + builder.play(GaussianSquare(800, 0.05, 64, 544), DriveChannel(1)) + builder.play(GaussianSquare(800, 0.22, 64, 544, 2), ControlChannel(0)) + builder.play(Drag(160, 0.1, 40, 1.5), DriveChannel(0)) + with builder.align_left(): + builder.play(GaussianSquare(800, -0.05, 64, 544), DriveChannel(1)) + builder.play(GaussianSquare(800, -0.22, 64, 544, 2), ControlChannel(0)) + builder.play(Drag(160, 0.1, 40, 1.5), DriveChannel(0)) + # Measure + with builder.align_left(): + builder.play(GaussianSquare(8000, 0.2, 64, 7744), MeasureChannel(0)) + builder.acquire(8000, AcquireChannel(0), MemorySlot(0)) + + self.assert_roundtrip_equal(test_sched, True) + def test_with_acquire_instruction_with_kernel(self): """Test a schedblk with acquire instruction with kernel.""" kernel = Kernel( @@ -435,22 +459,3 @@ def test_symengine_full_path(self): qpy_file.seek(0) new_sched = load(qpy_file)[0] self.assertEqual(self.test_sched, new_sched) - - @unittest.skipIf(not _optional.HAS_SYMENGINE, "Install symengine to run this test.") - def test_dump_no_symengine(self): - """Test dump fails if symengine is not installed and use_symengine==True.""" - qpy_file = io.BytesIO() - with _optional.HAS_SYMENGINE.disable_locally(): - with self.assertRaises(MissingOptionalLibraryError): - dump(self.test_sched, qpy_file, use_symengine=True) - - @unittest.skipIf(not _optional.HAS_SYMENGINE, "Install symengine to run this test.") - def test_load_no_symengine(self): - """Test that load fails if symengine is not installed and the - file was created with use_symengine==True.""" - qpy_file = io.BytesIO() - dump(self.test_sched, qpy_file, use_symengine=True) - qpy_file.seek(0) - with _optional.HAS_SYMENGINE.disable_locally(): - with self.assertRaises(MissingOptionalLibraryError): - _ = load(qpy_file)[0] diff --git a/test/python/quantum_info/operators/symplectic/test_clifford.py b/test/python/quantum_info/operators/symplectic/test_clifford.py index 76cd9463f00e..615f09e6ad21 100644 --- a/test/python/quantum_info/operators/symplectic/test_clifford.py +++ b/test/python/quantum_info/operators/symplectic/test_clifford.py @@ -1044,7 +1044,8 @@ def test_visualize_does_not_throw_error(self): # An error may be thrown if visualization code calls op.condition instead # of getattr(op, "condition", None) clifford = random_clifford(3, seed=0) - print(clifford) + _ = str(clifford) + _ = repr(clifford) @combine(num_qubits=[1, 2, 3, 4]) def test_from_matrix_round_trip(self, num_qubits): diff --git a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py index 929534d26e8f..c519855faa6f 100644 --- a/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py +++ b/test/python/quantum_info/operators/symplectic/test_sparse_pauli_op.py @@ -1088,6 +1088,25 @@ def test_apply_layout_layout_list_and_num_qubits(self): res = op.apply_layout([4, 0], 5) self.assertEqual(SparsePauliOp.from_list([("IIIIY", 2), ("IIIIX", 1)]), res) + def test_apply_layout_null_layout_no_num_qubits(self): + """Test apply_layout with a null layout""" + op = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)]) + res = op.apply_layout(layout=None) + self.assertEqual(op, res) + + def test_apply_layout_null_layout_and_num_qubits(self): + """Test apply_layout with a null layout a num_qubits provided""" + op = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)]) + res = op.apply_layout(layout=None, num_qubits=5) + # this should expand the operator + self.assertEqual(SparsePauliOp.from_list([("IIIII", 1), ("IIIIZ", 2), ("IIIXI", 3)]), res) + + def test_apply_layout_null_layout_invalid_num_qubits(self): + """Test apply_layout with a null layout and num_qubits smaller than capable""" + op = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)]) + with self.assertRaises(QiskitError): + op.apply_layout(layout=None, num_qubits=1) + if __name__ == "__main__": unittest.main() diff --git a/test/python/quantum_info/states/test_statevector.py b/test/python/quantum_info/states/test_statevector.py index a7228d6bf5d2..c1c6beed27cc 100644 --- a/test/python/quantum_info/states/test_statevector.py +++ b/test/python/quantum_info/states/test_statevector.py @@ -33,7 +33,7 @@ from qiskit.quantum_info.operators.operator import Operator from qiskit.quantum_info.operators.symplectic import Pauli, SparsePauliOp from qiskit.quantum_info.operators.predicates import matrix_equal -from qiskit.visualization.state_visualization import numbers_to_latex_terms, state_to_latex +from qiskit.visualization.state_visualization import state_to_latex logger = logging.getLogger(__name__) @@ -1320,28 +1320,6 @@ def test_state_to_latex_with_decimals_round(self): "0.354 |000\\rangle+0.354 |001\\rangle- 0.354 i |110\\rangle+0.354 i |111\\rangle", ) - def test_number_to_latex_terms(self): - """Test conversions of complex numbers to latex terms""" - - cases = [ - ([1 - 8e-17, 0], ["", None]), - ([0, -1], [None, "-"]), - ([0, 1], [None, ""]), - ([0, 1j], [None, "i"]), - ([-1, 1], ["-", "+"]), - ([0, 1j], [None, "i"]), - ([-1, 1j], ["-", "+i"]), - ([1e-16 + 1j], ["i"]), - ([-1 + 1e-16 * 1j], ["-"]), - ([-1, -1 - 1j], ["-", "+(-1 - i)"]), - ([np.sqrt(2) / 2, np.sqrt(2) / 2], ["\\frac{\\sqrt{2}}{2}", "+\\frac{\\sqrt{2}}{2}"]), - ([1 + np.sqrt(2)], ["(1 + \\sqrt{2})"]), - ] - with self.assertWarns(DeprecationWarning): - for numbers, latex_terms in cases: - terms = numbers_to_latex_terms(numbers, 15) - self.assertListEqual(terms, latex_terms) - def test_statevector_draw_latex_regression(self): """Test numerical rounding errors are not printed""" sv = Statevector(np.array([1 - 8e-17, 8.32667268e-17j])) diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py index 2d134967e34f..c04337d3ffd3 100644 --- a/test/python/quantum_info/test_synthesis.py +++ b/test/python/quantum_info/test_synthesis.py @@ -30,6 +30,7 @@ from qiskit.circuit.library import ( HGate, IGate, + RGate, SdgGate, SGate, U3Gate, @@ -446,6 +447,8 @@ def test_special_RR(self): self.check_oneq_special_cases(U3Gate(-np.pi, 0.2, 0.0).to_matrix(), "RR", {"r": 1}) self.check_oneq_special_cases(U3Gate(np.pi, 0.0, 0.2).to_matrix(), "RR", {"r": 1}) self.check_oneq_special_cases(U3Gate(0.1, 0.2, 0.3).to_matrix(), "RR", {"r": 2}) + self.check_oneq_special_cases(U3Gate(0.1, 0.2, -0.2).to_matrix(), "RR", {"r": 1}) + self.check_oneq_special_cases(RGate(0.1, 0.2).to_matrix(), "RR", {"r": 1}) def test_special_U1X(self): """Special cases of U1X""" diff --git a/test/python/result/test_sampled_expval.py b/test/python/result/test_sampled_expval.py index f7cb06c5b86b..cb68099b310e 100644 --- a/test/python/result/test_sampled_expval.py +++ b/test/python/result/test_sampled_expval.py @@ -15,7 +15,6 @@ import unittest from qiskit.result import Counts, QuasiDistribution, ProbDistribution, sampled_expectation_value from qiskit.quantum_info import Pauli, SparsePauliOp -from qiskit.opflow import PauliOp, PauliSumOp from qiskit.test import QiskitTestCase @@ -82,17 +81,9 @@ def test_same(self): exp2 = sampled_expectation_value(counts, Pauli(oper)) self.assertAlmostEqual(exp2, ans) - with self.assertWarns(DeprecationWarning): - exp3 = sampled_expectation_value(counts, PauliOp(Pauli(oper))) - self.assertAlmostEqual(exp3, ans) - spo = SparsePauliOp([oper], coeffs=[1]) - with self.assertWarns(DeprecationWarning): - exp4 = sampled_expectation_value(counts, PauliSumOp(spo, coeff=2)) - self.assertAlmostEqual(exp4, 2 * ans) - - exp5 = sampled_expectation_value(counts, SparsePauliOp.from_list([[oper, 1]])) - self.assertAlmostEqual(exp5, ans) + exp3 = sampled_expectation_value(counts, spo) + self.assertAlmostEqual(exp3, ans) def test_asym_ops(self): """Test that asymmetric exp values work""" diff --git a/test/python/scheduler/test_basic_scheduler.py b/test/python/scheduler/test_basic_scheduler.py index 83782243d15f..f26a4fe6c7a8 100644 --- a/test/python/scheduler/test_basic_scheduler.py +++ b/test/python/scheduler/test_basic_scheduler.py @@ -1140,3 +1140,35 @@ def test_schedule_block_in_instmap(self): ref_sched += Play(Gaussian(100, 0.1, 10), DriveChannel(0)) self.assertEqual(sched, ref_sched) + + def test_inst_sched_map_get_measure_0(self): + """Test that Schedule returned by backend.instruction_schedule_map.get('measure', [0]) + is actually Schedule for just qubit_0""" + sched_from_backend = self.backend.instruction_schedule_map.get("measure", [0]) + expected_sched = Schedule( + (0, Acquire(1472, AcquireChannel(0), MemorySlot(0))), + (0, Acquire(1472, AcquireChannel(1), MemorySlot(1))), + (0, Acquire(1472, AcquireChannel(2), MemorySlot(2))), + (0, Acquire(1472, AcquireChannel(3), MemorySlot(3))), + (0, Acquire(1472, AcquireChannel(4), MemorySlot(4))), + (0, Acquire(1472, AcquireChannel(5), MemorySlot(5))), + (0, Acquire(1472, AcquireChannel(6), MemorySlot(6))), + ( + 0, + Play( + GaussianSquare( + duration=1472, + sigma=64, + width=1216, + amp=0.24000000000000002, + angle=-0.24730169436555283, + name="M_m0", + ), + MeasureChannel(0), + name="M_m0", + ), + ), + (1472, Delay(1568, MeasureChannel(0))), + name="measure", + ) + self.assertEqual(sched_from_backend, expected_sched) diff --git a/test/python/synthesis/test_qft_synthesis.py b/test/python/synthesis/test_qft_synthesis.py new file mode 100644 index 000000000000..2ea8e6033cb9 --- /dev/null +++ b/test/python/synthesis/test_qft_synthesis.py @@ -0,0 +1,62 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Tests for QFT synthesis methods.""" + + +import unittest +from test import combine +from ddt import ddt + +from qiskit.test import QiskitTestCase +from qiskit.circuit.library import QFT +from qiskit.synthesis.qft import synth_qft_line +from qiskit.quantum_info import Operator + +from qiskit.synthesis.linear.linear_circuits_utils import check_lnn_connectivity + + +@ddt +class TestQFTLNN(QiskitTestCase): + """Tests for QFT synthesis functions.""" + + @combine(num_qubits=[2, 3, 4, 5, 6, 7, 8], do_swaps=[True, False]) + def test_qft_lnn(self, num_qubits, do_swaps): + """Assert that the original and synthesized QFT circuits are the same.""" + qft_circ = QFT(num_qubits, do_swaps=do_swaps) + qft_lnn = synth_qft_line(num_qubits, do_swaps=do_swaps) + + with self.subTest(msg="original and synthesized QFT circuits are not the same"): + self.assertEqual(Operator(qft_circ), Operator(qft_lnn)) + + # Check that the output circuit has LNN connectivity + with self.subTest(msg="synthesized QFT circuit do not have LNN connectivity"): + self.assertTrue(check_lnn_connectivity(qft_lnn)) + + @combine(num_qubits=[5, 6, 7, 8], do_swaps=[True, False], approximation_degree=[2, 3]) + def test_qft_lnn_approximated(self, num_qubits, do_swaps, approximation_degree): + """Assert that the original and synthesized QFT circuits are the same with approximation.""" + qft_circ = QFT(num_qubits, do_swaps=do_swaps, approximation_degree=approximation_degree) + qft_lnn = synth_qft_line( + num_qubits, do_swaps=do_swaps, approximation_degree=approximation_degree + ) + + with self.subTest(msg="original and synthesized QFT circuits are not the same"): + self.assertEqual(Operator(qft_circ), Operator(qft_lnn)) + + # Check that the output circuit has LNN connectivity + with self.subTest(msg="synthesized QFT circuit do not have LNN connectivity"): + self.assertTrue(check_lnn_connectivity(qft_lnn)) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/test_qasm_parser.py b/test/python/test_qasm_parser.py deleted file mode 100644 index 49cdc2f12673..000000000000 --- a/test/python/test_qasm_parser.py +++ /dev/null @@ -1,126 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test for the QASM parser""" - -import os -import unittest -import ply -import ddt - -from qiskit.qasm import Qasm, QasmError -from qiskit.qasm.node.node import Node -from qiskit.test import QiskitTestCase - - -def parse(file_path): - """ - Simple helper - - file_path: Path to the OpenQASM file - - prec: Precision for the returned string - """ - qasm = Qasm(file_path) - return qasm.parse().qasm() - - -@ddt.ddt -class TestParser(QiskitTestCase): - """QasmParser""" - - def setUp(self): - super().setUp() - self.qasm_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "qasm") - self.qasm_file_path = os.path.join(self.qasm_dir, "example.qasm") - self.qasm_file_path_fail = os.path.join(self.qasm_dir, "example_fail.qasm") - self.qasm_file_path_if = os.path.join(self.qasm_dir, "example_if.qasm") - self.qasm_file_path_version_fail = os.path.join(self.qasm_dir, "example_version_fail.qasm") - self.qasm_file_path_version_2 = os.path.join(self.qasm_dir, "example_version_2.qasm") - self.qasm_file_path_minor_ver_fail = os.path.join( - self.qasm_dir, "example_minor_version_fail.qasm" - ) - - def test_parser(self): - """should return a correct response for a valid circuit.""" - - res = parse(self.qasm_file_path) - self.log.info(res) - # TODO: For now only some basic checks. - starts_expected = "OPENQASM 2.0;\ngate " - ends_expected = "\n".join( - [ - "}", - "qreg q[3];", - "qreg r[3];", - "h q;", - "cx q,r;", - "creg c[3];", - "creg d[3];", - "barrier q;", - "measure q -> c;", - "measure r -> d;", - "", - ] - ) - - self.assertEqual(res[: len(starts_expected)], starts_expected) - self.assertEqual(res[-len(ends_expected) :], ends_expected) - - def test_parser_fail(self): - """should fail a for a not valid circuit.""" - - self.assertRaisesRegex( - QasmError, "Perhaps there is a missing", parse, file_path=self.qasm_file_path_fail - ) - - @ddt.data("example_version_fail.qasm", "example_minor_version_fail.qasm") - def test_parser_version_fail(self, filename): - """Ensure versions other than 2.0 or 2 fail.""" - filename = os.path.join(self.qasm_dir, filename) - with self.assertRaisesRegex( - QasmError, r"Invalid version: '.+'\. This module supports OpenQASM 2\.0 only\." - ): - parse(filename) - - def test_parser_version_2(self): - """should succeed for OPENQASM version 2. Parser should automatically add minor verison.""" - res = parse(self.qasm_file_path_version_2) - version_start = "OPENQASM 2.0;" - self.assertEqual(res[: len(version_start)], version_start) - - def test_all_valid_nodes(self): - """Test that the tree contains only Node subclasses.""" - - def inspect(node): - """Inspect node children.""" - for child in node.children: - self.assertTrue(isinstance(child, Node)) - inspect(child) - - # Test the canonical example file. - qasm = Qasm(self.qasm_file_path) - res = qasm.parse() - inspect(res) - - # Test a file containing if instructions. - qasm_if = Qasm(self.qasm_file_path_if) - res_if = qasm_if.parse() - inspect(res_if) - - def test_generate_tokens(self): - """Test whether we get only valid tokens.""" - qasm = Qasm(self.qasm_file_path) - for token in qasm.generate_tokens(): - self.assertTrue(isinstance(token, ply.lex.LexToken)) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/test_util.py b/test/python/test_util.py index b574ff8390b0..d60aad284c18 100644 --- a/test/python/test_util.py +++ b/test/python/test_util.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2018. +# (C) Copyright IBM 2017, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -13,11 +13,9 @@ """Tests for qiskit/utils""" from unittest import mock -import numpy as np from qiskit.utils.multiprocessing import local_hardware_info from qiskit.test import QiskitTestCase -from qiskit.utils.arithmetic import triu_to_dense class TestUtil(QiskitTestCase): @@ -31,14 +29,3 @@ def test_local_hardware_none_cpu_count(self, cpu_count_mock, vmem_mock, platform del cpu_count_mock, vmem_mock, platform_mock # unused result = local_hardware_info() self.assertEqual(1, result["cpus"]) - - def test_triu_to_dense(self): - """Test conversion of upper triangular matrix to dense matrix.""" - np.random.seed(50) - n = np.random.randint(5, 15) - m = np.random.randint(-100, 100, size=(n, n)) - symm = (m + m.T) / 2 - - triu = [[symm[i, j] for i in range(j, n)] for j in range(n)] - - self.assertTrue(np.array_equal(symm, triu_to_dense(triu))) diff --git a/test/python/test_version.py b/test/python/test_version.py deleted file mode 100644 index 3d260d6a3a8e..000000000000 --- a/test/python/test_version.py +++ /dev/null @@ -1,26 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for qiskit/version.py""" - -from qiskit import __qiskit_version__ -from qiskit import __version__ -from qiskit.test import QiskitTestCase - - -class TestVersion(QiskitTestCase): - """Tests for qiskit/version.py""" - - def test_qiskit_version(self): - """Test qiskit-version sets the correct version for terra.""" - with self.assertWarnsRegex(DeprecationWarning, "__qiskit_version__"): - self.assertEqual(__version__, __qiskit_version__["qiskit"]) diff --git a/test/python/transpiler/aqc/test_aqc.py b/test/python/transpiler/aqc/test_aqc.py index 45b1a5ea51ee..c54de8c21dc7 100644 --- a/test/python/transpiler/aqc/test_aqc.py +++ b/test/python/transpiler/aqc/test_aqc.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,10 +12,15 @@ """ Tests AQC framework using hardcoded and randomly generated circuits. """ +from functools import partial + import unittest from test.python.transpiler.aqc.sample_data import ORIGINAL_CIRCUIT, INITIAL_THETAS + +from ddt import ddt, data import numpy as np -from qiskit.algorithms.optimizers import L_BFGS_B +from scipy.optimize import minimize + from qiskit.quantum_info import Operator from qiskit.test import QiskitTestCase from qiskit.transpiler.synthesis.aqc.aqc import AQC @@ -25,10 +30,12 @@ from qiskit.transpiler.synthesis.aqc.fast_gradient.fast_gradient import FastCNOTUnitObjective +@ddt class TestAqc(QiskitTestCase): """Main tests of approximate quantum compiler.""" - def test_aqc(self): + @data(True, False) + def test_aqc(self, uses_default): """Tests AQC on a hardcoded circuit/matrix.""" seed = 12345 @@ -38,9 +45,11 @@ def test_aqc(self): num_qubits=num_qubits, network_layout="spin", connectivity_type="full", depth=0 ) - optimizer = L_BFGS_B(maxiter=200) - - aqc = AQC(optimizer=optimizer, seed=seed) + if uses_default: + aqc = AQC(seed=seed) + else: + optimizer = partial(minimize, args=(), method="L-BFGS-B", options={"maxiter": 200}) + aqc = AQC(optimizer=optimizer, seed=seed) target_matrix = ORIGINAL_CIRCUIT approximate_circuit = CNOTUnitCircuit(num_qubits, cnots) @@ -55,7 +64,7 @@ def test_aqc(self): approx_matrix = Operator(approximate_circuit).data error = 0.5 * (np.linalg.norm(approx_matrix - ORIGINAL_CIRCUIT, "fro") ** 2) - self.assertTrue(error < 1e-3) + self.assertLess(error, 1e-3) def test_aqc_fastgrad(self): """ @@ -70,7 +79,7 @@ def test_aqc_fastgrad(self): num_qubits=num_qubits, network_layout="spin", connectivity_type="full", depth=0 ) - optimizer = L_BFGS_B(maxiter=200) + optimizer = partial(minimize, args=(), method="L-BFGS-B", options={"maxiter": 200}) aqc = AQC(optimizer=optimizer, seed=seed) # Make multi-control CNOT gate matrix. @@ -103,7 +112,7 @@ def test_aqc_determinant_minus_one(self): num_qubits=num_qubits, network_layout="spin", connectivity_type="full", depth=0 ) - optimizer = L_BFGS_B(maxiter=200) + optimizer = partial(minimize, args=(), method="L-BFGS-B", options={"maxiter": 200}) aqc = AQC(optimizer=optimizer, seed=seed) target_matrix = np.eye(2**num_qubits, dtype=int) diff --git a/test/python/transpiler/aqc/test_aqc_plugin.py b/test/python/transpiler/aqc/test_aqc_plugin.py index 0ad2742a1195..b5f3bf1858f4 100644 --- a/test/python/transpiler/aqc/test_aqc_plugin.py +++ b/test/python/transpiler/aqc/test_aqc_plugin.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -12,11 +12,12 @@ """ Tests AQC plugin. """ +from functools import partial import numpy as np +from scipy.optimize import minimize from qiskit import QuantumCircuit -from qiskit.algorithms.optimizers import SLSQP from qiskit.converters import dag_to_circuit, circuit_to_dag from qiskit.quantum_info import Operator from qiskit.test import QiskitTestCase @@ -68,12 +69,13 @@ def test_plugin_setup(self): def test_plugin_configuration(self): """Tests plugin with a custom configuration.""" + optimizer = partial(minimize, args=(), method="SLSQP") config = { "network_layout": "sequ", "connectivity_type": "full", "depth": 0, "seed": 12345, - "optimizer": SLSQP(), + "optimizer": optimizer, } transpiler_pass = UnitarySynthesis( basis_gates=["rx", "ry", "rz", "cx"], method="aqc", plugin_config=config diff --git a/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py b/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py index feb50bab8002..efd3b86ee393 100644 --- a/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py +++ b/test/python/transpiler/legacy_scheduling/test_instruction_alignments.py @@ -14,8 +14,316 @@ from qiskit import QuantumCircuit, pulse from qiskit.test import QiskitTestCase +from qiskit.transpiler import InstructionDurations from qiskit.transpiler.exceptions import TranspilerError -from qiskit.transpiler.passes import ValidatePulseGates +from qiskit.transpiler.passes import ( + AlignMeasures, + ValidatePulseGates, + ALAPSchedule, + TimeUnitConversion, +) + + +class TestAlignMeasures(QiskitTestCase): + """A test for measurement alignment pass.""" + + def setUp(self): + super().setUp() + instruction_durations = InstructionDurations() + instruction_durations.update( + [ + ("rz", (0,), 0), + ("rz", (1,), 0), + ("x", (0,), 160), + ("x", (1,), 160), + ("sx", (0,), 160), + ("sx", (1,), 160), + ("cx", (0, 1), 800), + ("cx", (1, 0), 800), + ("measure", None, 1600), + ] + ) + self.time_conversion_pass = TimeUnitConversion(inst_durations=instruction_durations) + # reproduce old behavior of 0.20.0 before #7655 + # currently default write latency is 0 + self.scheduling_pass = ALAPSchedule( + durations=instruction_durations, + clbit_write_latency=1600, + conditional_latency=0, + ) + self.align_measure_pass = AlignMeasures(alignment=16) + + def test_t1_experiment_type(self): + """Test T1 experiment type circuit. + + (input) + + ┌───┐┌────────────────┐┌─┐ + q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├ + └───┘└────────────────┘└╥┘ + c: 1/════════════════════════╩═ + 0 + + (aligned) + + ┌───┐┌────────────────┐┌─┐ + q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├ + └───┘└────────────────┘└╥┘ + c: 1/════════════════════════╩═ + 0 + + This type of experiment slightly changes delay duration of interest. + However the quantization error should be less than alignment * dt. + """ + circuit = QuantumCircuit(1, 1) + circuit.x(0) + circuit.delay(100, 0, unit="dt") + circuit.measure(0, 0) + + timed_circuit = self.time_conversion_pass(circuit) + scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"}) + aligned_circuit = self.align_measure_pass( + scheduled_circuit, property_set={"time_unit": "dt"} + ) + + ref_circuit = QuantumCircuit(1, 1) + ref_circuit.x(0) + ref_circuit.delay(112, 0, unit="dt") + ref_circuit.measure(0, 0) + + self.assertEqual(aligned_circuit, ref_circuit) + + def test_hanh_echo_experiment_type(self): + """Test Hahn echo experiment type circuit. + + (input) + + ┌────┐┌────────────────┐┌───┐┌────────────────┐┌────┐┌─┐ + q_0: ┤ √X ├┤ Delay(100[dt]) ├┤ X ├┤ Delay(100[dt]) ├┤ √X ├┤M├ + └────┘└────────────────┘└───┘└────────────────┘└────┘└╥┘ + c: 1/══════════════════════════════════════════════════════╩═ + 0 + + (output) + + ┌────┐┌────────────────┐┌───┐┌────────────────┐┌────┐┌──────────────┐┌─┐ + q_0: ┤ √X ├┤ Delay(100[dt]) ├┤ X ├┤ Delay(100[dt]) ├┤ √X ├┤ Delay(8[dt]) ├┤M├ + └────┘└────────────────┘└───┘└────────────────┘└────┘└──────────────┘└╥┘ + c: 1/══════════════════════════════════════════════════════════════════════╩═ + 0 + + This type of experiment doesn't change duration of interest (two in the middle). + However induces slight delay less than alignment * dt before measurement. + This might induce extra amplitude damping error. + """ + circuit = QuantumCircuit(1, 1) + circuit.sx(0) + circuit.delay(100, 0, unit="dt") + circuit.x(0) + circuit.delay(100, 0, unit="dt") + circuit.sx(0) + circuit.measure(0, 0) + + timed_circuit = self.time_conversion_pass(circuit) + scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"}) + aligned_circuit = self.align_measure_pass( + scheduled_circuit, property_set={"time_unit": "dt"} + ) + + ref_circuit = QuantumCircuit(1, 1) + ref_circuit.sx(0) + ref_circuit.delay(100, 0, unit="dt") + ref_circuit.x(0) + ref_circuit.delay(100, 0, unit="dt") + ref_circuit.sx(0) + ref_circuit.delay(8, 0, unit="dt") + ref_circuit.measure(0, 0) + + self.assertEqual(aligned_circuit, ref_circuit) + + def test_mid_circuit_measure(self): + """Test circuit with mid circuit measurement. + + (input) + + ┌───┐┌────────────────┐┌─┐┌───────────────┐┌───┐┌────────────────┐┌─┐ + q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├┤ Delay(10[dt]) ├┤ X ├┤ Delay(120[dt]) ├┤M├ + └───┘└────────────────┘└╥┘└───────────────┘└───┘└────────────────┘└╥┘ + c: 2/════════════════════════╩══════════════════════════════════════════╩═ + 0 1 + + (output) + + ┌───┐┌────────────────┐┌─┐┌───────────────┐┌───┐┌────────────────┐┌─┐ + q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├┤ Delay(10[dt]) ├┤ X ├┤ Delay(134[dt]) ├┤M├ + └───┘└────────────────┘└╥┘└───────────────┘└───┘└────────────────┘└╥┘ + c: 2/════════════════════════╩══════════════════════════════════════════╩═ + 0 1 + + Extra delay is always added to the existing delay right before the measurement. + Delay after measurement is unchanged. + """ + circuit = QuantumCircuit(1, 2) + circuit.x(0) + circuit.delay(100, 0, unit="dt") + circuit.measure(0, 0) + circuit.delay(10, 0, unit="dt") + circuit.x(0) + circuit.delay(120, 0, unit="dt") + circuit.measure(0, 1) + + timed_circuit = self.time_conversion_pass(circuit) + scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"}) + aligned_circuit = self.align_measure_pass( + scheduled_circuit, property_set={"time_unit": "dt"} + ) + + ref_circuit = QuantumCircuit(1, 2) + ref_circuit.x(0) + ref_circuit.delay(112, 0, unit="dt") + ref_circuit.measure(0, 0) + ref_circuit.delay(10, 0, unit="dt") + ref_circuit.x(0) + ref_circuit.delay(134, 0, unit="dt") + ref_circuit.measure(0, 1) + + self.assertEqual(aligned_circuit, ref_circuit) + + def test_mid_circuit_multiq_gates(self): + """Test circuit with mid circuit measurement and multi qubit gates. + + (input) + + ┌───┐┌────────────────┐┌─┐ ┌─┐ + q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├──■───────■──┤M├ + └───┘└────────────────┘└╥┘┌─┴─┐┌─┐┌─┴─┐└╥┘ + q_1: ────────────────────────╫─┤ X ├┤M├┤ X ├─╫─ + ║ └───┘└╥┘└───┘ ║ + c: 2/════════════════════════╩═══════╩═══════╩═ + 0 1 0 + + (output) + + ┌───┐ ┌────────────────┐┌─┐ ┌─────────────────┐ ┌─┐» + q_0: ───────┤ X ├───────┤ Delay(112[dt]) ├┤M├──■──┤ Delay(1600[dt]) ├──■──┤M├» + ┌──────┴───┴──────┐└────────────────┘└╥┘┌─┴─┐└───────┬─┬───────┘┌─┴─┐└╥┘» + q_1: ┤ Delay(1872[dt]) ├───────────────────╫─┤ X ├────────┤M├────────┤ X ├─╫─» + └─────────────────┘ ║ └───┘ └╥┘ └───┘ ║ » + c: 2/══════════════════════════════════════╩═══════════════╩═══════════════╩═» + 0 1 0 » + « + «q_0: ─────────────────── + « ┌─────────────────┐ + «q_1: ┤ Delay(1600[dt]) ├ + « └─────────────────┘ + «c: 2/═══════════════════ + « + + Delay for the other channel paired by multi-qubit instruction is also scheduled. + Delay (1872dt) = X (160dt) + Delay (100dt + extra 12dt) + Measure (1600dt). + """ + circuit = QuantumCircuit(2, 2) + circuit.x(0) + circuit.delay(100, 0, unit="dt") + circuit.measure(0, 0) + circuit.cx(0, 1) + circuit.measure(1, 1) + circuit.cx(0, 1) + circuit.measure(0, 0) + + timed_circuit = self.time_conversion_pass(circuit) + scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"}) + aligned_circuit = self.align_measure_pass( + scheduled_circuit, property_set={"time_unit": "dt"} + ) + + ref_circuit = QuantumCircuit(2, 2) + ref_circuit.x(0) + ref_circuit.delay(112, 0, unit="dt") + ref_circuit.measure(0, 0) + ref_circuit.delay(160 + 112 + 1600, 1, unit="dt") + ref_circuit.cx(0, 1) + ref_circuit.delay(1600, 0, unit="dt") + ref_circuit.measure(1, 1) + ref_circuit.cx(0, 1) + ref_circuit.delay(1600, 1, unit="dt") + ref_circuit.measure(0, 0) + + self.assertEqual(aligned_circuit, ref_circuit) + + def test_alignment_is_not_processed(self): + """Test avoid pass processing if delay is aligned.""" + circuit = QuantumCircuit(2, 2) + circuit.x(0) + circuit.delay(160, 0, unit="dt") + circuit.measure(0, 0) + circuit.cx(0, 1) + circuit.measure(1, 1) + circuit.cx(0, 1) + circuit.measure(0, 0) + + # pre scheduling is not necessary because alignment is skipped + # this is to minimize breaking changes to existing code. + transpiled = self.align_measure_pass(circuit, property_set={"time_unit": "dt"}) + + self.assertEqual(transpiled, circuit) + + def test_circuit_using_clbit(self): + """Test a circuit with instructions using a common clbit. + + (input) + ┌───┐┌────────────────┐┌─┐ + q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├────────────── + └───┘└────────────────┘└╥┘ ┌───┐ + q_1: ────────────────────────╫────┤ X ├────── + ║ └─╥─┘ ┌─┐ + q_2: ────────────────────────╫──────╫─────┤M├ + ║ ┌────╨────┐└╥┘ + c: 1/════════════════════════╩═╡ c_0 = T ╞═╩═ + 0 └─────────┘ 0 + + (aligned) + ┌───┐ ┌────────────────┐┌─┐┌────────────────┐ + q_0: ───────┤ X ├───────┤ Delay(112[dt]) ├┤M├┤ Delay(160[dt]) ├─── + ┌──────┴───┴──────┐└────────────────┘└╥┘└─────┬───┬──────┘ + q_1: ┤ Delay(1872[dt]) ├───────────────────╫───────┤ X ├────────── + └┬────────────────┤ ║ └─╥─┘ ┌─┐ + q_2: ─┤ Delay(432[dt]) ├───────────────────╫─────────╫─────────┤M├ + └────────────────┘ ║ ┌────╨────┐ └╥┘ + c: 1/══════════════════════════════════════╩════╡ c_0 = T ╞═════╩═ + 0 └─────────┘ 0 + + Looking at the q_0, the total schedule length T becomes + 160 (x) + 112 (aligned delay) + 1600 (measure) + 160 (delay) = 2032. + The last delay comes from ALAP scheduling called before the AlignMeasure pass, + which aligns stop times as late as possible, so the start time of x(1).c_if(0) + and the stop time of measure(0, 0) become T - 160. + """ + circuit = QuantumCircuit(3, 1) + circuit.x(0) + circuit.delay(100, 0, unit="dt") + circuit.measure(0, 0) + circuit.x(1).c_if(0, 1) + circuit.measure(2, 0) + + timed_circuit = self.time_conversion_pass(circuit) + scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"}) + aligned_circuit = self.align_measure_pass( + scheduled_circuit, property_set={"time_unit": "dt"} + ) + self.assertEqual(aligned_circuit.duration, 2032) + + ref_circuit = QuantumCircuit(3, 1) + ref_circuit.x(0) + ref_circuit.delay(112, 0, unit="dt") + ref_circuit.delay(1872, 1, unit="dt") # 2032 - 160 + ref_circuit.delay(432, 2, unit="dt") # 2032 - 1600 + ref_circuit.measure(0, 0) + ref_circuit.x(1).c_if(0, 1) + ref_circuit.delay(160, 0, unit="dt") + ref_circuit.measure(2, 0) + + self.assertEqual(aligned_circuit, ref_circuit) class TestPulseGateValidation(QiskitTestCase): diff --git a/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py b/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py new file mode 100644 index 000000000000..2f375c46f67b --- /dev/null +++ b/test/python/transpiler/legacy_scheduling/test_scheduling_pass.py @@ -0,0 +1,811 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Test the legacy Scheduling passes""" + +import unittest + +from ddt import ddt, data, unpack + +from qiskit import QuantumCircuit +from qiskit.circuit import Delay, Parameter +from qiskit.circuit.library.standard_gates import XGate, YGate, CXGate +from qiskit.test import QiskitTestCase +from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.instruction_durations import InstructionDurations +from qiskit.transpiler.passes import ASAPSchedule, ALAPSchedule, DynamicalDecoupling +from qiskit.transpiler.passmanager import PassManager +from qiskit.transpiler.target import Target, InstructionProperties + + +@ddt +class TestSchedulingPass(QiskitTestCase): + """Tests the Scheduling passes""" + + def test_alap_agree_with_reverse_asap_reverse(self): + """Test if ALAP schedule agrees with doubly-reversed ASAP schedule.""" + qc = QuantumCircuit(2) + qc.h(0) + qc.delay(500, 1) + qc.cx(0, 1) + qc.measure_all() + + durations = InstructionDurations( + [("h", 0, 200), ("cx", [0, 1], 700), ("measure", None, 1000)] + ) + + pm = PassManager(ALAPSchedule(durations)) + alap_qc = pm.run(qc) + + pm = PassManager(ASAPSchedule(durations)) + new_qc = pm.run(qc.reverse_ops()) + new_qc = new_qc.reverse_ops() + new_qc.name = new_qc.name + + self.assertEqual(alap_qc, new_qc) + + @data(ALAPSchedule, ASAPSchedule) + def test_classically_controlled_gate_after_measure(self, schedule_pass): + """Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit. + See: https://github.com/Qiskit/qiskit-terra/issues/7654 + + (input) + ┌─┐ + q_0: ┤M├─────────── + └╥┘ ┌───┐ + q_1: ─╫────┤ X ├─── + ║ └─╥─┘ + ║ ┌────╨────┐ + c: 1/═╩═╡ c_0 = T ╞ + 0 └─────────┘ + + (scheduled) + ┌─┐┌────────────────┐ + q_0: ───────────────────┤M├┤ Delay(200[dt]) ├ + ┌─────────────────┐└╥┘└─────┬───┬──────┘ + q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├─────── + └─────────────────┘ ║ └─╥─┘ + ║ ┌────╨────┐ + c: 1/════════════════════╩════╡ c_0=0x1 ╞════ + 0 └─────────┘ + """ + qc = QuantumCircuit(2, 1) + qc.measure(0, 0) + qc.x(1).c_if(0, True) + + durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) + pm = PassManager(schedule_pass(durations)) + scheduled = pm.run(qc) + + expected = QuantumCircuit(2, 1) + expected.measure(0, 0) + expected.delay(1000, 1) # x.c_if starts after measure + expected.x(1).c_if(0, True) + expected.delay(200, 0) + + self.assertEqual(expected, scheduled) + + @data(ALAPSchedule, ASAPSchedule) + def test_measure_after_measure(self, schedule_pass): + """Test if ALAP/ASAP schedules circuits with measure after measure with a common clbit. + See: https://github.com/Qiskit/qiskit-terra/issues/7654 + + (input) + ┌───┐┌─┐ + q_0: ┤ X ├┤M├─── + └───┘└╥┘┌─┐ + q_1: ──────╫─┤M├ + ║ └╥┘ + c: 1/══════╩══╩═ + 0 0 + + (scheduled) + ┌───┐ ┌─┐┌─────────────────┐ + q_0: ───────┤ X ├───────┤M├┤ Delay(1000[dt]) ├ + ┌──────┴───┴──────┐└╥┘└───────┬─┬───────┘ + q_1: ┤ Delay(1200[dt]) ├─╫─────────┤M├──────── + └─────────────────┘ ║ └╥┘ + c: 1/════════════════════╩══════════╩═════════ + 0 0 + """ + qc = QuantumCircuit(2, 1) + qc.x(0) + qc.measure(0, 0) + qc.measure(1, 0) + + durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) + pm = PassManager(schedule_pass(durations)) + scheduled = pm.run(qc) + + expected = QuantumCircuit(2, 1) + expected.x(0) + expected.measure(0, 0) + expected.delay(1200, 1) + expected.measure(1, 0) + expected.delay(1000, 0) + + self.assertEqual(expected, scheduled) + + @data(ALAPSchedule, ASAPSchedule) + def test_c_if_on_different_qubits(self, schedule_pass): + """Test if ALAP/ASAP schedules circuits with `c_if`s on different qubits. + + (input) + ┌─┐ + q_0: ┤M├────────────────────── + └╥┘ ┌───┐ + q_1: ─╫────┤ X ├────────────── + ║ └─╥─┘ ┌───┐ + q_2: ─╫──────╫────────┤ X ├─── + ║ ║ └─╥─┘ + ║ ┌────╨────┐┌────╨────┐ + c: 1/═╩═╡ c_0 = T ╞╡ c_0 = T ╞ + 0 └─────────┘└─────────┘ + + (scheduled) + + ┌─┐┌────────────────┐ + q_0: ───────────────────┤M├┤ Delay(200[dt]) ├─────────── + ┌─────────────────┐└╥┘└─────┬───┬──────┘ + q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├────────────────── + ├─────────────────┤ ║ └─╥─┘ ┌───┐ + q_2: ┤ Delay(1000[dt]) ├─╫─────────╫────────────┤ X ├─── + └─────────────────┘ ║ ║ └─╥─┘ + ║ ┌────╨────┐ ┌────╨────┐ + c: 1/════════════════════╩════╡ c_0=0x1 ╞════╡ c_0=0x1 ╞ + 0 └─────────┘ └─────────┘ + """ + qc = QuantumCircuit(3, 1) + qc.measure(0, 0) + qc.x(1).c_if(0, True) + qc.x(2).c_if(0, True) + + durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) + pm = PassManager(schedule_pass(durations)) + scheduled = pm.run(qc) + + expected = QuantumCircuit(3, 1) + expected.measure(0, 0) + expected.delay(1000, 1) + expected.delay(1000, 2) + expected.x(1).c_if(0, True) + expected.x(2).c_if(0, True) + expected.delay(200, 0) + + self.assertEqual(expected, scheduled) + + @data(ALAPSchedule, ASAPSchedule) + def test_shorter_measure_after_measure(self, schedule_pass): + """Test if ALAP/ASAP schedules circuits with shorter measure after measure with a common clbit. + + (input) + ┌─┐ + q_0: ┤M├─── + └╥┘┌─┐ + q_1: ─╫─┤M├ + ║ └╥┘ + c: 1/═╩══╩═ + 0 0 + + (scheduled) + ┌─┐┌────────────────┐ + q_0: ───────────────────┤M├┤ Delay(700[dt]) ├ + ┌─────────────────┐└╥┘└──────┬─┬───────┘ + q_1: ┤ Delay(1000[dt]) ├─╫────────┤M├──────── + └─────────────────┘ ║ └╥┘ + c: 1/════════════════════╩═════════╩═════════ + 0 0 + """ + qc = QuantumCircuit(2, 1) + qc.measure(0, 0) + qc.measure(1, 0) + + durations = InstructionDurations([("measure", [0], 1000), ("measure", [1], 700)]) + pm = PassManager(schedule_pass(durations)) + scheduled = pm.run(qc) + + expected = QuantumCircuit(2, 1) + expected.measure(0, 0) + expected.delay(1000, 1) + expected.measure(1, 0) + expected.delay(700, 0) + + self.assertEqual(expected, scheduled) + + @data(ALAPSchedule, ASAPSchedule) + def test_measure_after_c_if(self, schedule_pass): + """Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit. + + (input) + ┌─┐ + q_0: ┤M├────────────── + └╥┘ ┌───┐ + q_1: ─╫────┤ X ├────── + ║ └─╥─┘ ┌─┐ + q_2: ─╫──────╫─────┤M├ + ║ ┌────╨────┐└╥┘ + c: 1/═╩═╡ c_0 = T ╞═╩═ + 0 └─────────┘ 0 + + (scheduled) + ┌─┐┌─────────────────┐ + q_0: ───────────────────┤M├┤ Delay(1000[dt]) ├────────────────── + ┌─────────────────┐└╥┘└──────┬───┬──────┘┌────────────────┐ + q_1: ┤ Delay(1000[dt]) ├─╫────────┤ X ├───────┤ Delay(800[dt]) ├ + ├─────────────────┤ ║ └─╥─┘ └──────┬─┬───────┘ + q_2: ┤ Delay(1000[dt]) ├─╫──────────╫────────────────┤M├──────── + └─────────────────┘ ║ ┌────╨────┐ └╥┘ + c: 1/════════════════════╩═════╡ c_0=0x1 ╞════════════╩═════════ + 0 └─────────┘ 0 + """ + qc = QuantumCircuit(3, 1) + qc.measure(0, 0) + qc.x(1).c_if(0, 1) + qc.measure(2, 0) + + durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) + pm = PassManager(schedule_pass(durations)) + scheduled = pm.run(qc) + + expected = QuantumCircuit(3, 1) + expected.delay(1000, 1) + expected.delay(1000, 2) + expected.measure(0, 0) + expected.x(1).c_if(0, 1) + expected.measure(2, 0) + expected.delay(1000, 0) + expected.delay(800, 1) + + self.assertEqual(expected, scheduled) + + def test_parallel_gate_different_length(self): + """Test circuit having two parallel instruction with different length. + + (input) + ┌───┐┌─┐ + q_0: ┤ X ├┤M├─── + ├───┤└╥┘┌─┐ + q_1: ┤ X ├─╫─┤M├ + └───┘ ║ └╥┘ + c: 2/══════╩══╩═ + 0 1 + + (expected, ALAP) + ┌────────────────┐┌───┐┌─┐ + q_0: ┤ Delay(200[dt]) ├┤ X ├┤M├ + └─────┬───┬──────┘└┬─┬┘└╥┘ + q_1: ──────┤ X ├────────┤M├──╫─ + └───┘ └╥┘ ║ + c: 2/════════════════════╩═══╩═ + 1 0 + + (expected, ASAP) + ┌───┐┌─┐┌────────────────┐ + q_0: ┤ X ├┤M├┤ Delay(200[dt]) ├ + ├───┤└╥┘└──────┬─┬───────┘ + q_1: ┤ X ├─╫────────┤M├──────── + └───┘ ║ └╥┘ + c: 2/══════╩═════════╩═════════ + 0 1 + + """ + qc = QuantumCircuit(2, 2) + qc.x(0) + qc.x(1) + qc.measure(0, 0) + qc.measure(1, 1) + + durations = InstructionDurations( + [("x", [0], 200), ("x", [1], 400), ("measure", None, 1000)] + ) + pm = PassManager(ALAPSchedule(durations)) + qc_alap = pm.run(qc) + + alap_expected = QuantumCircuit(2, 2) + alap_expected.delay(200, 0) + alap_expected.x(0) + alap_expected.x(1) + alap_expected.measure(0, 0) + alap_expected.measure(1, 1) + + self.assertEqual(qc_alap, alap_expected) + + pm = PassManager(ASAPSchedule(durations)) + qc_asap = pm.run(qc) + + asap_expected = QuantumCircuit(2, 2) + asap_expected.x(0) + asap_expected.x(1) + asap_expected.measure(0, 0) # immediately start after X gate + asap_expected.measure(1, 1) + asap_expected.delay(200, 0) + + self.assertEqual(qc_asap, asap_expected) + + def test_parallel_gate_different_length_with_barrier(self): + """Test circuit having two parallel instruction with different length with barrier. + + (input) + ┌───┐┌─┐ + q_0: ┤ X ├┤M├─── + ├───┤└╥┘┌─┐ + q_1: ┤ X ├─╫─┤M├ + └───┘ ║ └╥┘ + c: 2/══════╩══╩═ + 0 1 + + (expected, ALAP) + ┌────────────────┐┌───┐ ░ ┌─┐ + q_0: ┤ Delay(200[dt]) ├┤ X ├─░─┤M├─── + └─────┬───┬──────┘└───┘ ░ └╥┘┌─┐ + q_1: ──────┤ X ├─────────────░──╫─┤M├ + └───┘ ░ ║ └╥┘ + c: 2/═══════════════════════════╩══╩═ + 0 1 + + (expected, ASAP) + ┌───┐┌────────────────┐ ░ ┌─┐ + q_0: ┤ X ├┤ Delay(200[dt]) ├─░─┤M├─── + ├───┤└────────────────┘ ░ └╥┘┌─┐ + q_1: ┤ X ├───────────────────░──╫─┤M├ + └───┘ ░ ║ └╥┘ + c: 2/═══════════════════════════╩══╩═ + 0 1 + """ + qc = QuantumCircuit(2, 2) + qc.x(0) + qc.x(1) + qc.barrier() + qc.measure(0, 0) + qc.measure(1, 1) + + durations = InstructionDurations( + [("x", [0], 200), ("x", [1], 400), ("measure", None, 1000)] + ) + pm = PassManager(ALAPSchedule(durations)) + qc_alap = pm.run(qc) + + alap_expected = QuantumCircuit(2, 2) + alap_expected.delay(200, 0) + alap_expected.x(0) + alap_expected.x(1) + alap_expected.barrier() + alap_expected.measure(0, 0) + alap_expected.measure(1, 1) + + self.assertEqual(qc_alap, alap_expected) + + pm = PassManager(ASAPSchedule(durations)) + qc_asap = pm.run(qc) + + asap_expected = QuantumCircuit(2, 2) + asap_expected.x(0) + asap_expected.delay(200, 0) + asap_expected.x(1) + asap_expected.barrier() + asap_expected.measure(0, 0) + asap_expected.measure(1, 1) + + self.assertEqual(qc_asap, asap_expected) + + def test_measure_after_c_if_on_edge_locking(self): + """Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit. + + The scheduler is configured to reproduce behavior of the 0.20.0, + in which clbit lock is applied to the end-edge of measure instruction. + See https://github.com/Qiskit/qiskit-terra/pull/7655 + + (input) + ┌─┐ + q_0: ┤M├────────────── + └╥┘ ┌───┐ + q_1: ─╫────┤ X ├────── + ║ └─╥─┘ ┌─┐ + q_2: ─╫──────╫─────┤M├ + ║ ┌────╨────┐└╥┘ + c: 1/═╩═╡ c_0 = T ╞═╩═ + 0 └─────────┘ 0 + + (ASAP scheduled) + ┌─┐┌────────────────┐ + q_0: ───────────────────┤M├┤ Delay(200[dt]) ├───────────────────── + ┌─────────────────┐└╥┘└─────┬───┬──────┘ + q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├──────────────────────────── + └─────────────────┘ ║ └─╥─┘ ┌─┐┌────────────────┐ + q_2: ────────────────────╫─────────╫─────────┤M├┤ Delay(200[dt]) ├ + ║ ┌────╨────┐ └╥┘└────────────────┘ + c: 1/════════════════════╩════╡ c_0=0x1 ╞═════╩═══════════════════ + 0 └─────────┘ 0 + + (ALAP scheduled) + ┌─┐┌────────────────┐ + q_0: ───────────────────┤M├┤ Delay(200[dt]) ├─── + ┌─────────────────┐└╥┘└─────┬───┬──────┘ + q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├────────── + └┬────────────────┤ ║ └─╥─┘ ┌─┐ + q_2: ─┤ Delay(200[dt]) ├─╫─────────╫─────────┤M├ + └────────────────┘ ║ ┌────╨────┐ └╥┘ + c: 1/════════════════════╩════╡ c_0=0x1 ╞═════╩═ + 0 └─────────┘ 0 + + """ + qc = QuantumCircuit(3, 1) + qc.measure(0, 0) + qc.x(1).c_if(0, 1) + qc.measure(2, 0) + + durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)]) + + # lock at the end edge + actual_asap = PassManager(ASAPSchedule(durations, clbit_write_latency=1000)).run(qc) + actual_alap = PassManager(ALAPSchedule(durations, clbit_write_latency=1000)).run(qc) + + # start times of 2nd measure depends on ASAP/ALAP + expected_asap = QuantumCircuit(3, 1) + expected_asap.measure(0, 0) + expected_asap.delay(1000, 1) + expected_asap.x(1).c_if(0, 1) + expected_asap.measure(2, 0) + expected_asap.delay(200, 0) + expected_asap.delay(200, 2) + self.assertEqual(expected_asap, actual_asap) + + expected_alap = QuantumCircuit(3, 1) + expected_alap.measure(0, 0) + expected_alap.delay(1000, 1) + expected_alap.x(1).c_if(0, 1) + expected_alap.delay(200, 2) + expected_alap.measure(2, 0) + expected_alap.delay(200, 0) + self.assertEqual(expected_alap, actual_alap) + + @data([100, 200], [500, 0], [1000, 200]) + @unpack + def test_active_reset_circuit(self, write_lat, cond_lat): + """Test practical example of reset circuit. + + Because of the stimulus pulse overlap with the previous XGate on the q register, + measure instruction is always triggered after XGate regardless of write latency. + Thus only conditional latency matters in the scheduling. + + (input) + ┌─┐ ┌───┐ ┌─┐ ┌───┐ ┌─┐ ┌───┐ + q: ┤M├───┤ X ├───┤M├───┤ X ├───┤M├───┤ X ├─── + └╥┘ └─╥─┘ └╥┘ └─╥─┘ └╥┘ └─╥─┘ + ║ ┌────╨────┐ ║ ┌────╨────┐ ║ ┌────╨────┐ + c: 1/═╩═╡ c_0=0x1 ╞═╩═╡ c_0=0x1 ╞═╩═╡ c_0=0x1 ╞ + 0 └─────────┘ 0 └─────────┘ 0 └─────────┘ + + """ + qc = QuantumCircuit(1, 1) + qc.measure(0, 0) + qc.x(0).c_if(0, 1) + qc.measure(0, 0) + qc.x(0).c_if(0, 1) + qc.measure(0, 0) + qc.x(0).c_if(0, 1) + + durations = InstructionDurations([("x", None, 100), ("measure", None, 1000)]) + actual_asap = PassManager( + ASAPSchedule(durations, clbit_write_latency=write_lat, conditional_latency=cond_lat) + ).run(qc) + actual_alap = PassManager( + ALAPSchedule(durations, clbit_write_latency=write_lat, conditional_latency=cond_lat) + ).run(qc) + + expected = QuantumCircuit(1, 1) + expected.measure(0, 0) + if cond_lat > 0: + expected.delay(cond_lat, 0) + expected.x(0).c_if(0, 1) + expected.measure(0, 0) + if cond_lat > 0: + expected.delay(cond_lat, 0) + expected.x(0).c_if(0, 1) + expected.measure(0, 0) + if cond_lat > 0: + expected.delay(cond_lat, 0) + expected.x(0).c_if(0, 1) + + self.assertEqual(expected, actual_asap) + self.assertEqual(expected, actual_alap) + + def test_random_complicated_circuit(self): + """Test scheduling complicated circuit with control flow. + + (input) + ┌────────────────┐ ┌───┐ ░ ┌───┐ » + q_0: ┤ Delay(100[dt]) ├───┤ X ├────░──────────────────┤ X ├───» + └────────────────┘ └─╥─┘ ░ ┌───┐ └─╥─┘ » + q_1: ───────────────────────╫──────░───────┤ X ├────────╫─────» + ║ ░ ┌─┐ └─╥─┘ ║ » + q_2: ───────────────────────╫──────░─┤M├─────╫──────────╫─────» + ┌────╨────┐ ░ └╥┘┌────╨────┐┌────╨────┐» + c: 1/══════════════════╡ c_0=0x1 ╞════╩═╡ c_0=0x0 ╞╡ c_0=0x0 ╞» + └─────────┘ 0 └─────────┘└─────────┘» + « ┌────────────────┐┌───┐ + «q_0: ┤ Delay(300[dt]) ├┤ X ├─────■───── + « └────────────────┘└───┘ ┌─┴─┐ + «q_1: ────────■─────────────────┤ X ├─── + « ┌─┴─┐ ┌─┐ └─╥─┘ + «q_2: ──────┤ X ├────────┤M├──────╫───── + « └───┘ └╥┘ ┌────╨────┐ + «c: 1/════════════════════╩══╡ c_0=0x0 ╞ + « 0 └─────────┘ + + (ASAP scheduled) duration = 2800 dt + ┌────────────────┐┌────────────────┐ ┌───┐ ░ ┌─────────────────┐» + q_0: ┤ Delay(100[dt]) ├┤ Delay(100[dt]) ├───┤ X ├────░─┤ Delay(1400[dt]) ├» + ├────────────────┤└────────────────┘ └─╥─┘ ░ ├─────────────────┤» + q_1: ┤ Delay(300[dt]) ├───────────────────────╫──────░─┤ Delay(1200[dt]) ├» + ├────────────────┤ ║ ░ └───────┬─┬───────┘» + q_2: ┤ Delay(300[dt]) ├───────────────────────╫──────░─────────┤M├────────» + └────────────────┘ ┌────╨────┐ ░ └╥┘ » + c: 1/════════════════════════════════════╡ c_0=0x1 ╞════════════╩═════════» + └─────────┘ 0 » + « ┌───┐ ┌────────────────┐» + «q_0: ────────────────────────────────┤ X ├───┤ Delay(300[dt]) ├» + « ┌───┐ └─╥─┘ └────────────────┘» + «q_1: ───┤ X ├──────────────────────────╫─────────────■─────────» + « └─╥─┘ ┌────────────────┐ ║ ┌─┴─┐ » + «q_2: ─────╫─────┤ Delay(300[dt]) ├─────╫───────────┤ X ├───────» + « ┌────╨────┐└────────────────┘┌────╨────┐ └───┘ » + «c: 1/╡ c_0=0x0 ╞══════════════════╡ c_0=0x0 ╞══════════════════» + « └─────────┘ └─────────┘ » + « ┌───┐ ┌────────────────┐ + «q_0: ──────┤ X ├────────────■─────┤ Delay(700[dt]) ├ + « ┌─────┴───┴──────┐ ┌─┴─┐ ├────────────────┤ + «q_1: ┤ Delay(400[dt]) ├───┤ X ├───┤ Delay(700[dt]) ├ + « ├────────────────┤ └─╥─┘ └──────┬─┬───────┘ + «q_2: ┤ Delay(300[dt]) ├─────╫────────────┤M├──────── + « └────────────────┘┌────╨────┐ └╥┘ + «c: 1/══════════════════╡ c_0=0x0 ╞════════╩═════════ + « └─────────┘ 0 + + (ALAP scheduled) duration = 3100 + ┌────────────────┐┌────────────────┐ ┌───┐ ░ ┌─────────────────┐» + q_0: ┤ Delay(100[dt]) ├┤ Delay(100[dt]) ├───┤ X ├────░─┤ Delay(1400[dt]) ├» + ├────────────────┤└────────────────┘ └─╥─┘ ░ ├─────────────────┤» + q_1: ┤ Delay(300[dt]) ├───────────────────────╫──────░─┤ Delay(1200[dt]) ├» + ├────────────────┤ ║ ░ └───────┬─┬───────┘» + q_2: ┤ Delay(300[dt]) ├───────────────────────╫──────░─────────┤M├────────» + └────────────────┘ ┌────╨────┐ ░ └╥┘ » + c: 1/════════════════════════════════════╡ c_0=0x1 ╞════════════╩═════════» + └─────────┘ 0 » + « ┌───┐ ┌────────────────┐» + «q_0: ────────────────────────────────┤ X ├───┤ Delay(300[dt]) ├» + « ┌───┐ ┌────────────────┐ └─╥─┘ └────────────────┘» + «q_1: ───┤ X ├───┤ Delay(300[dt]) ├─────╫─────────────■─────────» + « └─╥─┘ ├────────────────┤ ║ ┌─┴─┐ » + «q_2: ─────╫─────┤ Delay(600[dt]) ├─────╫───────────┤ X ├───────» + « ┌────╨────┐└────────────────┘┌────╨────┐ └───┘ » + «c: 1/╡ c_0=0x0 ╞══════════════════╡ c_0=0x0 ╞══════════════════» + « └─────────┘ └─────────┘ » + « ┌───┐ ┌────────────────┐ + «q_0: ──────┤ X ├────────────■─────┤ Delay(700[dt]) ├ + « ┌─────┴───┴──────┐ ┌─┴─┐ ├────────────────┤ + «q_1: ┤ Delay(100[dt]) ├───┤ X ├───┤ Delay(700[dt]) ├ + « └──────┬─┬───────┘ └─╥─┘ └────────────────┘ + «q_2: ───────┤M├─────────────╫─────────────────────── + « └╥┘ ┌────╨────┐ + «c: 1/════════╩═════════╡ c_0=0x0 ╞══════════════════ + « 0 └─────────┘ + + """ + qc = QuantumCircuit(3, 1) + qc.delay(100, 0) + qc.x(0).c_if(0, 1) + qc.barrier() + qc.measure(2, 0) + qc.x(1).c_if(0, 0) + qc.x(0).c_if(0, 0) + qc.delay(300, 0) + qc.cx(1, 2) + qc.x(0) + qc.cx(0, 1).c_if(0, 0) + qc.measure(2, 0) + + durations = InstructionDurations( + [("x", None, 100), ("measure", None, 1000), ("cx", None, 200)] + ) + + actual_asap = PassManager( + ASAPSchedule(durations, clbit_write_latency=100, conditional_latency=200) + ).run(qc) + actual_alap = PassManager( + ALAPSchedule(durations, clbit_write_latency=100, conditional_latency=200) + ).run(qc) + + expected_asap = QuantumCircuit(3, 1) + expected_asap.delay(100, 0) + expected_asap.delay(100, 0) # due to conditional latency of 200dt + expected_asap.delay(300, 1) + expected_asap.delay(300, 2) + expected_asap.x(0).c_if(0, 1) + expected_asap.barrier() + expected_asap.delay(1400, 0) + expected_asap.delay(1200, 1) + expected_asap.measure(2, 0) + expected_asap.x(1).c_if(0, 0) + expected_asap.x(0).c_if(0, 0) + expected_asap.delay(300, 0) + expected_asap.x(0) + expected_asap.delay(300, 2) + expected_asap.cx(1, 2) + expected_asap.delay(400, 1) + expected_asap.cx(0, 1).c_if(0, 0) + expected_asap.delay(700, 0) # creg is released at t0 of cx(0,1).c_if(0,0) + expected_asap.delay( + 700, 1 + ) # no creg write until 100dt. thus measure can move left by 300dt. + expected_asap.delay(300, 2) + expected_asap.measure(2, 0) + self.assertEqual(expected_asap, actual_asap) + self.assertEqual(actual_asap.duration, 3100) + + expected_alap = QuantumCircuit(3, 1) + expected_alap.delay(100, 0) + expected_alap.delay(100, 0) # due to conditional latency of 200dt + expected_alap.delay(300, 1) + expected_alap.delay(300, 2) + expected_alap.x(0).c_if(0, 1) + expected_alap.barrier() + expected_alap.delay(1400, 0) + expected_alap.delay(1200, 1) + expected_alap.measure(2, 0) + expected_alap.x(1).c_if(0, 0) + expected_alap.x(0).c_if(0, 0) + expected_alap.delay(300, 0) + expected_alap.x(0) + expected_alap.delay(300, 1) + expected_alap.delay(600, 2) + expected_alap.cx(1, 2) + expected_alap.delay(100, 1) + expected_alap.cx(0, 1).c_if(0, 0) + expected_alap.measure(2, 0) + expected_alap.delay(700, 0) + expected_alap.delay(700, 1) + self.assertEqual(expected_alap, actual_alap) + self.assertEqual(actual_alap.duration, 3100) + + def test_dag_introduces_extra_dependency_between_conditionals(self): + """Test dependency between conditional operations in the scheduling. + + In the below example circuit, the conditional x on q1 could start at time 0, + however it must be scheduled after the conditional x on q0 in ASAP scheduling. + That is because circuit model used in the transpiler passes (DAGCircuit) + interprets instructions acting on common clbits must be run in the order + given by the original circuit (QuantumCircuit). + + (input) + ┌────────────────┐ ┌───┐ + q_0: ┤ Delay(100[dt]) ├───┤ X ├─── + └─────┬───┬──────┘ └─╥─┘ + q_1: ──────┤ X ├────────────╫───── + └─╥─┘ ║ + ┌────╨────┐ ┌────╨────┐ + c: 1/═══╡ c_0=0x1 ╞════╡ c_0=0x1 ╞ + └─────────┘ └─────────┘ + + (ASAP scheduled) + ┌────────────────┐ ┌───┐ + q_0: ┤ Delay(100[dt]) ├───┤ X ├────────────── + ├────────────────┤ └─╥─┘ ┌───┐ + q_1: ┤ Delay(100[dt]) ├─────╫────────┤ X ├─── + └────────────────┘ ║ └─╥─┘ + ┌────╨────┐┌────╨────┐ + c: 1/══════════════════╡ c_0=0x1 ╞╡ c_0=0x1 ╞ + └─────────┘└─────────┘ + """ + qc = QuantumCircuit(2, 1) + qc.delay(100, 0) + qc.x(0).c_if(0, True) + qc.x(1).c_if(0, True) + + durations = InstructionDurations([("x", None, 160)]) + pm = PassManager(ASAPSchedule(durations)) + scheduled = pm.run(qc) + + expected = QuantumCircuit(2, 1) + expected.delay(100, 0) + expected.delay(100, 1) # due to extra dependency on clbits + expected.x(0).c_if(0, True) + expected.x(1).c_if(0, True) + + self.assertEqual(expected, scheduled) + + @data(ALAPSchedule, ASAPSchedule) + def test_respect_target_instruction_constraints(self, schedule_pass): + """Test if ALAP/ASAP does not pad delays for qubits that do not support delay instructions. + See: https://github.com/Qiskit/qiskit-terra/issues/9993 + """ + target = Target(dt=1) + target.add_instruction(XGate(), {(1,): InstructionProperties(duration=200)}) + # delays are not supported + + qc = QuantumCircuit(2) + qc.x(1) + + pm = PassManager(schedule_pass(target=target)) + scheduled = pm.run(qc) + + expected = QuantumCircuit(2) + expected.x(1) + # no delay on qubit 0 + + self.assertEqual(expected, scheduled) + + def test_dd_respect_target_instruction_constraints(self): + """Test if DD pass does not pad delays for qubits that do not support delay instructions + and does not insert DD gates for qubits that do not support necessary gates. + See: https://github.com/Qiskit/qiskit-terra/issues/9993 + """ + qc = QuantumCircuit(3) + qc.cx(0, 1) + qc.cx(1, 2) + + target = Target(dt=1) + # Y is partially supported (not supported on qubit 2) + target.add_instruction( + XGate(), {(q,): InstructionProperties(duration=100) for q in range(2)} + ) + target.add_instruction( + CXGate(), + { + (0, 1): InstructionProperties(duration=1000), + (1, 2): InstructionProperties(duration=1000), + }, + ) + # delays are not supported + + # No DD instructions nor delays are padded due to no delay support in the target + pm_xx = PassManager( + [ + ALAPSchedule(target=target), + DynamicalDecoupling(durations=None, dd_sequence=[XGate(), XGate()], target=target), + ] + ) + scheduled = pm_xx.run(qc) + self.assertEqual(qc, scheduled) + + # Fails since Y is not supported in the target + with self.assertRaises(TranspilerError): + PassManager( + [ + ALAPSchedule(target=target), + DynamicalDecoupling( + durations=None, + dd_sequence=[XGate(), YGate(), XGate(), YGate()], + target=target, + ), + ] + ) + + # Add delay support to the target + target.add_instruction(Delay(Parameter("t")), {(q,): None for q in range(3)}) + # No error but no DD on qubit 2 (just delay is padded) since X is not supported on it + scheduled = pm_xx.run(qc) + + expected = QuantumCircuit(3) + expected.delay(1000, [2]) + expected.cx(0, 1) + expected.cx(1, 2) + expected.delay(200, [0]) + expected.x([0]) + expected.delay(400, [0]) + expected.x([0]) + expected.delay(200, [0]) + self.assertEqual(expected, scheduled) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/transpiler/test_calibrationbuilder.py b/test/python/transpiler/test_calibrationbuilder.py index 45ada1789a88..34c8414fb168 100644 --- a/test/python/transpiler/test_calibrationbuilder.py +++ b/test/python/transpiler/test_calibrationbuilder.py @@ -20,7 +20,7 @@ from qiskit import circuit, schedule, QiskitError, QuantumCircuit from qiskit.circuit import Parameter -from qiskit.circuit.library.standard_gates import SXGate, RZGate, RXGate +from qiskit.circuit.library.standard_gates import SXGate, RXGate from qiskit.providers.fake_provider import FakeHanoi # TODO - include FakeHanoiV2, FakeSherbrooke from qiskit.providers.fake_provider import FakeArmonk from qiskit.providers.fake_provider import FakeBelemV2 @@ -118,12 +118,13 @@ def build_forward( """A helper function to generate reference pulse schedule for forward direction.""" duration = self.compute_stretch_duration(u0p_play, theta) width = self.compute_stretch_width(u0p_play, theta) + inst_sched_map = backend.defaults().instruction_schedule_map with builder.build( backend, default_alignment="sequential", - default_transpiler_settings={"optimization_level": 0}, ) as ref_sched: + with builder.align_left(): # Positive CRs u0p_params = u0p_play.pulse.parameters @@ -140,7 +141,9 @@ def build_forward( GaussianSquare(**d1p_params), DriveChannel(1), ) - builder.x(0) + # Get Schedule for 'x' gate from the backend. + builder.call(inst_sched_map._get_calibration_entry("x", (0,)).get_schedule()) + with builder.align_left(): # Negative CRs u0m_params = u0m_play.pulse.parameters @@ -157,7 +160,9 @@ def build_forward( GaussianSquare(**d1m_params), DriveChannel(1), ) - builder.x(0) + # Get Schedule for 'x' gate from the backend. + builder.call(inst_sched_map._get_calibration_entry("x", (0,)).get_schedule()) + return ref_sched def build_reverse( @@ -172,19 +177,36 @@ def build_reverse( """A helper function to generate reference pulse schedule for backward direction.""" duration = self.compute_stretch_duration(u0p_play, theta) width = self.compute_stretch_width(u0p_play, theta) + inst_sched_map = backend.defaults().instruction_schedule_map + + rz_qc_q0 = QuantumCircuit(1) + rz_qc_q0.rz(pi / 2, 0) + + rz_qc_q1 = QuantumCircuit(2) + rz_qc_q1.rz(pi / 2, 1) + + rz_sched_q0 = schedule(rz_qc_q0, backend) + rz_sched_q1 = schedule(rz_qc_q1, backend) with builder.build( backend, default_alignment="sequential", - default_transpiler_settings={"optimization_level": 0}, ) as ref_sched: - # Hadamard gates - builder.call_gate(RZGate(np.pi / 2), qubits=(0,)) - builder.call_gate(SXGate(), qubits=(0,)) - builder.call_gate(RZGate(np.pi / 2), qubits=(0,)) - builder.call_gate(RZGate(np.pi / 2), qubits=(1,)) - builder.call_gate(SXGate(), qubits=(1,)) - builder.call_gate(RZGate(np.pi / 2), qubits=(1,)) + + # Get Schedule from the backend for Gates equivalent to Hadamard gates. + with builder.align_left(): + builder.call(rz_sched_q0) + builder.call( + inst_sched_map._get_calibration_entry(SXGate(), qubits=(0,)).get_schedule() + ) + builder.call(rz_sched_q0) + + builder.call(rz_sched_q1) + builder.call( + inst_sched_map._get_calibration_entry(SXGate(), qubits=(1,)).get_schedule() + ) + builder.call(rz_sched_q1) + with builder.align_left(): # Positive CRs u0p_params = u0p_play.pulse.parameters @@ -201,7 +223,10 @@ def build_reverse( GaussianSquare(**d1p_params), DriveChannel(1), ) - builder.x(0) + + # Get Schedule for 'x' gate from the backend. + builder.call(inst_sched_map._get_calibration_entry("x", (0,)).get_schedule()) + with builder.align_left(): # Negative CRs u0m_params = u0m_play.pulse.parameters @@ -218,14 +243,23 @@ def build_reverse( GaussianSquare(**d1m_params), DriveChannel(1), ) - builder.x(0) - # Hadamard gates - builder.call_gate(RZGate(np.pi / 2), qubits=(0,)) - builder.call_gate(SXGate(), qubits=(0,)) - builder.call_gate(RZGate(np.pi / 2), qubits=(0,)) - builder.call_gate(RZGate(np.pi / 2), qubits=(1,)) - builder.call_gate(SXGate(), qubits=(1,)) - builder.call_gate(RZGate(np.pi / 2), qubits=(1,)) + + # Get Schedule for 'x' gate from the backend. + builder.call(inst_sched_map._get_calibration_entry("x", (0,)).get_schedule()) + + # Get Schedule from the backend for Gates equivalent to Hadamard gates. + with builder.align_left(): + builder.call(rz_sched_q0) + builder.call( + inst_sched_map._get_calibration_entry(SXGate(), qubits=(0,)).get_schedule() + ) + builder.call(rz_sched_q0) + + builder.call(rz_sched_q1) + builder.call( + inst_sched_map._get_calibration_entry(SXGate(), qubits=(1,)).get_schedule() + ) + builder.call(rz_sched_q1) return ref_sched diff --git a/test/python/transpiler/test_dynamical_decoupling.py b/test/python/transpiler/test_dynamical_decoupling.py index b74091dd76f8..e53fb9b0226e 100644 --- a/test/python/transpiler/test_dynamical_decoupling.py +++ b/test/python/transpiler/test_dynamical_decoupling.py @@ -1031,6 +1031,25 @@ def test_respect_target_instruction_constraints(self): expected.delay(200, [0]) self.assertEqual(expected, scheduled) + def test_paramaterized_global_phase(self): + """Test paramaterized global phase in DD circuit. + See:https://github.com/Qiskit/qiskit-terra/issues/10569 + """ + dd_sequence = [XGate(), YGate()] * 2 + qc = QuantumCircuit(1, 1) + qc.h(0) + qc.delay(1700, 0) + qc.y(0) + qc.global_phase = Parameter("a") + pm = PassManager( + [ + ALAPScheduleAnalysis(self.durations), + PadDynamicalDecoupling(self.durations, dd_sequence), + ] + ) + + self.assertEqual(qc.global_phase + np.pi, pm.run(qc).global_phase) + if __name__ == "__main__": unittest.main() diff --git a/test/python/transpiler/test_filter_op_nodes.py b/test/python/transpiler/test_filter_op_nodes.py new file mode 100644 index 000000000000..0084dd35d4c0 --- /dev/null +++ b/test/python/transpiler/test_filter_op_nodes.py @@ -0,0 +1,78 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023 +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""FilterOpNodes pass testing""" + + +from qiskit import QuantumCircuit +from qiskit.transpiler.passes import FilterOpNodes +from qiskit.test import QiskitTestCase + + +class TestFilterOpNodes(QiskitTestCase): + """Tests for FilterOpNodes transformation pass.""" + + def test_empty_circuit(self): + """Empty DAG has does nothing.""" + circuit = QuantumCircuit() + self.assertEqual(FilterOpNodes(lambda x: False)(circuit), circuit) + + def test_remove_x_gate(self): + """Test filter removes matching gates.""" + circuit = QuantumCircuit(2) + circuit.x(0) + circuit.x(1) + circuit.cx(0, 1) + circuit.cx(1, 0) + circuit.cx(0, 1) + circuit.measure_all() + + filter_pass = FilterOpNodes(lambda node: node.op.name != "x") + + expected = QuantumCircuit(2) + expected.cx(0, 1) + expected.cx(1, 0) + expected.cx(0, 1) + expected.measure_all() + + self.assertEqual(filter_pass(circuit), expected) + + def test_filter_exception(self): + """Test a filter function exception passes through.""" + circuit = QuantumCircuit(2) + circuit.x(0) + circuit.x(1) + circuit.cx(0, 1) + circuit.cx(1, 0) + circuit.cx(0, 1) + circuit.measure_all() + + def filter_fn(node): + raise TypeError("Failure") + + filter_pass = FilterOpNodes(filter_fn) + with self.assertRaises(TypeError): + filter_pass(circuit) + + def test_no_matches(self): + """Test the pass does nothing if there are no filter matches.""" + circuit = QuantumCircuit(2) + circuit.x(0) + circuit.x(1) + circuit.cx(0, 1) + circuit.cx(1, 0) + circuit.cx(0, 1) + circuit.measure_all() + + filter_pass = FilterOpNodes(lambda node: node.op.name != "cz") + + self.assertEqual(filter_pass(circuit), circuit) diff --git a/test/python/transpiler/test_inverse_cancellation.py b/test/python/transpiler/test_inverse_cancellation.py index 686b01dd5d7c..c39b531c1b0f 100644 --- a/test/python/transpiler/test_inverse_cancellation.py +++ b/test/python/transpiler/test_inverse_cancellation.py @@ -22,7 +22,17 @@ from qiskit.transpiler.passes import InverseCancellation from qiskit.transpiler import PassManager from qiskit.test import QiskitTestCase -from qiskit.circuit.library import RXGate, HGate, CXGate, PhaseGate, XGate, TGate, TdgGate +from qiskit.circuit.library import ( + RXGate, + HGate, + CXGate, + PhaseGate, + XGate, + TGate, + TdgGate, + CZGate, + RZGate, +) class TestInverseCancellation(QiskitTestCase): @@ -271,6 +281,111 @@ def test_cx_do_not_wrongly_cancel(self): self.assertIn("cx", gates_after) self.assertEqual(gates_after["cx"], 2) + def test_no_gates_to_cancel(self): + """Test when there are no gates to cancel.""" + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.cx(1, 0) + inverse_pass = InverseCancellation([HGate()]) + new_circ = inverse_pass(qc) + self.assertEqual(qc, new_circ) + + def test_some_cancel_rules_to_cancel(self): + """Test when there are some gates to cancel.""" + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.cx(1, 0) + qc.h(0) + qc.h(0) + inverse_pass = InverseCancellation([HGate(), CXGate(), CZGate()]) + new_circ = inverse_pass(qc) + self.assertNotIn("h", new_circ.count_ops()) + + def test_no_inverse_pairs(self): + """Test when there are no inverse pairs to cancel.""" + qc = QuantumCircuit(1) + qc.s(0) + qc.sdg(0) + inverse_pass = InverseCancellation([(TGate(), TdgGate())]) + new_circ = inverse_pass(qc) + self.assertEqual(qc, new_circ) + + def test_some_inverse_pairs(self): + """Test when there are some but not all inverse pairs to cancel.""" + qc = QuantumCircuit(1) + qc.s(0) + qc.sdg(0) + qc.t(0) + qc.tdg(0) + inverse_pass = InverseCancellation([(TGate(), TdgGate())]) + new_circ = inverse_pass(qc) + self.assertNotIn("t", new_circ.count_ops()) + self.assertNotIn("tdg", new_circ.count_ops()) + + def test_some_inverse_and_cancelled(self): + """Test when there are some but not all pairs to cancel.""" + qc = QuantumCircuit(2) + qc.s(0) + qc.sdg(0) + qc.t(0) + qc.tdg(0) + qc.cx(0, 1) + qc.cx(1, 0) + qc.h(0) + qc.h(0) + inverse_pass = InverseCancellation([HGate(), CXGate(), CZGate(), (TGate(), TdgGate())]) + new_circ = inverse_pass(qc) + self.assertNotIn("h", new_circ.count_ops()) + self.assertNotIn("t", new_circ.count_ops()) + self.assertNotIn("tdg", new_circ.count_ops()) + + def test_half_of_an_inverse_pair(self): + """Test that half of an inverse pair doesn't do anything.""" + qc = QuantumCircuit(1) + qc.t(0) + qc.t(0) + qc.t(0) + inverse_pass = InverseCancellation([(TGate(), TdgGate())]) + new_circ = inverse_pass(qc) + self.assertEqual(new_circ, qc) + + def test_parameterized_self_inverse(self): + """Test that a parameterized self inverse gate cancels correctly.""" + qc = QuantumCircuit(1) + qc.rz(0, 0) + qc.rz(0, 0) + inverse_pass = InverseCancellation([RZGate(0)]) + new_circ = inverse_pass(qc) + self.assertEqual(new_circ, QuantumCircuit(1)) + + def test_parameterized_self_inverse_not_equal_parameter(self): + """Test that a parameterized self inverse gate doesn't cancel incorrectly.""" + qc = QuantumCircuit(1) + qc.rz(0, 0) + qc.rz(3.14159, 0) + qc.rz(0, 0) + inverse_pass = InverseCancellation([RZGate(0)]) + new_circ = inverse_pass(qc) + self.assertEqual(new_circ, qc) + + def test_controlled_gate_open_control_does_not_cancel(self): + """Test that a controlled gate with an open control doesn't cancel.""" + qc = QuantumCircuit(2) + qc.cx(0, 1) + qc.cx(0, 1, ctrl_state=0) + inverse_pass = InverseCancellation([CXGate()]) + new_circ = inverse_pass(qc) + self.assertEqual(new_circ, qc) + + def test_backwards_pair(self): + """Test a backwards inverse pair works.""" + qc = QuantumCircuit(1) + qc.tdg(0) + qc.t(0) + inverse_pass = InverseCancellation([(TGate(), TdgGate())]) + new_circ = inverse_pass(qc) + self.assertEqual(new_circ, QuantumCircuit(1)) + if __name__ == "__main__": unittest.main() diff --git a/test/python/transpiler/test_optimize_1q_decomposition.py b/test/python/transpiler/test_optimize_1q_decomposition.py index 5d3ebb3d7245..72dbae0d3115 100644 --- a/test/python/transpiler/test_optimize_1q_decomposition.py +++ b/test/python/transpiler/test_optimize_1q_decomposition.py @@ -745,6 +745,20 @@ def test_nested_control_flow(self): result = passmanager.run(test) self.assertEqual(result, expected) + def test_prefer_no_substitution_if_all_ideal(self): + """Test that gates are not substituted if all our ideal gates in basis.""" + target = Target(num_qubits=1) + target.add_instruction(HGate(), {(0,): InstructionProperties(error=0)}) + target.add_instruction( + UGate(Parameter("a"), Parameter("b"), Parameter("c")), + {(0,): InstructionProperties(error=0)}, + ) + qc = QuantumCircuit(1) + qc.h(0) + opt_pass = Optimize1qGatesDecomposition(target) + res = opt_pass(qc) + self.assertEqual(res, qc) + if __name__ == "__main__": unittest.main() diff --git a/test/python/transpiler/test_sabre_layout.py b/test/python/transpiler/test_sabre_layout.py index abc48f63ae68..ac0db40a8541 100644 --- a/test/python/transpiler/test_sabre_layout.py +++ b/test/python/transpiler/test_sabre_layout.py @@ -423,13 +423,15 @@ def test_starting_layout(self): def test_integration_with_pass_manager(self): """Tests SabrePreLayoutIntegration with the rest of PassManager pipeline.""" backend = FakeAlmadenV2() - pm = generate_preset_pass_manager(1, backend, seed_transpiler=0) + pm = generate_preset_pass_manager( + 0, backend, layout_method="sabre", routing_method="sabre", seed_transpiler=0 + ) pm.pre_layout = PassManager([SabrePreLayout(backend.target)]) qct = pm.run(self.circuit) qct_initial_layout = qct.layout.initial_layout self.assertEqual( [qct_initial_layout[q] for q in self.circuit.qubits], - [1, 6, 5, 10, 11, 12, 16, 17, 18, 13, 14, 9, 8, 3, 2, 0], + [3, 8, 7, 12, 13, 14, 18, 17, 16, 11, 10, 5, 6, 1, 2, 4], ) diff --git a/test/python/transpiler/test_sabre_swap.py b/test/python/transpiler/test_sabre_swap.py index aaa84257d490..100e7882c782 100644 --- a/test/python/transpiler/test_sabre_swap.py +++ b/test/python/transpiler/test_sabre_swap.py @@ -129,6 +129,29 @@ def test_trivial_case(self): self.assertEqual(new_qc, qc) + def test_2q_barriers_not_routed(self): + """Test that a 2q barrier is not routed.""" + coupling = CouplingMap.from_line(5) + + qr = QuantumRegister(5, "q") + qc = QuantumCircuit(qr) + qc.barrier(0, 1) + qc.barrier(0, 2) + qc.barrier(0, 3) + qc.barrier(2, 3) + qc.h(0) + qc.barrier(1, 2) + qc.barrier(1, 0) + qc.barrier(1, 3) + qc.barrier(1, 4) + qc.barrier(4, 3) + qc.barrier(0, 4) + + passmanager = PassManager(SabreSwap(coupling, "basic")) + new_qc = passmanager.run(qc) + + self.assertEqual(new_qc, qc) + def test_trivial_with_target(self): """Test that an already mapped circuit is unchanged with target.""" coupling = CouplingMap.from_ring(5) diff --git a/test/python/transpiler/test_stage_plugin.py b/test/python/transpiler/test_stage_plugin.py index 1279fae467e8..d11fcb1c8a62 100644 --- a/test/python/transpiler/test_stage_plugin.py +++ b/test/python/transpiler/test_stage_plugin.py @@ -22,6 +22,7 @@ from qiskit.compiler.transpiler import transpile from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap +from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from qiskit.transpiler.preset_passmanagers.builtin_plugins import BasicSwapPassManager from qiskit.transpiler.preset_passmanagers.plugin import ( PassManagerStagePluginManager, @@ -113,3 +114,16 @@ def test_routing_plugins(self, optimization_level, routing_method): backend = QasmSimulatorPy() counts = backend.run(tqc, shots=1000).result().get_counts() self.assertDictAlmostEqual(counts, {"0000": 500, "1111": 500}, delta=100) + + @combine( + optimization_level=list(range(4)), + ) + def test_unitary_synthesis_plugins(self, optimization_level): + """Test unitary synthesis plugins""" + backend = QasmSimulatorPy() + with self.assertRaises(TranspilerError): + _ = generate_preset_pass_manager( + optimization_level=optimization_level, + backend=backend, + unitary_synthesis_method="not a method", + ) diff --git a/test/python/transpiler/test_unitary_synthesis.py b/test/python/transpiler/test_unitary_synthesis.py index d93b03934173..93302ca78fd1 100644 --- a/test/python/transpiler/test_unitary_synthesis.py +++ b/test/python/transpiler/test_unitary_synthesis.py @@ -344,25 +344,36 @@ def test_two_qubit_synthesis_not_pulse_optimal(self): backend = FakeVigo() conf = backend.configuration() qr = QuantumRegister(2) - coupling_map = CouplingMap([[0, 1], [1, 2], [1, 3], [3, 4]]) - triv_layout_pass = TrivialLayout(coupling_map) qc = QuantumCircuit(qr) qc.unitary(random_unitary(4, seed=12), [0, 1]) - unisynth_pass = UnitarySynthesis( - basis_gates=conf.basis_gates, - coupling_map=coupling_map, - backend_props=backend.properties(), - pulse_optimize=False, - natural_direction=True, + coupling_map = CouplingMap([[0, 1]]) + pm_nonoptimal = PassManager( + [ + TrivialLayout(coupling_map), + UnitarySynthesis( + basis_gates=conf.basis_gates, + coupling_map=coupling_map, + backend_props=backend.properties(), + pulse_optimize=False, + natural_direction=True, + ), + ] ) - pm = PassManager([triv_layout_pass, unisynth_pass]) - qc_out = pm.run(qc) - if isinstance(qc_out, QuantumCircuit): - num_ops = qc_out.count_ops() - else: - num_ops = qc_out[0].count_ops() - self.assertIn("sx", num_ops) - self.assertGreaterEqual(num_ops["sx"], 16) + pm_optimal = PassManager( + [ + TrivialLayout(coupling_map), + UnitarySynthesis( + basis_gates=conf.basis_gates, + coupling_map=coupling_map, + backend_props=backend.properties(), + pulse_optimize=True, + natural_direction=True, + ), + ] + ) + qc_nonoptimal = pm_nonoptimal.run(qc) + qc_optimal = pm_optimal.run(qc) + self.assertGreater(qc_nonoptimal.count_ops()["sx"], qc_optimal.count_ops()["sx"]) def test_two_qubit_pulse_optimal_true_raises(self): """Verify raises if pulse optimal==True but cx is not in the backend basis.""" diff --git a/test/python/transpiler/test_vf2_post_layout.py b/test/python/transpiler/test_vf2_post_layout.py index d06e46a229bd..da0dbb274c2b 100644 --- a/test/python/transpiler/test_vf2_post_layout.py +++ b/test/python/transpiler/test_vf2_post_layout.py @@ -108,7 +108,7 @@ def test_empty_circuit(self): vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], - VF2PostLayoutStopReason.NO_SOLUTION_FOUND, + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, ) def test_empty_circuit_v2(self): @@ -119,7 +119,7 @@ def test_empty_circuit_v2(self): vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], - VF2PostLayoutStopReason.NO_SOLUTION_FOUND, + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, ) def test_skip_3q_circuit(self): @@ -389,6 +389,46 @@ def test_target_some_error(self): # No layout selected because nothing will beat initial layout self.assertNotIn("post_layout", vf2_pass.property_set) + def test_trivial_layout_is_best(self): + """Test that vf2postlayout reports no better solution if the trivial layout is the best layout""" + n_qubits = 4 + trivial_target = Target() + trivial_target.add_instruction( + CXGate(), {(i, i + 1): InstructionProperties(error=0.001) for i in range(n_qubits - 1)} + ) + + circuit = QuantumCircuit(n_qubits) + circuit.cx(0, 1) + circuit.cx(1, 2) + + vf2_pass = VF2PostLayout(target=trivial_target, seed=self.seed, strict_direction=False) + dag = circuit_to_dag(circuit) + vf2_pass.run(dag) + self.assertEqual( + vf2_pass.property_set["VF2PostLayout_stop_reason"], + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, + ) + + def test_last_qubits_best(self): + """Test that vf2postlayout determines the best layout when the last qubits have least error""" + n_qubits = 4 + target_last_qubits_best = Target() + target_last_qubits_best.add_instruction( + CXGate(), + {(i, i + 1): InstructionProperties(error=10**-i) for i in range(n_qubits - 1)}, + ) + + circuit = QuantumCircuit(n_qubits) + circuit.cx(0, 1) + circuit.cx(1, 2) + + vf2_pass = VF2PostLayout( + target=target_last_qubits_best, seed=self.seed, strict_direction=False + ) + dag = circuit_to_dag(circuit) + vf2_pass.run(dag) + self.assertLayout(dag, target_last_qubits_best.build_coupling_map(), vf2_pass.property_set) + class TestVF2PostLayoutScoring(QiskitTestCase): """Test scoring heuristic function for VF2PostLayout.""" @@ -480,7 +520,7 @@ def test_empty_circuit(self): vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], - VF2PostLayoutStopReason.NO_SOLUTION_FOUND, + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, ) def test_empty_circuit_v2(self): @@ -491,7 +531,7 @@ def test_empty_circuit_v2(self): vf2_pass.run(circuit_to_dag(qc)) self.assertEqual( vf2_pass.property_set["VF2PostLayout_stop_reason"], - VF2PostLayoutStopReason.NO_SOLUTION_FOUND, + VF2PostLayoutStopReason.NO_BETTER_SOLUTION_FOUND, ) def test_skip_3q_circuit(self): diff --git a/test/python/utils/mitigation/__init__.py b/test/python/utils/mitigation/__init__.py deleted file mode 100644 index ce3f4ee22ee0..000000000000 --- a/test/python/utils/mitigation/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Qiskit mitigation utils unit tests.""" diff --git a/test/python/utils/mitigation/test_meas.py b/test/python/utils/mitigation/test_meas.py deleted file mode 100644 index a5105b5ba4d5..000000000000 --- a/test/python/utils/mitigation/test_meas.py +++ /dev/null @@ -1,709 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021, 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=invalid-name - -""" -Test of measurement calibration: -1) Preparation of the basis states, generating the calibration circuits -(without noise), computing the calibration matrices, -and validating that they equal -to the identity matrices -2) Generating ideal (equally distributed) results, computing -the calibration output (without noise), -and validating that it is equally distributed -3) Testing the the measurement calibration on a circuit -(without noise), verifying that it is close to the -expected (equally distributed) result -4) Testing the fitters on pre-generated data with noise -""" - -import unittest -import numpy as np - -import qiskit -from qiskit.test import QiskitTestCase -from qiskit.result.result import Result -from qiskit.utils.mitigation import ( - CompleteMeasFitter, - TensoredMeasFitter, - complete_meas_cal, - tensored_meas_cal, -) -from qiskit.utils.mitigation._filters import MeasurementFilter -from qiskit.utils.mitigation.circuits import count_keys - -from qiskit.utils import optionals - -if optionals.HAS_AER: - # pylint: disable=no-name-in-module - from qiskit_aer import AerSimulator - from qiskit_aer.noise import NoiseModel - from qiskit_aer.noise.errors.standard_errors import pauli_error - -# fixed seed for tests - for both simulator and transpiler -SEED = 42 - - -def convert_ndarray_to_list_in_data(data: np.ndarray): - """ - converts ndarray format into list format (keeps all the dicts in the array) - also convert inner ndarrays into lists (recursively) - Args: - data: ndarray containing dicts or ndarrays in it - - Returns: - list: same array, converted to list format (in order to save it as json) - - """ - new_data = [] - for item in data: - if isinstance(item, np.ndarray): - new_item = convert_ndarray_to_list_in_data(item) - elif isinstance(item, dict): - new_item = {} - for key, value in item.items(): - new_item[key] = value.tolist() - else: - new_item = item - new_data.append(new_item) - - return new_data - - -def meas_calib_circ_creation(): - """ - create measurement calibration circuits and a GHZ state circuit for the tests - - Returns: - QuantumCircuit: the measurement calibrations circuits - list[str]: the mitigation pattern - QuantumCircuit: ghz circuit with 5 qubits (3 are used) - - """ - qubit_list = [1, 2, 3] - total_number_of_qubit = 5 - meas_calibs, state_labels = complete_meas_cal(qubit_list=qubit_list, qr=total_number_of_qubit) - - # Choose 3 qubits - qubit_1 = qubit_list[0] - qubit_2 = qubit_list[1] - qubit_3 = qubit_list[2] - ghz = qiskit.QuantumCircuit(total_number_of_qubit, len(qubit_list)) - ghz.h(qubit_1) - ghz.cx(qubit_1, qubit_2) - ghz.cx(qubit_1, qubit_3) - for i in qubit_list: - ghz.measure(i, i - 1) - return meas_calibs, state_labels, ghz - - -def tensored_calib_circ_creation(): - """ - create tensored measurement calibration circuits and a GHZ state circuit for the tests - - Returns: - QuantumCircuit: the tensored measurement calibration circuit - list[list[int]]: the mitigation pattern - QuantumCircuit: ghz circuit with 5 qubits (3 are used) - - """ - mit_pattern = [[2], [4, 1]] - meas_layout = [2, 4, 1] - qr = qiskit.QuantumRegister(5) - # Generate the calibration circuits - meas_calibs, mit_pattern = tensored_meas_cal(mit_pattern, qr=qr) - - cr = qiskit.ClassicalRegister(3) - ghz_circ = qiskit.QuantumCircuit(qr, cr) - ghz_circ.h(mit_pattern[0][0]) - ghz_circ.cx(mit_pattern[0][0], mit_pattern[1][0]) - ghz_circ.cx(mit_pattern[0][0], mit_pattern[1][1]) - ghz_circ.measure(mit_pattern[0][0], cr[0]) - ghz_circ.measure(mit_pattern[1][0], cr[1]) - ghz_circ.measure(mit_pattern[1][1], cr[2]) - return meas_calibs, mit_pattern, ghz_circ, meas_layout - - -def meas_calibration_circ_execution(shots: int, seed: int): - """ - create measurement calibration circuits and simulate them with noise - Args: - shots (int): number of shots per simulation - seed (int): the seed to use in the simulations - - Returns: - list: list of Results of the measurement calibration simulations - list: list of all the possible states with this amount of qubits - dict: dictionary of results counts of GHZ circuit simulation with measurement errors - """ - # define the circuits - meas_calibs, state_labels, ghz = meas_calib_circ_creation() - - # define noise - prob = 0.2 - error_meas = pauli_error([("X", prob), ("I", 1 - prob)]) - noise_model = NoiseModel() - noise_model.add_all_qubit_quantum_error(error_meas, "measure") - - # run the circuits multiple times - backend = AerSimulator() - cal_results = qiskit.execute( - meas_calibs, backend=backend, shots=shots, noise_model=noise_model, seed_simulator=seed - ).result() - - ghz_results = ( - qiskit.execute( - ghz, backend=backend, shots=shots, noise_model=noise_model, seed_simulator=seed - ) - .result() - .get_counts() - ) - - return cal_results, state_labels, ghz_results - - -def tensored_calib_circ_execution(shots: int, seed: int): - """ - create tensored measurement calibration circuits and simulate them with noise - Args: - shots (int): number of shots per simulation - seed (int): the seed to use in the simulations - - Returns: - list: list of Results of the measurement calibration simulations - list: the mitigation pattern - dict: dictionary of results counts of GHZ circuit simulation with measurement errors - """ - # define the circuits - meas_calibs, mit_pattern, ghz_circ, meas_layout = tensored_calib_circ_creation() - # define noise - prob = 0.2 - error_meas = pauli_error([("X", prob), ("I", 1 - prob)]) - noise_model = NoiseModel() - noise_model.add_all_qubit_quantum_error(error_meas, "measure") - - # run the circuits multiple times - backend = AerSimulator() - cal_results = qiskit.execute( - meas_calibs, backend=backend, shots=shots, noise_model=noise_model, seed_simulator=seed - ).result() - - ghz_results = qiskit.execute( - ghz_circ, backend=backend, shots=shots, noise_model=noise_model, seed_simulator=seed - ).result() - - return cal_results, mit_pattern, ghz_results, meas_layout - - -@unittest.skipUnless(optionals.HAS_AER, "Qiskit aer is required to run these tests") -class TestMeasCal(QiskitTestCase): - """The test class.""" - - def setUp(self): - super().setUp() - self.nq_list = [1, 2, 3, 4, 5] # Test up to 5 qubits - self.shots = 1024 # Number of shots (should be a power of 2) - - @staticmethod - def choose_calibration(nq, pattern_type): - """ - Generate a calibration circuit - - Args: - nq (int): number of qubits - pattern_type (int): a pattern in range(1, 2**nq) - - Returns: - qubits: a list of qubits according to the given pattern - weight: the weight of the pattern_type, - equals to the number of qubits - - Additional Information: - qr[i] exists if and only if the i-th bit in the binary - expression of - pattern_type equals 1 - """ - qubits = [] - weight = 0 - for i in range(nq): - pattern_bit = pattern_type & 1 - pattern_type = pattern_type >> 1 - if pattern_bit == 1: - qubits.append(i) - weight += 1 - return qubits, weight - - def generate_ideal_results(self, state_labels, weight): - """ - Generate ideal equally distributed results - - Args: - state_labels (list): a list of calibration state labels - weight (int): the number of qubits - - Returns: - results_dict: a dictionary of equally distributed results - results_list: a list of equally distributed results - - Additional Information: - for each state in state_labels: - result_dict[state] = #shots/len(state_labels) - """ - results_dict = {} - results_list = [0] * (2**weight) - state_num = len(state_labels) - for state in state_labels: - shots_per_state = self.shots / state_num - results_dict[state] = shots_per_state - # converting state (binary) to an integer - place = int(state, 2) - results_list[place] = shots_per_state - return results_dict, results_list - - def test_ideal_meas_cal(self): - """Test ideal execution, without noise.""" - for nq in self.nq_list: - for pattern_type in range(1, 2**nq): - - # Generate the quantum register according to the pattern - qubits, weight = self.choose_calibration(nq, pattern_type) - - with self.assertWarns(DeprecationWarning): - # Generate the calibration circuits - meas_calibs, state_labels = complete_meas_cal( - qubit_list=qubits, circlabel="test" - ) - - # Perform an ideal execution on the generated circuits - backend = AerSimulator() - job = qiskit.execute(meas_calibs, backend=backend, shots=self.shots) - cal_results = job.result() - - with self.assertWarns(DeprecationWarning): - # Make a calibration matrix - meas_cal = CompleteMeasFitter(cal_results, state_labels, circlabel="test") - - # Assert that the calibration matrix is equal to identity - IdentityMatrix = np.identity(2**weight) - self.assertListEqual( - meas_cal.cal_matrix.tolist(), - IdentityMatrix.tolist(), - "Error: the calibration matrix is not equal to identity", - ) - - # Assert that the readout fidelity is equal to 1 - self.assertEqual( - meas_cal.readout_fidelity(), - 1.0, - "Error: the average fidelity is not equal to 1", - ) - - # Generate ideal (equally distributed) results - results_dict, results_list = self.generate_ideal_results(state_labels, weight) - - with self.assertWarns(DeprecationWarning): - # Output the filter - meas_filter = meas_cal.filter - - # Apply the calibration matrix to results - # in list and dict forms using different methods - results_dict_1 = meas_filter.apply(results_dict, method="least_squares") - results_dict_0 = meas_filter.apply(results_dict, method="pseudo_inverse") - results_list_1 = meas_filter.apply(results_list, method="least_squares") - results_list_0 = meas_filter.apply(results_list, method="pseudo_inverse") - - # Assert that the results are equally distributed - self.assertListEqual(results_list, results_list_0.tolist()) - self.assertListEqual(results_list, np.round(results_list_1).tolist()) - self.assertDictEqual(results_dict, results_dict_0) - round_results = {} - for key, val in results_dict_1.items(): - round_results[key] = np.round(val) - self.assertDictEqual(results_dict, round_results) - - def test_meas_cal_on_circuit(self): - """Test an execution on a circuit.""" - # Generate the calibration circuits - with self.assertWarns(DeprecationWarning): - meas_calibs, state_labels, ghz = meas_calib_circ_creation() - - # Run the calibration circuits - backend = AerSimulator() - job = qiskit.execute( - meas_calibs, - backend=backend, - shots=self.shots, - seed_simulator=SEED, - seed_transpiler=SEED, - ) - cal_results = job.result() - - with self.assertWarns(DeprecationWarning): - # Make a calibration matrix - meas_cal = CompleteMeasFitter(cal_results, state_labels) - # Calculate the fidelity - fidelity = meas_cal.readout_fidelity() - - job = qiskit.execute( - [ghz], backend=backend, shots=self.shots, seed_simulator=SEED, seed_transpiler=SEED - ) - results = job.result() - - # Predicted equally distributed results - predicted_results = {"000": 0.5, "111": 0.5} - - with self.assertWarns(DeprecationWarning): - meas_filter = meas_cal.filter - - # Calculate the results after mitigation - output_results_pseudo_inverse = meas_filter.apply( - results, method="pseudo_inverse" - ).get_counts(0) - output_results_least_square = meas_filter.apply(results, method="least_squares").get_counts( - 0 - ) - - # Compare with expected fidelity and expected results - self.assertAlmostEqual(fidelity, 1.0) - self.assertAlmostEqual( - output_results_pseudo_inverse["000"] / self.shots, predicted_results["000"], places=1 - ) - - self.assertAlmostEqual( - output_results_least_square["000"] / self.shots, predicted_results["000"], places=1 - ) - - self.assertAlmostEqual( - output_results_pseudo_inverse["111"] / self.shots, predicted_results["111"], places=1 - ) - - self.assertAlmostEqual( - output_results_least_square["111"] / self.shots, predicted_results["111"], places=1 - ) - - def test_ideal_tensored_meas_cal(self): - """Test ideal execution, without noise.""" - - mit_pattern = [[1, 2], [3, 4, 5], [6]] - meas_layout = [1, 2, 3, 4, 5, 6] - - # Generate the calibration circuits - with self.assertWarns(DeprecationWarning): - meas_calibs, _ = tensored_meas_cal(mit_pattern=mit_pattern) - - # Perform an ideal execution on the generated circuits - backend = AerSimulator() - cal_results = qiskit.execute(meas_calibs, backend=backend, shots=self.shots).result() - - with self.assertWarns(DeprecationWarning): - # Make calibration matrices - meas_cal = TensoredMeasFitter(cal_results, mit_pattern=mit_pattern) - - # Assert that the calibration matrices are equal to identity - cal_matrices = meas_cal.cal_matrices - self.assertEqual( - len(mit_pattern), len(cal_matrices), "Wrong number of calibration matrices" - ) - for qubit_list, cal_mat in zip(mit_pattern, cal_matrices): - IdentityMatrix = np.identity(2 ** len(qubit_list)) - self.assertListEqual( - cal_mat.tolist(), - IdentityMatrix.tolist(), - "Error: the calibration matrix is not equal to identity", - ) - - # Assert that the readout fidelity is equal to 1 - self.assertEqual( - meas_cal.readout_fidelity(), - 1.0, - "Error: the average fidelity is not equal to 1", - ) - - with self.assertWarns(DeprecationWarning): - # Generate ideal (equally distributed) results - results_dict, _ = self.generate_ideal_results(count_keys(6), 6) - # Output the filter - meas_filter = meas_cal.filter - # Apply the calibration matrix to results - # in list and dict forms using different methods - results_dict_1 = meas_filter.apply( - results_dict, method="least_squares", meas_layout=meas_layout - ) - results_dict_0 = meas_filter.apply( - results_dict, method="pseudo_inverse", meas_layout=meas_layout - ) - - # Assert that the results are equally distributed - self.assertDictEqual(results_dict, results_dict_0) - round_results = {} - for key, val in results_dict_1.items(): - round_results[key] = np.round(val) - self.assertDictEqual(results_dict, round_results) - - def test_tensored_meas_cal_on_circuit(self): - """Test an execution on a circuit.""" - - with self.assertWarns(DeprecationWarning): - # Generate the calibration circuits - meas_calibs, mit_pattern, ghz, meas_layout = tensored_calib_circ_creation() - - # Run the calibration circuits - backend = AerSimulator() - cal_results = qiskit.execute( - meas_calibs, - backend=backend, - shots=self.shots, - seed_simulator=SEED, - seed_transpiler=SEED, - ).result() - - with self.assertWarns(DeprecationWarning): - # Make a calibration matrix - meas_cal = TensoredMeasFitter(cal_results, mit_pattern=mit_pattern) - # Calculate the fidelity - fidelity = meas_cal.readout_fidelity(0) * meas_cal.readout_fidelity(1) - - results = qiskit.execute( - [ghz], backend=backend, shots=self.shots, seed_simulator=SEED, seed_transpiler=SEED - ).result() - - # Predicted equally distributed results - predicted_results = {"000": 0.5, "111": 0.5} - - with self.assertWarns(DeprecationWarning): - meas_filter = meas_cal.filter - # Calculate the results after mitigation - output_results_pseudo_inverse = meas_filter.apply( - results, method="pseudo_inverse", meas_layout=meas_layout - ).get_counts(0) - output_results_least_square = meas_filter.apply( - results, method="least_squares", meas_layout=meas_layout - ).get_counts(0) - - # Compare with expected fidelity and expected results - self.assertAlmostEqual(fidelity, 1.0) - self.assertAlmostEqual( - output_results_pseudo_inverse["000"] / self.shots, predicted_results["000"], places=1 - ) - - self.assertAlmostEqual( - output_results_least_square["000"] / self.shots, predicted_results["000"], places=1 - ) - - self.assertAlmostEqual( - output_results_pseudo_inverse["111"] / self.shots, predicted_results["111"], places=1 - ) - - self.assertAlmostEqual( - output_results_least_square["111"] / self.shots, predicted_results["111"], places=1 - ) - - def test_meas_fitter_with_noise(self): - """Test the MeasurementFitter with noise.""" - tests = [] - runs = 3 - with self.assertWarns(DeprecationWarning): - for run in range(runs): - cal_results, state_labels, circuit_results = meas_calibration_circ_execution( - 1000, SEED + run - ) - - meas_cal = CompleteMeasFitter(cal_results, state_labels) - meas_filter = MeasurementFilter(meas_cal.cal_matrix, state_labels) - - # Calculate the results after mitigation - results_pseudo_inverse = meas_filter.apply(circuit_results, method="pseudo_inverse") - results_least_square = meas_filter.apply(circuit_results, method="least_squares") - tests.append( - { - "cal_matrix": convert_ndarray_to_list_in_data(meas_cal.cal_matrix), - "fidelity": meas_cal.readout_fidelity(), - "results": circuit_results, - "results_pseudo_inverse": results_pseudo_inverse, - "results_least_square": results_least_square, - } - ) - # Set the state labels - state_labels = ["000", "001", "010", "011", "100", "101", "110", "111"] - meas_cal = CompleteMeasFitter(None, state_labels, circlabel="test") - - for tst_index, _ in enumerate(tests): - # Set the calibration matrix - meas_cal.cal_matrix = tests[tst_index]["cal_matrix"] - # Calculate the fidelity - fidelity = meas_cal.readout_fidelity() - - meas_filter = MeasurementFilter(tests[tst_index]["cal_matrix"], state_labels) - - # Calculate the results after mitigation - output_results_pseudo_inverse = meas_filter.apply( - tests[tst_index]["results"], method="pseudo_inverse" - ) - output_results_least_square = meas_filter.apply( - tests[tst_index]["results"], method="least_squares" - ) - - # Compare with expected fidelity and expected results - self.assertAlmostEqual(fidelity, tests[tst_index]["fidelity"], places=0) - self.assertAlmostEqual( - output_results_pseudo_inverse["000"], - tests[tst_index]["results_pseudo_inverse"]["000"], - places=0, - ) - - self.assertAlmostEqual( - output_results_least_square["000"], - tests[tst_index]["results_least_square"]["000"], - places=0, - ) - - self.assertAlmostEqual( - output_results_pseudo_inverse["111"], - tests[tst_index]["results_pseudo_inverse"]["111"], - places=0, - ) - - self.assertAlmostEqual( - output_results_least_square["111"], - tests[tst_index]["results_least_square"]["111"], - places=0, - ) - - def test_tensored_meas_fitter_with_noise(self): - """Test the TensoredFitter with noise.""" - with self.assertWarns(DeprecationWarning): - cal_results, mit_pattern, circuit_results, meas_layout = tensored_calib_circ_execution( - 1000, SEED - ) - meas_cal = TensoredMeasFitter(cal_results, mit_pattern=mit_pattern) - meas_filter = meas_cal.filter - # Calculate the results after mitigation - results_pseudo_inverse = meas_filter.apply( - circuit_results.get_counts(), method="pseudo_inverse", meas_layout=meas_layout - ) - results_least_square = meas_filter.apply( - circuit_results.get_counts(), method="least_squares", meas_layout=meas_layout - ) - - saved_info = { - "cal_results": cal_results.to_dict(), - "results": circuit_results.to_dict(), - "mit_pattern": mit_pattern, - "meas_layout": meas_layout, - "fidelity": meas_cal.readout_fidelity(), - "results_pseudo_inverse": results_pseudo_inverse, - "results_least_square": results_least_square, - } - - saved_info["cal_results"] = Result.from_dict(saved_info["cal_results"]) - saved_info["results"] = Result.from_dict(saved_info["results"]) - - with self.assertWarns(DeprecationWarning): - meas_cal = TensoredMeasFitter( - saved_info["cal_results"], mit_pattern=saved_info["mit_pattern"] - ) - # Calculate the fidelity - fidelity = meas_cal.readout_fidelity(0) * meas_cal.readout_fidelity(1) - # Compare with expected fidelity and expected results - - self.assertAlmostEqual(fidelity, saved_info["fidelity"], places=0) - - with self.assertWarns(DeprecationWarning): - meas_filter = meas_cal.filter - # Calculate the results after mitigation - output_results_pseudo_inverse = meas_filter.apply( - saved_info["results"].get_counts(0), - method="pseudo_inverse", - meas_layout=saved_info["meas_layout"], - ) - output_results_least_square = meas_filter.apply( - saved_info["results"], method="least_squares", meas_layout=saved_info["meas_layout"] - ) - - self.assertAlmostEqual( - output_results_pseudo_inverse["000"], - saved_info["results_pseudo_inverse"]["000"], - places=0, - ) - - self.assertAlmostEqual( - output_results_least_square.get_counts(0)["000"], - saved_info["results_least_square"]["000"], - places=0, - ) - - self.assertAlmostEqual( - output_results_pseudo_inverse["111"], - saved_info["results_pseudo_inverse"]["111"], - places=0, - ) - - self.assertAlmostEqual( - output_results_least_square.get_counts(0)["111"], - saved_info["results_least_square"]["111"], - places=0, - ) - - substates_list = [] - with self.assertWarns(DeprecationWarning): - for qubit_list in saved_info["mit_pattern"]: - substates_list.append(count_keys(len(qubit_list))[::-1]) - fitter_other_order = TensoredMeasFitter( - saved_info["cal_results"], - substate_labels_list=substates_list, - mit_pattern=saved_info["mit_pattern"], - ) - - fidelity = fitter_other_order.readout_fidelity(0) * meas_cal.readout_fidelity(1) - - self.assertAlmostEqual(fidelity, saved_info["fidelity"], places=0) - - with self.assertWarns(DeprecationWarning): - meas_filter = fitter_other_order.filter - # Calculate the results after mitigation - output_results_pseudo_inverse = meas_filter.apply( - saved_info["results"].get_counts(0), - method="pseudo_inverse", - meas_layout=saved_info["meas_layout"], - ) - output_results_least_square = meas_filter.apply( - saved_info["results"], method="least_squares", meas_layout=saved_info["meas_layout"] - ) - - self.assertAlmostEqual( - output_results_pseudo_inverse["000"], - saved_info["results_pseudo_inverse"]["000"], - places=0, - ) - - self.assertAlmostEqual( - output_results_least_square.get_counts(0)["000"], - saved_info["results_least_square"]["000"], - places=0, - ) - - self.assertAlmostEqual( - output_results_pseudo_inverse["111"], - saved_info["results_pseudo_inverse"]["111"], - places=0, - ) - - self.assertAlmostEqual( - output_results_least_square.get_counts(0)["111"], - saved_info["results_least_square"]["111"], - places=0, - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/python/utils/test_deprecation.py b/test/python/utils/test_deprecation.py index ccfd226b56ca..56024d2e2e3a 100644 --- a/test/python/utils/test_deprecation.py +++ b/test/python/utils/test_deprecation.py @@ -72,7 +72,7 @@ def test_deprecate_func_docstring(self) -> None: f"""\ .. deprecated:: 9.99 - The function ``{__name__}._deprecated_func()`` is deprecated as of qiskit-terra \ + The function ``{__name__}._deprecated_func()`` is deprecated as of qiskit \ 9.99. It will be removed in 2 releases. Instead, use new_func(). """ ), @@ -83,7 +83,7 @@ def test_deprecate_func_docstring(self) -> None: f"""\ .. deprecated:: 9.99_pending - The class ``{__name__}._Foo`` is pending deprecation as of qiskit-terra 9.99. It \ + The class ``{__name__}._Foo`` is pending deprecation as of qiskit 9.99. It \ will be marked deprecated in a future release, and then removed no earlier than 3 months after \ the release date. """ @@ -96,7 +96,7 @@ def test_deprecate_func_docstring(self) -> None: Method. .. deprecated:: 9.99 - The method ``{__name__}._Foo.my_method()`` is deprecated as of qiskit-terra \ + The method ``{__name__}._Foo.my_method()`` is deprecated as of qiskit \ 9.99. It will be removed no earlier than 3 months after the release date. Stop using this! """ ), @@ -108,7 +108,7 @@ def test_deprecate_func_docstring(self) -> None: Property. .. deprecated:: 9.99 - The property ``{__name__}._Foo.my_property`` is deprecated as of qiskit-terra \ + The property ``{__name__}._Foo.my_property`` is deprecated as of qiskit \ 9.99. It will be removed no earlier than 3 months after the release date. """ ), @@ -143,22 +143,22 @@ def my_func() -> None: .. deprecated:: 9.99 ``{__name__}.{my_func.__qualname__}()``'s argument ``arg4`` is deprecated as of \ -qiskit-terra 9.99. It will be removed no earlier than 3 months after the release date. Instead, \ +qiskit 9.99. It will be removed no earlier than 3 months after the release date. Instead, \ use foo. .. deprecated:: 9.99 - Using the argument arg3 is deprecated as of qiskit-terra 9.99. It will be \ + Using the argument arg3 is deprecated as of qiskit 9.99. It will be \ removed no earlier than 3 months after the release date. Instead, use the argument ``new_arg3``, \ which behaves identically. .. deprecated:: 9.99_pending ``{__name__}.{my_func.__qualname__}()``'s argument ``arg2`` is pending \ -deprecation as of qiskit-terra 9.99. It will be marked deprecated in a future release, and then \ +deprecation as of qiskit 9.99. It will be marked deprecated in a future release, and then \ removed no earlier than 3 months after the release date. .. deprecated:: 9.99 ``{__name__}.{my_func.__qualname__}()``'s argument ``arg1`` is deprecated as of \ -qiskit-terra 9.99. It will be removed in 2 releases. +qiskit 9.99. It will be removed in 2 releases. """ ), ) diff --git a/test/python/visualization/pulse_v2/test_generators.py b/test/python/visualization/pulse_v2/test_generators.py index 9c9c5110f8bc..35f1ad16c6dc 100644 --- a/test/python/visualization/pulse_v2/test_generators.py +++ b/test/python/visualization/pulse_v2/test_generators.py @@ -83,7 +83,7 @@ def test_consecutive_index_tiny_diff(self): def test_parse_waveform(self): """Test helper function that parse waveform with Waveform instance.""" - test_pulse = pulse.library.gaussian(10, 0.1, 3) + test_pulse = pulse.library.Gaussian(10, 0.1, 3).get_waveform() inst = pulse.Play(test_pulse, pulse.DriveChannel(0)) inst_data = create_instruction(inst, 0, 0, 10, 0.1) diff --git a/test/python/visualization/references/dag_dep.png b/test/python/visualization/references/dag_dep.png new file mode 100644 index 000000000000..8faed6008357 Binary files /dev/null and b/test/python/visualization/references/dag_dep.png differ diff --git a/test/python/visualization/references/dag_no_reg.png b/test/python/visualization/references/dag_no_reg.png index 48f7324b5230..1b1860929506 100644 Binary files a/test/python/visualization/references/dag_no_reg.png and b/test/python/visualization/references/dag_no_reg.png differ diff --git a/test/python/visualization/references/pass_manager_standard.dot b/test/python/visualization/references/pass_manager_standard.dot index ae4233665a10..e1c5d3979ca2 100644 --- a/test/python/visualization/references/pass_manager_standard.dot +++ b/test/python/visualization/references/pass_manager_standard.dot @@ -61,27 +61,29 @@ fontname=helvetica; label="[6] do_while"; labeljust=l; 18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; +19 [color=black, fontname=helvetica, fontsize=10, label=label, shape=ellipse, style=dashed]; +19 -> 18; 14 -> 18; } -subgraph cluster_19 { +subgraph cluster_20 { fontname=helvetica; label="[7] "; labeljust=l; -20 [color=blue, fontname=helvetica, label=GateDirection, shape=rectangle]; -21 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; -21 -> 20; -22 [color=black, fontname=helvetica, fontsize=10, label=target, shape=ellipse, style=dashed]; -22 -> 20; -18 -> 20; +21 [color=blue, fontname=helvetica, label=GateDirection, shape=rectangle]; +22 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; +22 -> 21; +23 [color=black, fontname=helvetica, fontsize=10, label=target, shape=ellipse, style=dashed]; +23 -> 21; +18 -> 21; } -subgraph cluster_23 { +subgraph cluster_24 { fontname=helvetica; label="[8] "; labeljust=l; -24 [color=blue, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; -20 -> 24; +25 [color=blue, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; +21 -> 25; } } diff --git a/test/python/visualization/references/pass_manager_style.dot b/test/python/visualization/references/pass_manager_style.dot index 3c779cfde72b..0c80853d00be 100644 --- a/test/python/visualization/references/pass_manager_style.dot +++ b/test/python/visualization/references/pass_manager_style.dot @@ -61,27 +61,29 @@ fontname=helvetica; label="[6] do_while"; labeljust=l; 18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; +19 [color=black, fontname=helvetica, fontsize=10, label=label, shape=ellipse, style=dashed]; +19 -> 18; 14 -> 18; } -subgraph cluster_19 { +subgraph cluster_20 { fontname=helvetica; label="[7] "; labeljust=l; -20 [color=blue, fontname=helvetica, label=GateDirection, shape=rectangle]; -21 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; -21 -> 20; -22 [color=black, fontname=helvetica, fontsize=10, label=target, shape=ellipse, style=dashed]; -22 -> 20; -18 -> 20; +21 [color=blue, fontname=helvetica, label=GateDirection, shape=rectangle]; +22 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; +22 -> 21; +23 [color=black, fontname=helvetica, fontsize=10, label=target, shape=ellipse, style=dashed]; +23 -> 21; +18 -> 21; } -subgraph cluster_23 { +subgraph cluster_24 { fontname=helvetica; label="[8] "; labeljust=l; -24 [color=grey, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; -20 -> 24; +25 [color=grey, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; +21 -> 25; } } diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py index 1a38a689fd85..2db1cd26949e 100644 --- a/test/python/visualization/test_circuit_text_drawer.py +++ b/test/python/visualization/test_circuit_text_drawer.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""`_text_circuit_drawer` draws a circuit in ascii art""" +"""circuit_drawer with output="text" draws a circuit in ascii art""" import pathlib import os @@ -27,9 +27,9 @@ from qiskit.quantum_info.random import random_unitary from qiskit.test import QiskitTestCase from qiskit.transpiler.layout import Layout, TranspileLayout +from qiskit.visualization.circuit.circuit_visualization import _text_circuit_drawer from qiskit.visualization import circuit_drawer from qiskit.visualization.circuit import text as elements -from qiskit.visualization.circuit.circuit_visualization import _text_circuit_drawer from qiskit.providers.fake_provider import FakeBelemV2 from qiskit.circuit.classical import expr from qiskit.circuit.library import ( @@ -112,7 +112,7 @@ def test_text_empty(self): """The empty circuit.""" expected = "" circuit = QuantumCircuit() - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_pager(self): """The pager breaks the circuit when the drawing does not fit in the console.""" @@ -153,7 +153,9 @@ def test_text_pager(self): circuit.measure(qr[0], cr[0]) circuit.cx(qr[1], qr[0]) circuit.cx(qr[0], qr[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, fold=20)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, fold=20)), expected + ) def test_text_no_pager(self): """The pager can be disable.""" @@ -161,7 +163,9 @@ def test_text_no_pager(self): circuit = QuantumCircuit(qr) for _ in range(100): circuit.h(qr[0]) - amount_of_lines = str(_text_circuit_drawer(circuit, fold=-1)).count("\n") + amount_of_lines = str( + circuit_drawer(circuit, output="text", initial_state=True, fold=-1) + ).count("\n") self.assertEqual(amount_of_lines, 2) @@ -188,7 +192,10 @@ def test_text_measure_cregbundle(self): cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) circuit.measure(qr, cr) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_measure_cregbundle_2(self): """The measure operator, using 2 classical registers with cregbundle=True.""" @@ -212,7 +219,10 @@ def test_text_measure_cregbundle_2(self): circuit = QuantumCircuit(qr, cr_a, cr_b) circuit.measure(qr[0], cr_a[0]) circuit.measure(qr[1], cr_b[0]) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_measure_1(self): """The measure operator, using 3-bit-length registers.""" @@ -238,7 +248,10 @@ def test_text_measure_1(self): cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) circuit.measure(qr, cr) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_measure_1_reverse_bits(self): """The measure operator, using 3-bit-length registers, with reverse_bits""" @@ -260,7 +273,10 @@ def test_text_measure_1_reverse_bits(self): cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) circuit.measure(qr, cr) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_text_measure_2(self): """The measure operator, using some registers.""" @@ -288,7 +304,7 @@ def test_text_measure_2(self): cr2 = ClassicalRegister(2, "c2") circuit = QuantumCircuit(qr1, qr2, cr1, cr2) circuit.measure(qr2, cr2) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_measure_2_reverse_bits(self): """The measure operator, using some registers, with reverse_bits""" @@ -316,7 +332,10 @@ def test_text_measure_2_reverse_bits(self): cr2 = ClassicalRegister(2, "c2") circuit = QuantumCircuit(qr1, qr2, cr1, cr2) circuit.measure(qr2, cr2) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_wire_order(self): """Test the wire_order option""" @@ -356,8 +375,12 @@ def test_wire_order(self): circuit.x(3).c_if(cr, 10) self.assertEqual( str( - _text_circuit_drawer( - circuit, cregbundle=False, wire_order=[2, 1, 3, 0, 6, 8, 9, 5, 4, 7] + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=False, + wire_order=[2, 1, 3, 0, 6, 8, 9, 5, 4, 7], ) ), expected, @@ -383,7 +406,7 @@ def test_text_swap(self): qr2 = QuantumRegister(2, "q2") circuit = QuantumCircuit(qr1, qr2) circuit.swap(qr1, qr2) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_swap_reverse_bits(self): """Swap drawing with reverse_bits.""" @@ -405,7 +428,10 @@ def test_text_swap_reverse_bits(self): qr2 = QuantumRegister(2, "q2") circuit = QuantumCircuit(qr1, qr2) circuit.swap(qr1, qr2) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_text_reverse_bits_read_from_config(self): """Swap drawing with reverse_bits set in the configuration file.""" @@ -473,7 +499,7 @@ def test_text_cswap(self): circuit.cswap(qr[0], qr[1], qr[2]) circuit.cswap(qr[1], qr[0], qr[2]) circuit.cswap(qr[2], qr[1], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cswap_reverse_bits(self): """CSwap drawing with reverse_bits.""" @@ -494,7 +520,10 @@ def test_text_cswap_reverse_bits(self): circuit.cswap(qr[0], qr[1], qr[2]) circuit.cswap(qr[1], qr[0], qr[2]) circuit.cswap(qr[2], qr[1], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_text_cu3(self): """cu3 drawing.""" @@ -514,7 +543,7 @@ def test_text_cu3(self): circuit = QuantumCircuit(qr) circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]) circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[2], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cu3_reverse_bits(self): """cu3 drawing with reverse_bits""" @@ -534,7 +563,10 @@ def test_text_cu3_reverse_bits(self): circuit = QuantumCircuit(qr) circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[0], qr[1]]) circuit.append(CU3Gate(pi / 2, pi / 2, pi / 2), [qr[2], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_text_crz(self): """crz drawing.""" @@ -553,7 +585,7 @@ def test_text_crz(self): circuit = QuantumCircuit(qr) circuit.crz(pi / 2, qr[0], qr[1]) circuit.crz(pi / 2, qr[2], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cry(self): """cry drawing.""" @@ -572,7 +604,7 @@ def test_text_cry(self): circuit = QuantumCircuit(qr) circuit.cry(pi / 2, qr[0], qr[1]) circuit.cry(pi / 2, qr[2], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_crx(self): """crx drawing.""" @@ -591,7 +623,7 @@ def test_text_crx(self): circuit = QuantumCircuit(qr) circuit.crx(pi / 2, qr[0], qr[1]) circuit.crx(pi / 2, qr[2], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cx(self): """cx drawing.""" @@ -610,7 +642,7 @@ def test_text_cx(self): circuit = QuantumCircuit(qr) circuit.cx(qr[0], qr[1]) circuit.cx(qr[2], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cy(self): """cy drawing.""" @@ -629,7 +661,7 @@ def test_text_cy(self): circuit = QuantumCircuit(qr) circuit.cy(qr[0], qr[1]) circuit.cy(qr[2], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cz(self): """cz drawing.""" @@ -648,7 +680,7 @@ def test_text_cz(self): circuit = QuantumCircuit(qr) circuit.cz(qr[0], qr[1]) circuit.cz(qr[2], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_ch(self): """ch drawing.""" @@ -667,7 +699,7 @@ def test_text_ch(self): circuit = QuantumCircuit(qr) circuit.ch(qr[0], qr[1]) circuit.ch(qr[2], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_rzz(self): """rzz drawing. See #1957""" @@ -686,7 +718,7 @@ def test_text_rzz(self): circuit = QuantumCircuit(qr) circuit.rzz(0, qr[0], qr[1]) circuit.rzz(pi / 2, qr[2], qr[1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cu1(self): """cu1 drawing.""" @@ -705,7 +737,7 @@ def test_text_cu1(self): circuit = QuantumCircuit(qr) circuit.append(CU1Gate(pi / 2), [qr[0], qr[1]]) circuit.append(CU1Gate(pi / 2), [qr[2], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cp(self): """cp drawing.""" @@ -724,7 +756,7 @@ def test_text_cp(self): circuit = QuantumCircuit(qr) circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]) circuit.append(CPhaseGate(pi / 2), [qr[2], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_cu1_condition(self): """Test cu1 with condition""" @@ -745,7 +777,7 @@ def test_text_cu1_condition(self): cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) circuit.append(CU1Gate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) - self.assertEqual(str(_text_circuit_drawer(circuit, initial_state=False)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_rzz_condition(self): """Test rzz with condition""" @@ -766,7 +798,7 @@ def test_text_rzz_condition(self): cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) circuit.append(RZZGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) - self.assertEqual(str(_text_circuit_drawer(circuit, initial_state=False)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_cp_condition(self): """Test cp with condition""" @@ -787,7 +819,7 @@ def test_text_cp_condition(self): cr = ClassicalRegister(3, "c") circuit = QuantumCircuit(qr, cr) circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) - self.assertEqual(str(_text_circuit_drawer(circuit, initial_state=False)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=False)), expected) def test_text_cu1_reverse_bits(self): """cu1 drawing with reverse_bits""" @@ -806,7 +838,10 @@ def test_text_cu1_reverse_bits(self): circuit = QuantumCircuit(qr) circuit.append(CU1Gate(pi / 2), [qr[0], qr[1]]) circuit.append(CU1Gate(pi / 2), [qr[2], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_text_ccx(self): """cx drawing.""" @@ -826,7 +861,7 @@ def test_text_ccx(self): circuit.ccx(qr[0], qr[1], qr[2]) circuit.ccx(qr[2], qr[0], qr[1]) circuit.ccx(qr[2], qr[1], qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_reset(self): """Reset drawing.""" @@ -849,7 +884,7 @@ def test_text_reset(self): circuit = QuantumCircuit(qr1, qr2) circuit.reset(qr1) circuit.reset(qr2[1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_single_gate(self): """Single Qbit gate drawing.""" @@ -872,7 +907,7 @@ def test_text_single_gate(self): circuit = QuantumCircuit(qr1, qr2) circuit.h(qr1) circuit.h(qr2[1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_id(self): """Id drawing.""" @@ -895,7 +930,7 @@ def test_text_id(self): circuit = QuantumCircuit(qr1, qr2) circuit.id(qr1) circuit.id(qr2[1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_barrier(self): """Barrier drawing.""" @@ -918,7 +953,7 @@ def test_text_barrier(self): circuit = QuantumCircuit(qr1, qr2) circuit.barrier(qr1) circuit.barrier(qr2[1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_no_barriers(self): """Drawing without plotbarriers.""" @@ -943,7 +978,10 @@ def test_text_no_barriers(self): circuit.barrier(qr1) circuit.barrier(qr2[1]) circuit.h(qr2) - self.assertEqual(str(_text_circuit_drawer(circuit, plot_barriers=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, plot_barriers=False)), + expected, + ) def test_text_measure_html(self): """The measure operator. HTML representation.""" @@ -965,7 +1003,9 @@ def test_text_measure_html(self): cr = ClassicalRegister(1, "c") circuit = QuantumCircuit(qr, cr) circuit.measure(qr, cr) - self.assertEqual(_text_circuit_drawer(circuit)._repr_html_(), expected) + self.assertEqual( + circuit_drawer(circuit, output="text", initial_state=True)._repr_html_(), expected + ) def test_text_repr(self): """The measure operator. repr.""" @@ -982,7 +1022,9 @@ def test_text_repr(self): cr = ClassicalRegister(1, "c") circuit = QuantumCircuit(qr, cr) circuit.measure(qr, cr) - self.assertEqual(_text_circuit_drawer(circuit).__repr__(), expected) + self.assertEqual( + circuit_drawer(circuit, output="text", initial_state=True).__repr__(), expected + ) def test_text_justify_left(self): """Drawing with left justify""" @@ -1004,7 +1046,10 @@ def test_text_justify_left(self): circuit.x(qr1[0]) circuit.h(qr1[1]) circuit.measure(qr1[1], cr1[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="left")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="left")), + expected, + ) def test_text_justify_right(self): """Drawing with right justify""" @@ -1026,7 +1071,10 @@ def test_text_justify_right(self): circuit.x(qr1[0]) circuit.h(qr1[1]) circuit.measure(qr1[1], cr1[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="right")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="right")), + expected, + ) def test_text_justify_none(self): """Drawing with none justify""" @@ -1048,7 +1096,10 @@ def test_text_justify_none(self): circuit.x(qr1[0]) circuit.h(qr1[1]) circuit.measure(qr1[1], cr1[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="none")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="none")), + expected, + ) def test_text_justify_left_barrier(self): """Left justify respects barriers""" @@ -1067,7 +1118,10 @@ def test_text_justify_left_barrier(self): circuit.h(qr1[0]) circuit.barrier(qr1) circuit.h(qr1[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="left")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="left")), + expected, + ) def test_text_justify_right_barrier(self): """Right justify respects barriers""" @@ -1086,7 +1140,10 @@ def test_text_justify_right_barrier(self): circuit.h(qr1[0]) circuit.barrier(qr1) circuit.h(qr1[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="right")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="right")), + expected, + ) def test_text_barrier_label(self): """Show barrier label""" @@ -1108,7 +1165,7 @@ def test_text_barrier_label(self): circuit.y(0) circuit.x(1) circuit.barrier(label="End Y/X") - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_overlap_cx(self): """Overlapping CX gates are drawn not overlapping""" @@ -1130,7 +1187,10 @@ def test_text_overlap_cx(self): circuit = QuantumCircuit(qr1) circuit.cx(qr1[0], qr1[3]) circuit.cx(qr1[1], qr1[2]) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="left")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="left")), + expected, + ) def test_text_overlap_measure(self): """Measure is drawn not overlapping""" @@ -1151,7 +1211,10 @@ def test_text_overlap_measure(self): circuit = QuantumCircuit(qr1, cr1) circuit.measure(qr1[0], cr1[0]) circuit.x(qr1[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="left")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="left")), + expected, + ) def test_text_overlap_swap(self): """Swap is drawn in 2 separate columns""" @@ -1173,7 +1236,10 @@ def test_text_overlap_swap(self): qr2 = QuantumRegister(2, "q2") circuit = QuantumCircuit(qr1, qr2) circuit.swap(qr1, qr2) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="left")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="left")), + expected, + ) def test_text_justify_right_measure_resize(self): """Measure gate can resize if necessary""" @@ -1194,7 +1260,10 @@ def test_text_justify_right_measure_resize(self): circuit = QuantumCircuit(qr1, cr1) circuit.x(qr1[0]) circuit.measure(qr1[1], cr1[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, justify="right")), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, justify="right")), + expected, + ) def test_text_box_length(self): """The length of boxes is independent of other boxes in the layer @@ -1216,7 +1285,7 @@ def test_text_box_length(self): circuit.h(qr[0]) circuit.h(qr[0]) circuit.rz(0.0000001, qr[2]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_spacing_2378(self): """Small gates in the same layer as long gates. @@ -1236,7 +1305,7 @@ def test_text_spacing_2378(self): circuit = QuantumCircuit(qr) circuit.swap(qr[0], qr[1]) circuit.rz(11111, qr[2]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) @unittest.skipUnless(HAS_TWEEDLEDUM, "Tweedledum is required for these tests.") def test_text_synth_no_registerless(self): @@ -1261,7 +1330,7 @@ def grover_oracle(a: Int1, b: Int1, c: Int1) -> Int1: return a and b and not c circuit = grover_oracle.synth(registerless=False) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) class TestTextDrawerLabels(QiskitTestCase): @@ -1277,7 +1346,7 @@ def test_label(self): circuit = QuantumCircuit(1) circuit.append(HGate(label="an H gate"), [0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_gate_with_label(self): """Test a controlled gate-with-a-label.""" @@ -1293,7 +1362,7 @@ def test_controlled_gate_with_label(self): circuit = QuantumCircuit(2) circuit.append(HGate(label="an H gate").control(1), [0, 1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_label_on_controlled_gate(self): """Test a controlled gate with a label (as a as a whole).""" @@ -1310,7 +1379,7 @@ def test_label_on_controlled_gate(self): circuit = QuantumCircuit(2) circuit.append(HGate().control(1, label="a controlled H gate"), [0, 1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_rzz_on_wide_layer(self): """Test a labeled gate (RZZ) in a wide layer. @@ -1330,7 +1399,7 @@ def test_rzz_on_wide_layer(self): circuit.rzz(pi / 2, 0, 1) circuit.x(2, label="This is a really long long long box") - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cu1_on_wide_layer(self): """Test a labeled gate (CU1) in a wide layer. @@ -1350,7 +1419,7 @@ def test_cu1_on_wide_layer(self): circuit.append(CU1Gate(pi / 2), [0, 1]) circuit.x(2, label="This is a really long long long box") - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) class TestTextDrawerMultiQGates(QiskitTestCase): @@ -1374,7 +1443,10 @@ def test_2Qgate(self): my_gate2 = Gate(name="twoQ", num_qubits=2, params=[], label="twoQ") circuit.append(my_gate2, [qr[0], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_2Qgate_cross_wires(self): """2Q no params, with cross wires""" @@ -1394,7 +1466,10 @@ def test_2Qgate_cross_wires(self): my_gate2 = Gate(name="twoQ", num_qubits=2, params=[], label="twoQ") circuit.append(my_gate2, [qr[1], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_3Qgate_cross_wires(self): """3Q no params, with cross wires""" @@ -1416,7 +1491,10 @@ def test_3Qgate_cross_wires(self): my_gate3 = Gate(name="threeQ", num_qubits=3, params=[], label="threeQ") circuit.append(my_gate3, [qr[1], qr[2], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_2Qgate_nottogether(self): """2Q that are not together""" @@ -1437,7 +1515,10 @@ def test_2Qgate_nottogether(self): my_gate2 = Gate(name="twoQ", num_qubits=2, params=[], label="twoQ") circuit.append(my_gate2, [qr[0], qr[2]]) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_2Qgate_nottogether_across_4(self): """2Q that are 2 bits apart""" @@ -1461,7 +1542,10 @@ def test_2Qgate_nottogether_across_4(self): my_gate2 = Gate(name="twoQ", num_qubits=2, params=[], label="twoQ") circuit.append(my_gate2, [qr[0], qr[3]]) - self.assertEqual(str(_text_circuit_drawer(circuit, reverse_bits=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, reverse_bits=True)), + expected, + ) def test_unitary_nottogether_across_4(self): """unitary that are 2 bits apart""" @@ -1484,7 +1568,7 @@ def test_unitary_nottogether_across_4(self): qc.append(random_unitary(4, seed=42), [qr[0], qr[3]]) - self.assertEqual(str(_text_circuit_drawer(qc)), expected) + self.assertEqual(str(circuit_drawer(qc, initial_state=True, output="text")), expected) def test_kraus(self): """Test Kraus. @@ -1499,7 +1583,7 @@ def test_kraus(self): qc = QuantumCircuit(qr) qc.append(error, [qr[0]]) - self.assertEqual(str(_text_circuit_drawer(qc)), expected) + self.assertEqual(str(circuit_drawer(qc, output="text", initial_state=True)), expected) def test_multiplexer(self): """Test Multiplexer. @@ -1520,7 +1604,7 @@ def test_multiplexer(self): qc = QuantumCircuit(qr) qc.append(cx_multiplexer, [qr[0], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(qc)), expected) + self.assertEqual(str(circuit_drawer(qc, output="text", initial_state=True)), expected) def test_label_over_name_2286(self): """If there is a label, it should be used instead of the name @@ -1540,7 +1624,7 @@ def test_label_over_name_2286(self): circ.append(XGate(label="alt-X"), [qr[0]]) circ.append(UnitaryGate(numpy.eye(4), label="iswap"), [qr[0], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circ)), expected) + self.assertEqual(str(circuit_drawer(circ, output="text", initial_state=True)), expected) def test_label_turns_to_box_2286(self): """If there is a label, non-boxes turn into boxes @@ -1560,7 +1644,7 @@ def test_label_turns_to_box_2286(self): circ.append(CZGate(), [qr[0], qr[1]]) circ.append(CZGate(label="cz label"), [qr[0], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circ)), expected) + self.assertEqual(str(circuit_drawer(circ, output="text", initial_state=True)), expected) def test_control_gate_with_base_label_4361(self): """Control gate has a label and a base gate with a label @@ -1582,7 +1666,7 @@ def test_control_gate_with_base_label_4361(self): circ.append(controlh, [0, 1]) circ.append(controlh, [1, 0]) - self.assertEqual(str(_text_circuit_drawer(circ)), expected) + self.assertEqual(str(circuit_drawer(circ, output="text", initial_state=True)), expected) def test_control_gate_label_with_cond_1_low(self): """Control gate has a label and a conditional (compression=low) @@ -1608,7 +1692,12 @@ def test_control_gate_label_with_cond_1_low(self): controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [0, 1]) - self.assertEqual(str(_text_circuit_drawer(circ, vertical_compression="low")), expected) + self.assertEqual( + str( + circuit_drawer(circ, output="text", initial_state=True, vertical_compression="low") + ), + expected, + ) def test_control_gate_label_with_cond_1_low_cregbundle(self): """Control gate has a label and a conditional (compression=low) with cregbundle @@ -1635,7 +1724,16 @@ def test_control_gate_label_with_cond_1_low_cregbundle(self): circ.append(controlh, [0, 1]) self.assertEqual( - str(_text_circuit_drawer(circ, vertical_compression="low", cregbundle=True)), expected + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + vertical_compression="low", + cregbundle=True, + ) + ), + expected, ) def test_control_gate_label_with_cond_1_med(self): @@ -1661,7 +1759,15 @@ def test_control_gate_label_with_cond_1_med(self): circ.append(controlh, [0, 1]) self.assertEqual( - str(_text_circuit_drawer(circ, cregbundle=False, vertical_compression="medium")), + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + cregbundle=False, + vertical_compression="medium", + ) + ), expected, ) @@ -1689,7 +1795,15 @@ def test_control_gate_label_with_cond_1_med_cregbundle(self): circ.append(controlh, [0, 1]) self.assertEqual( - str(_text_circuit_drawer(circ, vertical_compression="medium", cregbundle=True)), + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="medium", + ) + ), expected, ) @@ -1716,7 +1830,16 @@ def test_control_gate_label_with_cond_1_high(self): circ.append(controlh, [0, 1]) self.assertEqual( - str(_text_circuit_drawer(circ, cregbundle=False, vertical_compression="high")), expected + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + cregbundle=False, + vertical_compression="high", + ) + ), + expected, ) def test_control_gate_label_with_cond_1_high_cregbundle(self): @@ -1742,7 +1865,16 @@ def test_control_gate_label_with_cond_1_high_cregbundle(self): circ.append(controlh, [0, 1]) self.assertEqual( - str(_text_circuit_drawer(circ, vertical_compression="high", cregbundle=True)), expected + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="high", + ) + ), + expected, ) def test_control_gate_label_with_cond_2_med_space(self): @@ -1768,7 +1900,14 @@ def test_control_gate_label_with_cond_2_med_space(self): controlh = hgate.control(label="my ch").c_if(cr, 1) circ.append(controlh, [1, 0]) - self.assertEqual(str(_text_circuit_drawer(circ, vertical_compression="medium")), expected) + self.assertEqual( + str( + circuit_drawer( + circ, output="text", initial_state=True, vertical_compression="medium" + ) + ), + expected, + ) def test_control_gate_label_with_cond_2_med(self): """Control gate has a label and a conditional (on label, compression=med) @@ -1794,7 +1933,15 @@ def test_control_gate_label_with_cond_2_med(self): circ.append(controlh, [1, 0]) self.assertEqual( - str(_text_circuit_drawer(circ, cregbundle=False, vertical_compression="medium")), + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + cregbundle=False, + vertical_compression="medium", + ) + ), expected, ) @@ -1822,7 +1969,15 @@ def test_control_gate_label_with_cond_2_med_cregbundle(self): circ.append(controlh, [1, 0]) self.assertEqual( - str(_text_circuit_drawer(circ, vertical_compression="medium", cregbundle=True)), + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="medium", + ) + ), expected, ) @@ -1851,7 +2006,16 @@ def test_control_gate_label_with_cond_2_low(self): circ.append(controlh, [1, 0]) self.assertEqual( - str(_text_circuit_drawer(circ, cregbundle=False, vertical_compression="low")), expected + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + cregbundle=False, + vertical_compression="low", + ) + ), + expected, ) def test_control_gate_label_with_cond_2_low_cregbundle(self): @@ -1879,7 +2043,16 @@ def test_control_gate_label_with_cond_2_low_cregbundle(self): circ.append(controlh, [1, 0]) self.assertEqual( - str(_text_circuit_drawer(circ, vertical_compression="low", cregbundle=True)), expected + str( + circuit_drawer( + circ, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="low", + ) + ), + expected, ) @@ -1899,7 +2072,7 @@ def test_text_no_parameters(self): qr = QuantumRegister(1, "q") circuit = QuantumCircuit(qr) circuit.x(0) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_parameters_mix(self): """cu3 drawing with parameters""" @@ -1917,7 +2090,7 @@ def test_text_parameters_mix(self): circuit = QuantumCircuit(qr) circuit.cu(pi / 2, Parameter("theta"), pi, 0, qr[0], qr[1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_bound_parameters(self): """Bound parameters @@ -1937,7 +2110,7 @@ def test_text_bound_parameters(self): circuit.append(my_u2, [qr[0]]) circuit = circuit.assign_parameters({phi: 3.141592653589793, lam: 3.141592653589793}) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_pi_param_expr(self): """Text pi in circuit with parameter expression.""" @@ -1976,7 +2149,7 @@ def test_text_ndarray_parameters(self): qr = QuantumRegister(1, "q") circuit = QuantumCircuit(qr) circuit.unitary(numpy.array([[0, 1], [1, 0]]), 0) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_qc_parameters(self): """Test that if params are type QuantumCircuit, params are not displayed.""" @@ -1997,7 +2170,7 @@ def test_text_qc_parameters(self): qr = QuantumRegister(2, "q") circuit = QuantumCircuit(qr) circuit.append(inst, [0, 1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) class TestTextDrawerVerticalCompressionLow(QiskitTestCase): @@ -2030,7 +2203,15 @@ def test_text_conditional_1(self): circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( - str(_text_circuit_drawer(circuit, cregbundle=False, vertical_compression="low")), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=False, + vertical_compression="low", + ) + ), expected, ) @@ -2061,7 +2242,15 @@ def test_text_conditional_1_bundle(self): circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( - str(_text_circuit_drawer(circuit, vertical_compression="low", cregbundle=True)), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + vertical_compression="low", + cregbundle=True, + ) + ), expected, ) @@ -2104,8 +2293,13 @@ def test_text_conditional_reverse_bits_true(self): self.assertEqual( str( - _text_circuit_drawer( - circuit, vertical_compression="low", cregbundle=False, reverse_bits=True + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=False, + reverse_bits=True, + vertical_compression="low", ) ), expected, @@ -2150,8 +2344,13 @@ def test_text_conditional_reverse_bits_false(self): self.assertEqual( str( - _text_circuit_drawer( - circuit, vertical_compression="low", cregbundle=False, reverse_bits=False + circuit_drawer( + circuit, + output="text", + initial_state=True, + vertical_compression="low", + cregbundle=False, + reverse_bits=False, ) ), expected, @@ -2180,7 +2379,15 @@ def test_text_justify_right(self): circuit.h(qr1[1]) circuit.measure(qr1[1], cr1[1]) self.assertEqual( - str(_text_circuit_drawer(circuit, justify="right", vertical_compression="low")), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + justify="right", + vertical_compression="low", + ) + ), expected, ) @@ -2212,7 +2419,15 @@ def test_text_conditional_1(self): ) circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( - str(_text_circuit_drawer(circuit, cregbundle=False, vertical_compression="medium")), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=False, + vertical_compression="medium", + ) + ), expected, ) @@ -2242,7 +2457,15 @@ def test_text_conditional_1_bundle(self): circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( - str(_text_circuit_drawer(circuit, vertical_compression="medium", cregbundle=True)), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + vertical_compression="medium", + cregbundle=True, + ) + ), expected, ) @@ -2275,7 +2498,15 @@ def test_text_measure_with_spaces(self): ) circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( - str(_text_circuit_drawer(circuit, cregbundle=False, vertical_compression="medium")), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=False, + vertical_compression="medium", + ) + ), expected, ) @@ -2305,7 +2536,15 @@ def test_text_measure_with_spaces_bundle(self): ) circuit = QuantumCircuit.from_qasm_str(qasm_string) self.assertEqual( - str(_text_circuit_drawer(circuit, vertical_compression="medium", cregbundle=True)), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + vertical_compression="medium", + cregbundle=True, + ) + ), expected, ) @@ -2332,7 +2571,15 @@ def test_text_barrier_med_compress_1(self): ) self.assertEqual( - str(_text_circuit_drawer(circuit, vertical_compression="medium", cregbundle=False)), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + vertical_compression="medium", + cregbundle=False, + ) + ), expected, ) @@ -2360,7 +2607,15 @@ def test_text_barrier_med_compress_2(self): ) self.assertEqual( - str(_text_circuit_drawer(circuit, vertical_compression="medium", cregbundle=False)), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + vertical_compression="medium", + cregbundle=False, + ) + ), expected, ) @@ -2391,8 +2646,10 @@ def test_text_barrier_med_compress_3(self): self.assertEqual( str( - _text_circuit_drawer( + circuit_drawer( circuit, + output="text", + initial_state=True, vertical_compression="medium", wire_order=[0, 1, 3, 4, 2], cregbundle=False, @@ -2429,7 +2686,18 @@ def test_text_conditional_1_cregbundle(self): ) circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="high", + ) + ), + expected, + ) def test_text_conditional_1(self): """Conditional drawing with 1-bit-length regs.""" @@ -2455,7 +2723,10 @@ def test_text_conditional_1(self): ) circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_2_cregbundle(self): """Conditional drawing with 2-bit-length regs with cregbundle""" @@ -2480,7 +2751,18 @@ def test_text_conditional_2_cregbundle(self): ] ) circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="high", + ) + ), + expected, + ) def test_text_conditional_2(self): """Conditional drawing with 2-bit-length regs.""" @@ -2509,7 +2791,10 @@ def test_text_conditional_2(self): ] ) circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_3_cregbundle(self): """Conditional drawing with 3-bit-length regs with cregbundle.""" @@ -2534,7 +2819,18 @@ def test_text_conditional_3_cregbundle(self): ] ) circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="high", + ) + ), + expected, + ) def test_text_conditional_3(self): """Conditional drawing with 3-bit-length regs.""" @@ -2567,7 +2863,10 @@ def test_text_conditional_3(self): ] ) circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_4(self): """Conditional drawing with 4-bit-length regs.""" @@ -2592,7 +2891,14 @@ def test_text_conditional_4(self): ] ) circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual( + str( + circuit_drawer( + circuit, output="text", initial_state=True, vertical_compression="high" + ) + ), + expected, + ) def test_text_conditional_5(self): """Conditional drawing with 5-bit-length regs.""" @@ -2633,7 +2939,10 @@ def test_text_conditional_5(self): ] ) circuit = QuantumCircuit.from_qasm_str(qasm_string) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_cz_no_space_cregbundle(self): """Conditional CZ without space""" @@ -2654,7 +2963,10 @@ def test_text_conditional_cz_no_space_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_cz_no_space(self): """Conditional CZ without space""" @@ -2675,7 +2987,10 @@ def test_text_conditional_cz_no_space(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_cz_cregbundle(self): """Conditional CZ with a wire in the middle""" @@ -2698,7 +3013,10 @@ def test_text_conditional_cz_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_cz(self): """Conditional CZ with a wire in the middle""" @@ -2721,7 +3039,10 @@ def test_text_conditional_cz(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_cx_ct_cregbundle(self): """Conditional CX (control-target) with a wire in the middle""" @@ -2744,7 +3065,10 @@ def test_text_conditional_cx_ct_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_cx_ct(self): """Conditional CX (control-target) with a wire in the middle""" @@ -2767,7 +3091,10 @@ def test_text_conditional_cx_ct(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_cx_tc_cregbundle(self): """Conditional CX (target-control) with a wire in the middle with cregbundle.""" @@ -2790,7 +3117,10 @@ def test_text_conditional_cx_tc_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_cx_tc(self): """Conditional CX (target-control) with a wire in the middle""" @@ -2813,7 +3143,10 @@ def test_text_conditional_cx_tc(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_cu3_ct_cregbundle(self): """Conditional Cu3 (control-target) with a wire in the middle with cregbundle""" @@ -2836,7 +3169,10 @@ def test_text_conditional_cu3_ct_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_cu3_ct(self): """Conditional Cu3 (control-target) with a wire in the middle""" @@ -2859,7 +3195,10 @@ def test_text_conditional_cu3_ct(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_cu3_tc_cregbundle(self): """Conditional Cu3 (target-control) with a wire in the middle with cregbundle""" @@ -2882,7 +3221,10 @@ def test_text_conditional_cu3_tc_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_cu3_tc(self): """Conditional Cu3 (target-control) with a wire in the middle""" @@ -2905,7 +3247,10 @@ def test_text_conditional_cu3_tc(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_ccx_cregbundle(self): """Conditional CCX with a wire in the middle with cregbundle""" @@ -2930,7 +3275,10 @@ def test_text_conditional_ccx_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_ccx(self): """Conditional CCX with a wire in the middle""" @@ -2955,7 +3303,10 @@ def test_text_conditional_ccx(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_ccx_no_space_cregbundle(self): """Conditional CCX without space with cregbundle""" @@ -2978,7 +3329,18 @@ def test_text_conditional_ccx_no_space_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="high", + ) + ), + expected, + ) def test_text_conditional_ccx_no_space(self): """Conditional CCX without space""" @@ -3001,7 +3363,10 @@ def test_text_conditional_ccx_no_space(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_h_cregbundle(self): """Conditional H with a wire in the middle with cregbundle""" @@ -3022,7 +3387,10 @@ def test_text_conditional_h_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_h(self): """Conditional H with a wire in the middle""" @@ -3043,7 +3411,10 @@ def test_text_conditional_h(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_swap_cregbundle(self): """Conditional SWAP with cregbundle""" @@ -3066,7 +3437,10 @@ def test_text_conditional_swap_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_swap(self): """Conditional SWAP""" @@ -3089,7 +3463,10 @@ def test_text_conditional_swap(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_conditional_cswap_cregbundle(self): """Conditional CSwap with cregbundle""" @@ -3114,7 +3491,10 @@ def test_text_conditional_cswap_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_conditional_cswap(self): """Conditional CSwap""" @@ -3139,7 +3519,10 @@ def test_text_conditional_cswap(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_conditional_reset_cregbundle(self): """Reset drawing with cregbundle.""" @@ -3161,7 +3544,10 @@ def test_conditional_reset_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_conditional_reset(self): """Reset drawing.""" @@ -3183,7 +3569,10 @@ def test_conditional_reset(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_conditional_multiplexer_cregbundle(self): """Test Multiplexer with cregbundle.""" @@ -3207,7 +3596,9 @@ def test_conditional_multiplexer_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(qc, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(qc, output="text", initial_state=True, cregbundle=True)), expected + ) def test_conditional_multiplexer(self): """Test Multiplexer.""" @@ -3231,7 +3622,9 @@ def test_conditional_multiplexer(self): ] ) - self.assertEqual(str(_text_circuit_drawer(qc, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(qc, output="text", initial_state=True, cregbundle=False)), expected + ) def test_text_conditional_measure_cregbundle(self): """Conditional with measure on same clbit with cregbundle""" @@ -3254,7 +3647,18 @@ def test_text_conditional_measure_cregbundle(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="high", + ) + ), + expected, + ) def test_text_conditional_measure(self): """Conditional with measure on same clbit""" @@ -3278,7 +3682,10 @@ def test_text_conditional_measure(self): " 0x1 ", ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_bit_conditional(self): """Test bit conditions on gates""" @@ -3303,7 +3710,10 @@ def test_text_bit_conditional(self): ] ) - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=False)), + expected, + ) def test_text_bit_conditional_cregbundle(self): """Test bit conditions on gates when cregbundle=True""" @@ -3328,7 +3738,15 @@ def test_text_bit_conditional_cregbundle(self): ) self.assertEqual( - str(_text_circuit_drawer(circuit, cregbundle=True, vertical_compression="medium")), + str( + circuit_drawer( + circuit, + output="text", + initial_state=True, + cregbundle=True, + vertical_compression="medium", + ) + ), expected, ) @@ -3362,7 +3780,8 @@ def test_text_condition_measure_bits_true(self): ] ) self.assertEqual( - str(_text_circuit_drawer(circuit, cregbundle=True, initial_state=False)), expected + str(circuit_drawer(circuit, output="text", cregbundle=True, initial_state=False)), + expected, ) def test_text_condition_measure_bits_false(self): @@ -3401,7 +3820,8 @@ def test_text_condition_measure_bits_false(self): ] ) self.assertEqual( - str(_text_circuit_drawer(circuit, cregbundle=False, initial_state=False)), expected + str(circuit_drawer(circuit, output="text", cregbundle=False, initial_state=False)), + expected, ) def test_text_conditional_reverse_bits_1(self): @@ -3428,7 +3848,12 @@ def test_text_conditional_reverse_bits_1(self): ) self.assertEqual( - str(_text_circuit_drawer(circuit, cregbundle=False, reverse_bits=True)), expected + str( + circuit_drawer( + circuit, output="text", initial_state=True, cregbundle=False, reverse_bits=True + ) + ), + expected, ) def test_text_conditional_reverse_bits_2(self): @@ -3460,7 +3885,12 @@ def test_text_conditional_reverse_bits_2(self): ) self.assertEqual( - str(_text_circuit_drawer(circuit, cregbundle=False, reverse_bits=True)), expected + str( + circuit_drawer( + circuit, output="text", initial_state=True, cregbundle=False, reverse_bits=True + ) + ), + expected, ) def test_text_condition_bits_reverse(self): @@ -3493,8 +3923,8 @@ def test_text_condition_bits_reverse(self): ) self.assertEqual( str( - _text_circuit_drawer( - circuit, cregbundle=True, initial_state=False, reverse_bits=True + circuit_drawer( + circuit, output="text", cregbundle=True, initial_state=False, reverse_bits=True ) ), expected, @@ -3514,7 +3944,10 @@ def test_text_h(self): qr1 = QuantumRegister(3, "q1") circuit = QuantumCircuit(qr1) circuit.h(qr1[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, idle_wires=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, idle_wires=False)), + expected, + ) def test_text_measure(self): """Remove QuWires and ClWires.""" @@ -3535,13 +3968,19 @@ def test_text_measure(self): cr2 = ClassicalRegister(2, "c2") circuit = QuantumCircuit(qr1, qr2, cr1, cr2) circuit.measure(qr2, cr2) - self.assertEqual(str(_text_circuit_drawer(circuit, idle_wires=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, idle_wires=False)), + expected, + ) def test_text_empty_circuit(self): """Remove everything in an empty circuit.""" expected = "" circuit = QuantumCircuit() - self.assertEqual(str(_text_circuit_drawer(circuit, idle_wires=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, idle_wires=False)), + expected, + ) def test_text_barrier(self): """idle_wires should ignore barrier @@ -3555,7 +3994,10 @@ def test_text_barrier(self): circuit = QuantumCircuit(qr) circuit.h(qr[1]) circuit.barrier(qr[1], qr[2]) - self.assertEqual(str(_text_circuit_drawer(circuit, idle_wires=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, idle_wires=False)), + expected, + ) def test_text_barrier_delay(self): """idle_wires should ignore delay""" @@ -3569,7 +4011,10 @@ def test_text_barrier_delay(self): circuit.h(qr[1]) circuit.barrier() circuit.delay(100, qr[2]) - self.assertEqual(str(_text_circuit_drawer(circuit, idle_wires=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, idle_wires=False)), + expected, + ) def test_does_not_mutate_circuit(self): """Using 'idle_wires=False' should not mutate the circuit. Regression test of gh-8739.""" @@ -3594,7 +4039,7 @@ def test_text_pifrac(self): qr = QuantumRegister(1, "q") circuit = QuantumCircuit(qr) circuit.u(pi, -5 * pi / 8, 0, qr[0]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_complex(self): """Complex numbers show up in the text @@ -3671,7 +4116,7 @@ def test_text_all_1q_1c(self): circuit = QuantumCircuit(qr1, cr1) circuit.append(inst, qr1[:], cr1[:]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_all_2q_2c(self): """Test q0-q1-c0-c1 in q0-q1-c0-c1""" @@ -3695,7 +4140,7 @@ def test_text_all_2q_2c(self): circuit = QuantumCircuit(qr2, cr2) circuit.append(inst, qr2[:], cr2[:]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_all_2q_2c_cregbundle(self): """Test q0-q1-c0-c1 in q0-q1-c0-c1. Ignore cregbundle=True""" @@ -3719,7 +4164,10 @@ def test_text_all_2q_2c_cregbundle(self): circuit = QuantumCircuit(qr2, cr2) circuit.append(inst, qr2[:], cr2[:]) with self.assertWarns(RuntimeWarning): - self.assertEqual(str(_text_circuit_drawer(circuit, cregbundle=True)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, cregbundle=True)), + expected, + ) def test_text_4q_2c(self): """Test q1-q2-q3-q4-c1-c2 in q0-q1-q2-q3-q4-q5-c0-c1-c2-c3-c4-c5""" @@ -3761,7 +4209,7 @@ def test_text_4q_2c(self): circuit = QuantumCircuit(qr6, cr6) circuit.append(inst, qr6[1:5], cr6[1:3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_2q_1c(self): """Test q0-c0 in q0-q1-c0 @@ -3784,7 +4232,7 @@ def test_text_2q_1c(self): inst = QuantumCircuit(1, 1, name="Name").to_instruction() circuit.append(inst, [qr[0]], [cr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_3q_3c_qlabels_inverted(self): """Test q3-q0-q1-c0-c1-c_10 in q0-q1-q2-q3-c0-c1-c2-c_10-c_11 @@ -3820,7 +4268,7 @@ def test_text_3q_3c_qlabels_inverted(self): inst = QuantumCircuit(3, 3, name="Name").to_instruction() circuit.append(inst, [qr[3], qr[0], qr[1]], [cr[0], cr[1], cr1[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_3q_3c_clabels_inverted(self): """Test q0-q1-q3-c_11-c0-c_10 in q0-q1-q2-q3-c0-c1-c2-c_10-c_11 @@ -3856,7 +4304,7 @@ def test_text_3q_3c_clabels_inverted(self): inst = QuantumCircuit(3, 3, name="Name").to_instruction() circuit.append(inst, [qr[0], qr[1], qr[3]], [cr1[1], cr[0], cr1[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_3q_3c_qclabels_inverted(self): """Test q3-q1-q2-c_11-c0-c_10 in q0-q1-q2-q3-c0-c1-c2-c_10-c_11 @@ -3892,7 +4340,7 @@ def test_text_3q_3c_qclabels_inverted(self): inst = QuantumCircuit(3, 3, name="Name").to_instruction() circuit.append(inst, [qr[3], qr[1], qr[2]], [cr1[1], cr[0], cr1[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) class TestTextDrawerAppendedLargeInstructions(QiskitTestCase): @@ -3934,7 +4382,7 @@ def test_text_11q(self): inst = QuantumCircuit(11, name="Name").to_instruction() circuit.append(inst, qr) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_text_11q_1c(self): """Test q0-...-q10-c0 in q0-...-q10-c0""" @@ -3974,7 +4422,7 @@ def test_text_11q_1c(self): inst = QuantumCircuit(11, 1, name="Name").to_instruction() circuit.append(inst, qr, cr) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) class TestTextControlledGate(QiskitTestCase): @@ -3996,7 +4444,7 @@ def test_cch_bot(self): qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(2), [qr[0], qr[1], qr[2]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cch_mid(self): """Controlled CH (middle)""" @@ -4014,7 +4462,7 @@ def test_cch_mid(self): qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(2), [qr[0], qr[2], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cch_top(self): """Controlled CH""" @@ -4032,7 +4480,7 @@ def test_cch_top(self): qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(2), [qr[2], qr[1], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_c3h(self): """Controlled Controlled CH""" @@ -4052,7 +4500,7 @@ def test_c3h(self): qr = QuantumRegister(4, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(3), [qr[0], qr[1], qr[2], qr[3]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_c3h_middle(self): """Controlled Controlled CH (middle)""" @@ -4072,7 +4520,7 @@ def test_c3h_middle(self): qr = QuantumRegister(4, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(3), [qr[0], qr[3], qr[2], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_c3u2(self): """Controlled Controlled U2""" @@ -4092,7 +4540,7 @@ def test_c3u2(self): qr = QuantumRegister(4, "q") circuit = QuantumCircuit(qr) circuit.append(U2Gate(pi, -5 * pi / 8).control(3), [qr[0], qr[3], qr[2], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_edge(self): """Controlled composite gates (edge) @@ -4119,7 +4567,7 @@ def test_controlled_composite_gate_edge(self): circuit = QuantumCircuit(4) circuit.append(cghz, [1, 0, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_top(self): """Controlled composite gates (top)""" @@ -4145,7 +4593,7 @@ def test_controlled_composite_gate_top(self): circuit = QuantumCircuit(4) circuit.append(cghz, [0, 1, 3, 2]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_bot(self): """Controlled composite gates (bottom)""" @@ -4171,7 +4619,7 @@ def test_controlled_composite_gate_bot(self): circuit = QuantumCircuit(4) circuit.append(cghz, [3, 1, 0, 2]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_top_bot(self): """Controlled composite gates (top and bottom)""" @@ -4199,7 +4647,7 @@ def test_controlled_composite_gate_top_bot(self): circuit = QuantumCircuit(5) circuit.append(ccghz, [4, 0, 1, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_all(self): """Controlled composite gates (top, bot, and edge)""" @@ -4229,7 +4677,7 @@ def test_controlled_composite_gate_all(self): circuit = QuantumCircuit(6) circuit.append(ccghz, [0, 2, 5, 1, 3, 4]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_even_label(self): """Controlled composite gates (top and bottom) with a even label length""" @@ -4257,7 +4705,7 @@ def test_controlled_composite_gate_even_label(self): circuit = QuantumCircuit(5) circuit.append(ccghz, [4, 0, 1, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) class TestTextOpenControlledGate(QiskitTestCase): @@ -4277,7 +4725,7 @@ def test_ch_bot(self): qr = QuantumRegister(2, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(1, ctrl_state=0), [qr[0], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cz_bot(self): """Open controlled Z (bottom)""" @@ -4291,7 +4739,7 @@ def test_cz_bot(self): qr = QuantumRegister(2, "q") circuit = QuantumCircuit(qr) circuit.append(ZGate().control(1, ctrl_state=0), [qr[0], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_ccz_bot(self): """Closed-Open controlled Z (bottom)""" @@ -4309,7 +4757,7 @@ def test_ccz_bot(self): qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) circuit.append(ZGate().control(2, ctrl_state="01"), [qr[0], qr[1], qr[2]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cccz_conditional(self): """Closed-Open controlled Z (with conditional)""" @@ -4334,7 +4782,7 @@ def test_cccz_conditional(self): circuit.append( ZGate().control(3, ctrl_state="101").c_if(cr, 1), [qr[0], qr[1], qr[2], qr[3]] ) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cch_bot(self): """Controlled CH (bottom)""" @@ -4352,7 +4800,7 @@ def test_cch_bot(self): qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(2, ctrl_state="10"), [qr[0], qr[1], qr[2]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cch_mid(self): """Controlled CH (middle)""" @@ -4370,7 +4818,7 @@ def test_cch_mid(self): qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(2, ctrl_state="10"), [qr[0], qr[2], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_cch_top(self): """Controlled CH""" @@ -4388,7 +4836,7 @@ def test_cch_top(self): qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(2, ctrl_state="10"), [qr[1], qr[2], qr[0]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_c3h(self): """Controlled Controlled CH""" @@ -4408,7 +4856,7 @@ def test_c3h(self): qr = QuantumRegister(4, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(3, ctrl_state="100"), [qr[0], qr[1], qr[2], qr[3]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_c3h_middle(self): """Controlled Controlled CH (middle)""" @@ -4428,7 +4876,7 @@ def test_c3h_middle(self): qr = QuantumRegister(4, "q") circuit = QuantumCircuit(qr) circuit.append(HGate().control(3, ctrl_state="010"), [qr[0], qr[3], qr[2], qr[1]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_c3u2(self): """Controlled Controlled U2""" @@ -4450,7 +4898,7 @@ def test_c3u2(self): circuit.append( U2Gate(pi, -5 * pi / 8).control(3, ctrl_state="100"), [qr[0], qr[3], qr[2], qr[1]] ) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_edge(self): """Controlled composite gates (edge) @@ -4477,7 +4925,7 @@ def test_controlled_composite_gate_edge(self): circuit = QuantumCircuit(4) circuit.append(cghz, [1, 0, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_top(self): """Controlled composite gates (top)""" @@ -4503,7 +4951,7 @@ def test_controlled_composite_gate_top(self): circuit = QuantumCircuit(4) circuit.append(cghz, [0, 1, 3, 2]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_bot(self): """Controlled composite gates (bottom)""" @@ -4529,7 +4977,7 @@ def test_controlled_composite_gate_bot(self): circuit = QuantumCircuit(4) circuit.append(cghz, [3, 1, 0, 2]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_top_bot(self): """Controlled composite gates (top and bottom)""" @@ -4557,7 +5005,7 @@ def test_controlled_composite_gate_top_bot(self): circuit = QuantumCircuit(5) circuit.append(ccghz, [4, 0, 1, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_controlled_composite_gate_all(self): """Controlled composite gates (top, bot, and edge)""" @@ -4587,7 +5035,7 @@ def test_controlled_composite_gate_all(self): circuit = QuantumCircuit(6) circuit.append(ccghz, [0, 2, 5, 1, 3, 4]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_open_controlled_x(self): """Controlled X gates. @@ -4620,7 +5068,7 @@ def test_open_controlled_x(self): control3 = XGate().control(4, ctrl_state="0101") circuit.append(control3, [0, 1, 4, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_open_controlled_y(self): """Controlled Y gates. @@ -4653,7 +5101,7 @@ def test_open_controlled_y(self): control3 = YGate().control(4, ctrl_state="0101") circuit.append(control3, [0, 1, 4, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_open_controlled_z(self): """Controlled Z gates.""" @@ -4685,7 +5133,7 @@ def test_open_controlled_z(self): control3 = ZGate().control(4, ctrl_state="0101") circuit.append(control3, [0, 1, 4, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_open_controlled_u1(self): """Controlled U1 gates.""" @@ -4717,7 +5165,7 @@ def test_open_controlled_u1(self): control3 = U1Gate(0.5).control(4, ctrl_state="0101") circuit.append(control3, [0, 1, 4, 2, 3]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_open_controlled_swap(self): """Controlled SWAP gates.""" @@ -4747,7 +5195,7 @@ def test_open_controlled_swap(self): control3 = SwapGate().control(3, ctrl_state="010") circuit.append(control3, [0, 1, 2, 3, 4]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_open_controlled_rzz(self): """Controlled RZZ gates.""" @@ -4777,7 +5225,7 @@ def test_open_controlled_rzz(self): control3 = RZZGate(1).control(3, ctrl_state="010") circuit.append(control3, [0, 1, 2, 3, 4]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_open_out_of_order(self): """Out of order CXs @@ -4801,7 +5249,7 @@ def test_open_out_of_order(self): circuit = QuantumCircuit(qr) circuit.append(XGate().control(3, ctrl_state="101"), [qr[0], qr[3], qr[1], qr[2]]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) class TestTextWithLayout(QiskitTestCase): @@ -4823,7 +5271,7 @@ def test_with_no_layout(self): qr = QuantumRegister(3, "q") circuit = QuantumCircuit(qr) circuit.h(qr[1]) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_mixed_layout(self): """With a mixed layout.""" @@ -4849,7 +5297,9 @@ def test_mixed_layout(self): pass_.property_set["layout"] = Layout({qr[0]: 0, ancilla[1]: 1, ancilla[0]: 2, qr[1]: 3}) circuit_with_layout = pass_(circuit) - self.assertEqual(str(_text_circuit_drawer(circuit_with_layout)), expected) + self.assertEqual( + str(circuit_drawer(circuit_with_layout, output="text", initial_state=True)), expected + ) def test_partial_layout(self): """With a partial layout. @@ -4878,7 +5328,7 @@ def test_partial_layout(self): ) circuit._layout.initial_layout.add_register(qr) - self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected) def test_with_classical_regs(self): """Involving classical registers""" @@ -4909,7 +5359,9 @@ def test_with_classical_regs(self): pass_.property_set["layout"] = Layout({qr1[0]: 0, qr1[1]: 1, qr2[0]: 2, qr2[1]: 3}) circuit_with_layout = pass_(circuit) - self.assertEqual(str(_text_circuit_drawer(circuit_with_layout)), expected) + self.assertEqual( + str(circuit_drawer(circuit_with_layout, output="text", initial_state=True)), expected + ) def test_with_layout_but_disable(self): """With parameter without_layout=False""" @@ -4936,7 +5388,10 @@ def test_with_layout_but_disable(self): circuit._layout = Layout({qr1[0]: 0, qr1[1]: 1, qr2[0]: 2, qr2[1]: 3}) circuit.measure(pqr[2], cr[0]) circuit.measure(pqr[3], cr[1]) - self.assertEqual(str(_text_circuit_drawer(circuit, with_layout=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=True, with_layout=False)), + expected, + ) def test_after_transpile(self): """After transpile, the drawing should include the layout""" @@ -5079,7 +5534,9 @@ def test_initial_value_false(self): ] ) - self.assertEqual(str(_text_circuit_drawer(self.circuit, initial_state=False)), expected) + self.assertEqual( + str(circuit_drawer(self.circuit, output="text", initial_state=False)), expected + ) class TestTextHamiltonianGate(QiskitTestCase): @@ -5255,7 +5712,12 @@ def test_text_drawer_utf8(self): filename = "current_textplot_utf8.txt" qc = self.sample_circuit() output = _text_circuit_drawer( - qc, filename=filename, fold=-1, initial_state=True, cregbundle=False, encoding="utf8" + qc, + filename=filename, + fold=-1, + initial_state=True, + cregbundle=False, + encoding="utf8", ) try: encode(str(output), encoding="utf8") @@ -5269,13 +5731,18 @@ def test_text_drawer_cp437(self): filename = "current_textplot_cp437.txt" qc = self.sample_circuit() output = _text_circuit_drawer( - qc, filename=filename, fold=-1, initial_state=True, cregbundle=False, encoding="cp437" + qc, + filename=filename, + fold=-1, + initial_state=True, + cregbundle=False, + encoding="cp437", ) try: encode(str(output), encoding="cp437") except UnicodeEncodeError: self.fail("_text_circuit_drawer() should be cp437.") - self.assertFilesAreEqual(filename, self.text_reference_cp437, "cp437") + self.assertFilesAreEqual("current_textplot_cp437.txt", self.text_reference_cp437, "cp437") os.remove(filename) @@ -5310,7 +5777,8 @@ def test_if_op_bundle_false(self): circuit.h(0) circuit.cx(0, 1) self.assertEqual( - str(_text_circuit_drawer(circuit, initial_state=False, cregbundle=False)), expected + str(circuit_drawer(circuit, output="text", initial_state=False, cregbundle=False)), + expected, ) def test_if_op_bundle_true(self): @@ -5338,7 +5806,10 @@ def test_if_op_bundle_true(self): with circuit.if_test((cr[1], 1)): circuit.h(0) circuit.cx(0, 1) - self.assertEqual(str(_text_circuit_drawer(circuit, initial_state=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=False)), + expected, + ) def test_if_else_with_body_specified(self): """Test an IfElseOp where the body is directly specified.""" @@ -5382,7 +5853,8 @@ def test_if_else_with_body_specified(self): circuit.if_else((cr[1], 1), circuit2, None, [0, 1, 2], [0, 1, 2]) circuit.x(0, label="X1i") self.assertEqual( - str(_text_circuit_drawer(circuit, initial_state=False, cregbundle=False)), expected + str(circuit_drawer(circuit, output="text", initial_state=False, cregbundle=False)), + expected, ) def test_if_op_nested_wire_order(self): @@ -5464,8 +5936,9 @@ def test_if_op_nested_wire_order(self): circuit.x(0) self.assertEqual( str( - _text_circuit_drawer( + circuit_drawer( circuit, + output="text", fold=77, initial_state=False, wire_order=[2, 0, 3, 1, 4, 5, 6], @@ -5509,7 +5982,8 @@ def test_while_loop(self): with circuit.if_test((cr[2], 1)): circuit.x(0) self.assertEqual( - str(_text_circuit_drawer(circuit, initial_state=False, cregbundle=False)), expected + str(circuit_drawer(circuit, output="text", initial_state=False, cregbundle=False)), + expected, ) def test_for_loop(self): @@ -5548,7 +6022,11 @@ def test_for_loop(self): with circuit.if_test((cr[2], 1)): circuit.z(0) self.assertEqual( - str(_text_circuit_drawer(circuit, fold=-1, initial_state=False, cregbundle=False)), + str( + circuit_drawer( + circuit, output="text", fold=-1, initial_state=False, cregbundle=False + ) + ), expected, ) @@ -5604,7 +6082,11 @@ def test_switch_case(self): circuit.cx(0, 1) circuit.h(0) self.assertEqual( - str(_text_circuit_drawer(circuit, fold=78, initial_state=False, cregbundle=False)), + str( + circuit_drawer( + circuit, output="text", fold=78, initial_state=False, cregbundle=False + ) + ), expected, ) @@ -5644,7 +6126,11 @@ def test_inner_wire_map_control_op(self): circuit = transpile(qc, backend, optimization_level=2, seed_transpiler=671_42) self.assertEqual( - str(_text_circuit_drawer(circuit, fold=78, initial_state=False, cregbundle=False)), + str( + circuit_drawer( + circuit, output="text", fold=78, initial_state=False, cregbundle=False + ) + ), expected, ) @@ -5683,7 +6169,8 @@ def test_if_else_op_from_circuit_with_conditions(self): circuit.if_else((cr[1], 1), qc2, None, [0, 1, 2], [0, 1, 2]) self.assertEqual( - str(_text_circuit_drawer(circuit, initial_state=False, cregbundle=False)), expected + str(circuit_drawer(circuit, output="text", initial_state=False, cregbundle=False)), + expected, ) def test_if_with_expr(self): @@ -5718,7 +6205,10 @@ def test_if_with_expr(self): with circuit.if_test(expr.equal(expr.bit_and(cr1, expr.bit_and(cr2, cr3)), 3)): circuit.z(0) - self.assertEqual(str(_text_circuit_drawer(circuit, initial_state=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", initial_state=False)), + expected, + ) def test_if_with_expr_nested(self): """Test an IfElseOp with an expression for nested""" @@ -5755,7 +6245,8 @@ def test_if_with_expr_nested(self): circuit.z(1) self.assertEqual( - str(_text_circuit_drawer(circuit, initial_state=False, fold=120)), expected + str(circuit_drawer(circuit, output="text", initial_state=False, fold=120)), + expected, ) def test_switch_with_expression(self): @@ -5808,7 +6299,10 @@ def test_switch_with_expression(self): with case(case.DEFAULT): circuit.cx(0, 1) - self.assertEqual(str(_text_circuit_drawer(circuit, fold=80, initial_state=False)), expected) + self.assertEqual( + str(circuit_drawer(circuit, output="text", fold=80, initial_state=False)), + expected, + ) if __name__ == "__main__": diff --git a/test/python/visualization/test_dag_drawer.py b/test/python/visualization/test_dag_drawer.py index bc5c588ff570..4b920390e880 100644 --- a/test/python/visualization/test_dag_drawer.py +++ b/test/python/visualization/test_dag_drawer.py @@ -16,11 +16,11 @@ import tempfile import unittest -from qiskit.circuit import QuantumRegister, QuantumCircuit, Qubit, Clbit +from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit, Qubit, Clbit from qiskit.visualization import dag_drawer from qiskit.exceptions import InvalidFileError from qiskit.visualization import VisualizationError -from qiskit.converters import circuit_to_dag +from qiskit.converters import circuit_to_dag, circuit_to_dagdependency from qiskit.utils import optionals as _optionals from .visualization import path_to_diagram_reference, QiskitVisualizationTestCase @@ -79,7 +79,34 @@ def test_dag_drawer_no_register(self): dag_drawer(dag, filename=tmp_path) image_ref = path_to_diagram_reference("dag_no_reg.png") image = Image.open(tmp_path) - self.assertImagesAreEqual(image, image_ref, 0.2) + self.assertImagesAreEqual(image, image_ref, 0.1) + + @unittest.skipUnless(_optionals.HAS_GRAPHVIZ, "Graphviz not installed") + @unittest.skipUnless(_optionals.HAS_PIL, "PIL not installed") + def test_dag_drawer_with_dag_dep(self): + """Test dag dependency visualization.""" + from PIL import Image # pylint: disable=import-error + + bits = [Qubit(), Clbit()] + qr = QuantumRegister(4, "qr") + cr = ClassicalRegister(4, "cr") + qc = QuantumCircuit(qr, bits, cr) + qc.h(0) + qc.cx(0, 1) + qc.cx(0, 2) + qc.cx(0, 3) + qc.x(3).c_if(cr[1], 1) + qc.h(3) + qc.x(4) + qc.barrier(0, 1) + qc.measure(0, 0) + dag = circuit_to_dagdependency(qc) + with tempfile.TemporaryDirectory() as tmpdirname: + tmp_path = os.path.join(tmpdirname, "dag_d.png") + dag_drawer(dag, filename=tmp_path) + image_ref = path_to_diagram_reference("dag_dep.png") + image = Image.open(tmp_path) + self.assertImagesAreEqual(image, image_ref, 0.1) if __name__ == "__main__": diff --git a/test/qpy_compat/compare_versions.py b/test/qpy_compat/compare_versions.py new file mode 100755 index 000000000000..5deec1b9ca5b --- /dev/null +++ b/test/qpy_compat/compare_versions.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Compare Qiskit versions to determine if we should run qpy compat tests.""" + +import argparse +import sys + +from qiskit.qpy.interface import VERSION_PATTERN_REGEX + + +def main(): + """Main function.""" + parser = argparse.ArgumentParser(prog="compare_version", description="Compare version strings") + parser.add_argument( + "source_version", help="Source version of Qiskit that is generating the payload" + ) + parser.add_argument("test_version", help="Version under test that will load the QPY payload") + args = parser.parse_args() + source_match = VERSION_PATTERN_REGEX.search(args.source_version) + target_match = VERSION_PATTERN_REGEX.search(args.test_version) + source_version = tuple(int(x) for x in source_match.group("release").split(".")) + target_version = tuple(int(x) for x in target_match.group("release").split(".")) + if source_version > target_version: + sys.exit(1) + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/test/qpy_compat/process_version.sh b/test/qpy_compat/process_version.sh index 59c5610eb95c..edf2157c1fae 100755 --- a/test/qpy_compat/process_version.sh +++ b/test/qpy_compat/process_version.sh @@ -14,16 +14,36 @@ set -e set -x +# version is the source version, this is the release with which to generate +# qpy files with to load with the version under test version=$1 parts=( ${version//./ } ) -if [[ ${parts[1]} -lt 18 ]] ; then +# qiskit_version is the version under test, We're testing that we can correctly +# read the qpy files generated with source version with this version. +qiskit_version=`./qiskit_venv/bin/python -c "import qiskit;print(qiskit.__version__)"` +qiskit_parts=( ${qiskit_version//./ } ) + + +# If source version is less than 0.18 QPY didn't exist yet so exit fast +if [[ ${parts[0]} -eq 0 && ${parts[1]} -lt 18 ]] ; then + exit 0 +fi + +# If the source version is newer than the version under test exit fast because +# there is no QPY compatibility for loading a qpy file generated from a newer +# release with an older release of Qiskit. +if ! ./qiskit_venv/bin/python ./compare_versions.py "$version" "$qiskit_version" ; then exit 0 fi if [[ ! -d qpy_$version ]] ; then echo "Building venv for qiskit-terra $version" python -m venv $version - ./$version/bin/pip install "qiskit-terra==$version" + if [[ ${parts[0]} -eq 0 ]] ; then + ./$version/bin/pip install "qiskit-terra==$version" + else + ./$version/bin/pip install "qiskit==$version" + fi mkdir qpy_$version pushd qpy_$version echo "Generating qpy files with qiskit-terra $version" diff --git a/test/randomized/test_transpiler_equivalence.py b/test/randomized/test_transpiler_equivalence.py index 1c678ddda5fc..2e9a2465be72 100644 --- a/test/randomized/test_transpiler_equivalence.py +++ b/test/randomized/test_transpiler_equivalence.py @@ -56,7 +56,7 @@ import hypothesis.strategies as st -from qiskit import transpile, Aer +from qiskit import transpile, Aer, qasm2 from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister from qiskit.circuit import Measure, Reset, Gate, Barrier from qiskit.providers.fake_provider import ( @@ -338,7 +338,7 @@ def add_c_if_last_gate(self, carg, data): @invariant() def qasm(self): """After each circuit operation, it should be possible to build QASM.""" - self.qc.qasm() + qasm2.dumps(self.qc) @precondition(lambda self: any(isinstance(d[0], Measure) for d in self.qc.data)) @rule(kwargs=transpiler_conf()) @@ -357,7 +357,7 @@ def equivalent_transpile(self, kwargs): + ", ".join(f"{key:s}={value!r}" for key, value in kwargs.items() if value is not None) + ")" ) - print(f"Evaluating {call} for:\n{self.qc.qasm()}") + print(f"Evaluating {call} for:\n{qasm2.dumps(self.qc)}") shots = 4096 @@ -368,7 +368,9 @@ def equivalent_transpile(self, kwargs): try: xpiled_qc = transpile(self.qc, **kwargs) except Exception as e: - failed_qasm = f"Exception caught during transpilation of circuit: \n{self.qc.qasm()}" + failed_qasm = ( + f"Exception caught during transpilation of circuit: \n{qasm2.dumps(self.qc)}" + ) raise RuntimeError(failed_qasm) from e xpiled_aer_counts = self.backend.run(xpiled_qc, shots=shots).result().get_counts() @@ -378,7 +380,7 @@ def equivalent_transpile(self, kwargs): assert ( count_differences == "" ), "Counts not equivalent: {}\nFailing QASM Input:\n{}\n\nFailing QASM Output:\n{}".format( - count_differences, self.qc.qasm(), xpiled_qc.qasm() + count_differences, qasm2.dumps(self.qc), qasm2.dumps(xpiled_qc) ) diff --git a/test/visual/mpl/circuit/references/qreg_names_after_layout.png b/test/visual/mpl/circuit/references/qreg_names_after_layout.png new file mode 100644 index 000000000000..7b372af05276 Binary files /dev/null and b/test/visual/mpl/circuit/references/qreg_names_after_layout.png differ diff --git a/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py b/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py index 88910f2db80e..e2f74f03c52a 100644 --- a/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py +++ b/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020. +# (C) Copyright IBM 2020, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -24,7 +24,7 @@ from qiskit.test import QiskitTestCase from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile from qiskit.providers.fake_provider import FakeTenerife, FakeBelemV2 -from qiskit.visualization.circuit.circuit_visualization import _matplotlib_circuit_drawer +from qiskit.visualization.circuit.circuit_visualization import circuit_drawer from qiskit.circuit.library import ( XGate, MCXGate, @@ -40,7 +40,7 @@ Isometry, ) from qiskit.circuit.library import MCXVChain -from qiskit.circuit import Parameter, Qubit, Clbit, SwitchCaseOp, IfElseOp +from qiskit.circuit import Parameter, Qubit, Clbit, IfElseOp, SwitchCaseOp from qiskit.circuit.library import IQP from qiskit.circuit.classical import expr from qiskit.quantum_info.random import random_unitary @@ -64,7 +64,7 @@ class TestCircuitMatplotlibDrawer(QiskitTestCase): def setUp(self): super().setUp() self.circuit_drawer = VisualTestUtilities.save_data_wrap( - _matplotlib_circuit_drawer, str(self), RESULT_DIR + circuit_drawer, str(self), RESULT_DIR ) if not os.path.exists(FAILURE_DIFF_DIR): @@ -90,7 +90,7 @@ def test_empty_circuit(self): circuit = QuantumCircuit() fname = "empty_circut.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -119,7 +119,7 @@ def test_calibrations(self): circuit.add_calibration("h", [0], h_q0) fname = "calibrations.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -156,7 +156,7 @@ def test_calibrations_with_control_gates(self): circuit.add_calibration("ch", [0, 1], ch_q01) fname = "calibrations_with_control_gates.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -193,7 +193,7 @@ def test_calibrations_with_swap_and_reset(self): circuit.add_calibration("reset", [0], reset_q0) fname = "calibrations_with_swap_and_reset.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -229,7 +229,7 @@ def test_calibrations_with_rzz_and_rxx(self): circuit.add_calibration("rxx", [0, 1], rxx_q01) fname = "calibrations_with_rzz_and_rxx.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -246,7 +246,7 @@ def test_no_ops(self): circuit = QuantumCircuit(2, 3) fname = "no_op_circut.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -274,7 +274,7 @@ def test_long_name(self): circuit.h(qr) fname = "long_name.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -294,7 +294,7 @@ def test_multi_underscore_reg_names(self): circuit = QuantumCircuit(q_reg1, q_reg3, c_reg1, c_reg3) fname = "multi_underscore_true.png" - self.circuit_drawer(circuit, cregbundle=True, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=True, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -304,7 +304,7 @@ def test_multi_underscore_reg_names(self): FAILURE_PREFIX, ) fname2 = "multi_underscore_false.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname2) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname2) ratio2 = VisualTestUtilities._save_diff( self._image_path(fname2), @@ -329,7 +329,7 @@ def test_conditional(self): circuit.h(qr[0]).c_if(cr, 2) fname = "reg_conditional.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -353,7 +353,7 @@ def test_bit_conditional_with_cregbundle(self): circuit.x(qr[1]).c_if(cr[1], 0) fname = "bit_conditional_bundle.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -377,7 +377,7 @@ def test_bit_conditional_no_cregbundle(self): circuit.x(qr[1]).c_if(cr[1], 0) fname = "bit_conditional_no_bundle.png" - self.circuit_drawer(circuit, filename=fname, cregbundle=False) + self.circuit_drawer(circuit, output="mpl", filename=fname, cregbundle=False) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -402,7 +402,7 @@ def test_plot_partial_barrier(self): circuit.h(q[0]) fname = "plot_partial_barrier.png" - self.circuit_drawer(circuit, filename=fname, plot_barriers=True) + self.circuit_drawer(circuit, output="mpl", filename=fname, plot_barriers=True) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -437,7 +437,7 @@ def test_plot_barriers(self): # check the barriers plot properly when plot_barriers= True fname = "plot_barriers_true.png" - self.circuit_drawer(circuit, filename=fname, plot_barriers=True) + self.circuit_drawer(circuit, output="mpl", filename=fname, plot_barriers=True) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -447,7 +447,7 @@ def test_plot_barriers(self): FAILURE_PREFIX, ) fname2 = "plot_barriers_false.png" - self.circuit_drawer(circuit, filename=fname2, plot_barriers=False) + self.circuit_drawer(circuit, output="mpl", filename=fname2, plot_barriers=False) ratio2 = VisualTestUtilities._save_diff( self._image_path(fname2), @@ -470,7 +470,7 @@ def test_no_barriers_false(self): circuit.h(q1[1]) fname = "no_barriers.png" - self.circuit_drawer(circuit, filename=fname, plot_barriers=False) + self.circuit_drawer(circuit, output="mpl", filename=fname, plot_barriers=False) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -491,7 +491,7 @@ def test_fold_minus1(self): circuit.x(0) fname = "fold_minus1.png" - self.circuit_drawer(circuit, fold=-1, filename=fname) + self.circuit_drawer(circuit, output="mpl", fold=-1, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -512,7 +512,7 @@ def test_fold_4(self): circuit.x(0) fname = "fold_4.png" - self.circuit_drawer(circuit, fold=4, filename=fname) + self.circuit_drawer(circuit, output="mpl", fold=4, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -549,7 +549,7 @@ def test_big_gates(self): circuit.append(Isometry(np.eye(4, 4), 0, 0), list(range(3, 5))) fname = "big_gates.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -572,7 +572,7 @@ def test_cnot(self): circuit.append(MCXVChain(3, dirty_ancillas=True), [qr[0], qr[1], qr[2], qr[3], qr[5]]) fname = "cnot.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -594,7 +594,7 @@ def test_cz(self): circuit.append(ZGate().control(1, ctrl_state="0", label="CZ Gate"), [2, 3]) fname = "cz.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -624,7 +624,7 @@ def test_pauli_clifford(self): circuit.dcx(3, 4) fname = "pauli_clifford.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -645,7 +645,9 @@ def test_creg_initial(self): circuit.x(1) fname = "creg_initial_true.png" - self.circuit_drawer(circuit, filename=fname, cregbundle=True, initial_state=True) + self.circuit_drawer( + circuit, output="mpl", filename=fname, cregbundle=True, initial_state=True + ) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -655,7 +657,9 @@ def test_creg_initial(self): FAILURE_PREFIX, ) fname2 = "creg_initial_false.png" - self.circuit_drawer(circuit, filename=fname2, cregbundle=False, initial_state=False) + self.circuit_drawer( + circuit, output="mpl", filename=fname2, cregbundle=False, initial_state=False + ) ratio2 = VisualTestUtilities._save_diff( self._image_path(fname2), @@ -682,7 +686,7 @@ def test_r_gates(self): circuit.rzz(pi / 2, 2, 3) fname = "r_gates.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -706,7 +710,7 @@ def test_ctrl_labels(self): ) fname = "ctrl_labels.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -725,7 +729,7 @@ def test_cswap_rzz(self): circuit.append(RZZGate(3 * pi / 4).control(3, ctrl_state="010"), [2, 1, 4, 3, 0]) fname = "cswap_rzz.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -749,7 +753,7 @@ def test_ghz_to_gate(self): circuit.append(ccghz, [4, 0, 1, 3, 2]) fname = "ghz_to_gate.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -767,7 +771,7 @@ def test_scale(self): circuit.unitary(random_unitary(2**5), circuit.qubits) fname = "scale_default.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -777,7 +781,7 @@ def test_scale(self): FAILURE_PREFIX, ) fname2 = "scale_half.png" - self.circuit_drawer(circuit, filename=fname2, scale=0.5) + self.circuit_drawer(circuit, output="mpl", filename=fname2, scale=0.5) ratio2 = VisualTestUtilities._save_diff( self._image_path(fname2), @@ -788,7 +792,7 @@ def test_scale(self): ) fname3 = "scale_double.png" - self.circuit_drawer(circuit, filename=fname3, scale=2) + self.circuit_drawer(circuit, output="mpl", filename=fname3, scale=2) ratio3 = VisualTestUtilities._save_diff( self._image_path(fname3), @@ -809,7 +813,7 @@ def test_pi_param_expr(self): circuit.rx((pi - x) * (pi - y), 0) fname = "pi_in_param_expr.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -835,7 +839,7 @@ def test_partial_layout(self): ) fname = "partial_layout.png" - self.circuit_drawer(transpiled, filename=fname) + self.circuit_drawer(transpiled, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -854,7 +858,7 @@ def test_init_reset(self): circuit.initialize([0, 1, 0, 0], [0, 1]) fname = "init_reset.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -871,7 +875,7 @@ def test_with_global_phase(self): circuit.h(range(3)) fname = "global_phase.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -924,7 +928,7 @@ def test_alternative_colors(self): else: ref_fname = fname - self.circuit_drawer(circuit, style={"name": style}, filename=fname) + self.circuit_drawer(circuit, output="mpl", style={"name": style}, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -946,7 +950,7 @@ def test_reverse_bits(self): circuit.ccx(2, 1, 0) fname = "reverse_bits.png" - self.circuit_drawer(circuit, reverse_bits=True, filename=fname) + self.circuit_drawer(circuit, output="mpl", reverse_bits=True, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -969,7 +973,7 @@ def test_bw(self): circuit.measure_all() fname = "bw.png" - self.circuit_drawer(circuit, style={"name": "bw"}, filename=fname) + self.circuit_drawer(circuit, output="mpl", style={"name": "bw"}, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1012,6 +1016,7 @@ def test_user_style(self): fname = "user_style.png" self.circuit_drawer( circuit, + output="mpl", style={ "name": "user_style", "displaytext": {"H2": "H_2"}, @@ -1039,7 +1044,7 @@ def test_subfont_change(self): style = {"name": "iqx", "subfontsize": 11} fname = "subfont.png" - self.circuit_drawer(circuit, style=style, filename=fname) + self.circuit_drawer(circuit, output="mpl", style=style, filename=fname) self.assertEqual(style, {"name": "iqx", "subfontsize": 11}) # check does not change style ratio = VisualTestUtilities._save_diff( @@ -1061,7 +1066,7 @@ def test_meas_condition(self): circuit.h(qr[1]).c_if(cr, 1) fname = "meas_condition.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1087,7 +1092,9 @@ def test_reverse_bits_condition(self): circuit.x(2).c_if(cr, 2) fname = "reverse_bits_cond_true.png" - self.circuit_drawer(circuit, cregbundle=False, reverse_bits=True, filename=fname) + self.circuit_drawer( + circuit, output="mpl", cregbundle=False, reverse_bits=True, filename=fname + ) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1097,7 +1104,9 @@ def test_reverse_bits_condition(self): FAILURE_PREFIX, ) fname2 = "reverse_bits_cond_false.png" - self.circuit_drawer(circuit, cregbundle=False, reverse_bits=False, filename=fname2) + self.circuit_drawer( + circuit, output="mpl", cregbundle=False, reverse_bits=False, filename=fname2 + ) ratio2 = VisualTestUtilities._save_diff( self._image_path(fname2), @@ -1131,6 +1140,7 @@ def cnotnot(gate_label): fname = "style_custom_gates.png" self.circuit_drawer( circuit, + output="mpl", style={ "displaycolor": {"CNOTNOT": ("#000000", "#FFFFFF"), "h": ("#A1A1A1", "#043812")}, "displaytext": {"CNOTNOT_PRIME": "$\\mathrm{CNOTNOT}'$"}, @@ -1157,6 +1167,7 @@ def test_6095(self): fname = "6095.png" self.circuit_drawer( circuit, + output="mpl", style={"displaycolor": {"cp": ("#A27486", "#000000"), "h": ("#A27486", "#000000")}}, filename=fname, ) @@ -1179,7 +1190,7 @@ def test_instruction_1q_1c(self): circuit.append(inst, [qr[0]], [cr[0]]) fname = "instruction_1q_1c.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1200,7 +1211,7 @@ def test_instruction_3q_3c_circ1(self): circuit.append(inst, [qr[0], qr[1], qr[2]], [cr2[0], cr[0], cr[1]]) fname = "instruction_3q_3c_circ1.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1221,7 +1232,7 @@ def test_instruction_3q_3c_circ2(self): circuit.append(inst, [qr[3], qr[0], qr[2]], [cr[0], cr[1], cr2[0]]) fname = "instruction_3q_3c_circ2.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1243,7 +1254,7 @@ def test_instruction_3q_3c_circ3(self): circuit.append(inst, [qr[3], qr[1], qr[2]], [cr3[1], cr[1], cr3[0]]) fname = "instruction_3q_3c_circ3.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1262,7 +1273,7 @@ def test_overwide_gates(self): circuit.initialize(initial_state) fname = "wide_params.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1284,7 +1295,7 @@ def test_one_bit_regs(self): circuit.measure(0, 0) fname = "one_bit_regs.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1313,7 +1324,7 @@ def test_user_ax_subplot(self): plt.close(fig) fname = "user_ax.png" - self.circuit_drawer(circuit, ax=ax2, filename=fname) + self.circuit_drawer(circuit, output="mpl", ax=ax2, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1334,7 +1345,7 @@ def test_figwidth(self): circuit.x(2) fname = "figwidth.png" - self.circuit_drawer(circuit, style={"figwidth": 5}, filename=fname) + self.circuit_drawer(circuit, output="mpl", style={"figwidth": 5}, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1353,7 +1364,7 @@ def test_registerless_one_bit(self): circuit = QuantumCircuit(qrx, [Qubit(), Qubit()], qry, [Clbit(), Clbit()], crx) fname = "registerless_one_bit.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1377,7 +1388,7 @@ def test_measures_with_conditions(self): circuit.h(0).c_if(cr2, 3) fname = "measure_cond_false.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1387,7 +1398,7 @@ def test_measures_with_conditions(self): FAILURE_PREFIX, ) fname2 = "measure_cond_true.png" - self.circuit_drawer(circuit, cregbundle=True, filename=fname2) + self.circuit_drawer(circuit, output="mpl", cregbundle=True, filename=fname2) ratio2 = VisualTestUtilities._save_diff( self._image_path(fname2), @@ -1410,7 +1421,7 @@ def test_conditions_measures_with_bits(self): circuit.measure(0, bits[3]) fname = "measure_cond_bits_false.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1420,7 +1431,7 @@ def test_conditions_measures_with_bits(self): FAILURE_PREFIX, ) fname2 = "measure_cond_bits_true.png" - self.circuit_drawer(circuit, cregbundle=True, filename=fname2) + self.circuit_drawer(circuit, output="mpl", cregbundle=True, filename=fname2) ratio2 = VisualTestUtilities._save_diff( self._image_path(fname2), @@ -1444,7 +1455,7 @@ def test_conditional_gates_right_of_measures_with_bits(self): circuit.h(qr[2]).c_if(cr[0], 0) fname = "measure_cond_bits_right.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1464,7 +1475,9 @@ def test_conditions_with_bits_reverse(self): circuit.x(0).c_if(bits[3], 0) fname = "cond_bits_reverse.png" - self.circuit_drawer(circuit, cregbundle=False, reverse_bits=True, filename=fname) + self.circuit_drawer( + circuit, output="mpl", cregbundle=False, reverse_bits=True, filename=fname + ) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1483,7 +1496,7 @@ def test_sidetext_with_condition(self): circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) fname = "sidetext_condition.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1518,7 +1531,7 @@ def test_fold_with_conditions(self): circuit.append(U1Gate(0).control(1), [1, 0]).c_if(cr, 31) fname = "fold_with_conditions.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1536,7 +1549,7 @@ def test_idle_wires_barrier(self): circuit.barrier() fname = "idle_wires_barrier.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1561,6 +1574,7 @@ def test_wire_order(self): fname = "wire_order.png" self.circuit_drawer( circuit, + output="mpl", cregbundle=False, wire_order=[2, 1, 3, 0, 6, 8, 9, 5, 4, 7], filename=fname, @@ -1586,7 +1600,7 @@ def test_barrier_label(self): circuit.barrier(label="End Y/X") fname = "barrier_label.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1608,7 +1622,7 @@ def test_if_op(self): circuit.cx(0, 1) fname = "if_op.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1632,7 +1646,7 @@ def test_if_else_op_bundle_false(self): circuit.cx(0, 1) fname = "if_else_op_false.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1657,7 +1671,7 @@ def test_if_else_op_bundle_true(self): circuit.cx(0, 1) fname = "if_else_op_true.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=True, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1681,7 +1695,9 @@ def test_if_else_op_textbook_style(self): circuit.cx(0, 1) fname = "if_else_op_textbook.png" - self.circuit_drawer(circuit, style="textbook", cregbundle=False, filename=fname) + self.circuit_drawer( + circuit, output="mpl", style="textbook", cregbundle=False, filename=fname + ) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1715,7 +1731,7 @@ def test_if_else_with_body(self): circuit.x(0, label="X1i") fname = "if_else_body.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1756,7 +1772,7 @@ def test_if_else_op_nested(self): circuit.x(0) fname = "if_else_op_nested.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=True, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1797,7 +1813,13 @@ def test_if_else_op_wire_order(self): circuit.x(0) fname = "if_else_op_wire_order.png" - self.circuit_drawer(circuit, wire_order=[2, 0, 3, 1, 4, 5, 6], filename=fname) + self.circuit_drawer( + circuit, + output="mpl", + cregbundle=False, + wire_order=[2, 0, 3, 1, 4, 5, 6], + filename=fname, + ) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1838,7 +1860,7 @@ def test_if_else_op_fold(self): circuit.x(0) fname = "if_else_op_fold.png" - self.circuit_drawer(circuit, fold=7, filename=fname) + self.circuit_drawer(circuit, output="mpl", fold=7, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1865,7 +1887,7 @@ def test_while_loop_op(self): circuit.x(0) fname = "while_loop.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1894,7 +1916,7 @@ def test_for_loop_op(self): circuit.z(0) fname = "for_loop.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1923,7 +1945,7 @@ def test_for_loop_op_range(self): circuit.z(0) fname = "for_loop_range.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1951,7 +1973,7 @@ def test_for_loop_op_1_qarg(self): circuit.z(0) fname = "for_loop_1_qarg.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -1983,7 +2005,7 @@ def test_switch_case_op(self): circuit.h(0) fname = "switch_case.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -2011,7 +2033,7 @@ def test_switch_case_op_1_qarg(self): circuit.h(0) fname = "switch_case_1_qarg.png" - self.circuit_drawer(circuit, cregbundle=False, filename=fname) + self.circuit_drawer(circuit, output="mpl", cregbundle=False, filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -2036,7 +2058,7 @@ def test_if_with_expression(self): circuit.z(0) fname = "if_op_expr.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -2063,7 +2085,7 @@ def test_if_with_expression_nested(self): circuit.z(1) fname = "if_op_expr_nested.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -2091,7 +2113,7 @@ def test_switch_with_expression(self): circuit.cx(0, 1) fname = "switch_expr.png" - self.circuit_drawer(circuit, filename=fname) + self.circuit_drawer(circuit, output="mpl", filename=fname) ratio = VisualTestUtilities._save_diff( self._image_path(fname), @@ -2122,7 +2144,8 @@ def test_control_flow_layout(self): backend.target.add_instruction(SwitchCaseOp, name="switch_case") tqc = transpile(qc, backend, optimization_level=2, seed_transpiler=671_42) fname = "layout_control_flow.png" - self.circuit_drawer(tqc, filename=fname) + self.circuit_drawer(tqc, output="mpl", filename=fname) + ratio = VisualTestUtilities._save_diff( self._image_path(fname), self._reference_path(fname), @@ -2154,8 +2177,10 @@ def test_control_flow_nested_layout(self): backend.target.add_instruction(SwitchCaseOp, name="switch_case") backend.target.add_instruction(IfElseOp, name="if_else") tqc = transpile(qc, backend, optimization_level=2, seed_transpiler=671_42) + fname = "nested_layout_control_flow.png" - self.circuit_drawer(tqc, filename=fname) + self.circuit_drawer(tqc, output="mpl", filename=fname) + ratio = VisualTestUtilities._save_diff( self._image_path(fname), self._reference_path(fname), @@ -2184,6 +2209,31 @@ def test_iqx_pendingdeprecation(self): ): qc.draw("mpl", style=style) + def test_no_qreg_names_after_layout(self): + """Test that full register names are not shown after transpilation. + See https://github.com/Qiskit/qiskit-terra/issues/11038""" + backend = FakeBelemV2() + + qc = QuantumCircuit(3) + qc.cx(0, 1) + qc.cx(1, 2) + qc.cx(2, 0) + circuit = transpile( + qc, backend, basis_gates=["rz", "sx", "cx"], layout_method="sabre", seed_transpiler=42 + ) + + fname = "qreg_names_after_layout.png" + self.circuit_drawer(circuit, output="mpl", filename=fname) + + ratio = VisualTestUtilities._save_diff( + self._image_path(fname), + self._reference_path(fname), + fname, + FAILURE_DIFF_DIR, + FAILURE_PREFIX, + ) + self.assertGreaterEqual(ratio, 0.9999) + if __name__ == "__main__": unittest.main(verbosity=1) diff --git a/tools/deploy_translatable_strings.sh b/tools/deploy_translatable_strings.sh deleted file mode 100755 index b481bde4fcdf..000000000000 --- a/tools/deploy_translatable_strings.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# Script for pushing the translatable messages to poBranch. - -# DO NOT `set -x`. We have to pass secrets to `openssl` on the command line, -# and we don't want them appearing in the log. This script instead manually -# 'echo's its status at various points. -set -eu -o pipefail - -if [[ "$#" -ne 1 ]]; then - echo "Usage: deploy_translatable_string.sh /path/to/translations/artifact" >&2 - exit 1 -fi - -# Variables used by this script. -# From github actions the docs/locale/en directory from the sphinx build -# gets downloaded from the github actions artifacts as deploy directory - -TARGET_REPO="git@github.com:qiskit-community/qiskit-translations.git" -TARGET_REPO_BRANCH="main" - -SOURCE_TOOLS_DIR="$(dirname "$(realpath "$0")")" -# Absolute paths to the git repository roots for the source repository (which -# this file lives in) and where we're going to clone the target repository. -SOURCE_REPO_ROOT="$(dirname "$SOURCE_TOOLS_DIR")" -TARGET_REPO_ROOT="${SOURCE_REPO_ROOT}/_qiskit_translations" - -SOURCE_LANG="en" -# Absolute paths to the source and target directories for the translations -# files. CI should feed the source in for us - it depends on the particulars of -# how it was built in a previous job. The target is under our control. -SOURCE_PO_DIR="$1" -TARGET_PO_DIR="${TARGET_REPO_ROOT}/docs/locale/${SOURCE_LANG}" - -# Add the SSH key needed to verify ourselves when pushing to the target remote. -echo "+ setup ssh keys" -eval "$(ssh-agent -s)" -openssl enc -aes-256-cbc -d \ - -in "${SOURCE_REPO_ROOT}/tools/github_poBranch_update_key.enc" \ - -K "$encrypted_deploy_po_branch_key" \ - -iv "$encrypted_deploy_po_branch_iv" \ - | ssh-add - - -# Clone the target repository so we can build our commit in it. -echo "+ 'git clone' translations target repository" -git clone --depth 1 "$TARGET_REPO" "$TARGET_REPO_ROOT" --single-branch --branch "$TARGET_REPO_BRANCH" -pushd "$TARGET_REPO_ROOT" - -echo "+ setup git configuration for commit" -git config user.name "Qiskit Autodeploy" -git config user.email "qiskit@qiskit.org" - -echo "+ 'git rm' current translations files" -# Remove existing versions of the translations, to ensure deletions in the source repository are recognised. -git rm -rf --ignore-unmatch \ - "$TARGET_PO_DIR/LC_MESSAGES/"*.po \ - "$TARGET_PO_DIR/LC_MESSAGES/api" \ - "$TARGET_PO_DIR/LC_MESSAGES/apidoc" \ - "$TARGET_PO_DIR/LC_MESSAGES/apidoc_legacy" \ - "$TARGET_PO_DIR/LC_MESSAGES/theme" \ - "$TARGET_PO_DIR/LC_MESSAGES/"_* - -echo "+ 'rm' unwanted files from source documentation" -# Remove files from the deployment that we don't want translating. -rm -rf \ - "$SOURCE_PO_DIR/LC_MESSAGES/api/" \ - "$SOURCE_PO_DIR/LC_MESSAGES/apidoc/" \ - "$SOURCE_PO_DIR/LC_MESSAGES/apidoc_legacy/" \ - "$SOURCE_PO_DIR/LC_MESSAGES/stubs/" \ - "$SOURCE_PO_DIR/LC_MESSAGES/theme/" - -echo "+ 'cp' wanted files from source to target" -# Copy the new rendered files and add them to the commit. -cp -r "$SOURCE_PO_DIR/." "$TARGET_PO_DIR" -# Copy files necessary to build the Qiskit metapackage. -cp "$SOURCE_REPO_ROOT/qiskit_pkg/setup.py" "${TARGET_REPO_ROOT}" -cat "$SOURCE_REPO_ROOT/requirements-dev.txt" "$SOURCE_REPO_ROOT/requirements-optional.txt" \ - > "${TARGET_REPO_ROOT}/requirements-dev.txt" -cp "$SOURCE_REPO_ROOT/constraints.txt" "${TARGET_REPO_ROOT}" - -echo "+ 'git add' files to target commit" -git add docs/ setup.py requirements-dev.txt constraints.txt - -echo "+ 'git commit' wanted files" -# Commit and push the changes. -git commit \ - -m "Automated documentation update to add .po files from qiskit" \ - -m "skip ci" \ - -m "Commit: $GITHUB_SHA" \ - -m "Github Actions Run: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" - -echo "+ 'git push' to target repository" -git push --quiet origin "$TARGET_REPO_BRANCH" -echo "********** End of pushing po to working repo! *************" -popd diff --git a/tools/execute_tutorials.py b/tools/execute_tutorials.py deleted file mode 100644 index 2ce0c0c07fe7..000000000000 --- a/tools/execute_tutorials.py +++ /dev/null @@ -1,100 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2023 -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=missing-function-docstring,broad-exception-caught - -""" -Utility script to parallelise the conversion of several Jupyter notebooks. - -If nbconvert starts offering built-in parallelisation this script can likely be dropped. -""" - -import argparse -import functools -import multiprocessing -import os -import pathlib -import sys -import typing - -import nbformat -from nbconvert.preprocessors import ExecutePreprocessor - - -def worker( - notebook_path: pathlib.Path, - in_root: pathlib.Path, - out_root: typing.Optional[pathlib.Path], - timeout: int = -1, -) -> typing.Optional[Exception]: - """Single parallel worker that spawns a Jupyter executor node, executes the given notebook - within it, and writes out the output.""" - try: - print(f"({os.getpid()}) Processing '{str(notebook_path)}'", flush=True) - processor = ExecutePreprocessor(timeout=timeout, kernel_name="python3") - with open(notebook_path, "r") as fptr: - notebook = nbformat.read(fptr, as_version=4) - # Run the notebook with the working directory set to the folder it resides in. - processor.preprocess(notebook, {"metadata": {"path": f"{notebook_path.parent}/"}}) - - # Ensure the output directory exists, and write to it. This overwrites the notebook with - # its executed form unless the '--out' flag was set. - out_root = in_root if out_root is None else out_root - out_path = out_root / notebook_path.relative_to(in_root) - out_path.parent.mkdir(parents=True, exist_ok=True) - with open(out_path, "w", encoding="utf-8") as fptr: - nbformat.write(notebook, fptr) - except Exception as exc: - return exc - return None - - -def main() -> int: - parser = argparse.ArgumentParser(description="Execute tutorial Jupyter notebooks.") - parser.add_argument( - "notebook_dirs", type=pathlib.Path, nargs="*", help="Folders containing Jupyter notebooks." - ) - parser.add_argument( - "-o", - "--out", - type=pathlib.Path, - help="Output directory for files. Defaults to same location as input file, overwriting it.", - ) - parser.add_argument( - "-j", - "--num-processes", - type=int, - default=os.cpu_count(), - help="Number of processes to use.", - ) - args = parser.parse_args() - notebooks = sorted( - { - (notebook_path, in_root, args.out) - for in_root in args.notebook_dirs - for notebook_path in in_root.glob("**/*.ipynb") - } - ) - timeout = int(os.getenv("QISKIT_CELL_TIMEOUT", "300")) - print(f"Using {args.num_processes} process{'' if args.num_processes == 1 else 'es'}.") - with multiprocessing.Pool(args.num_processes) as pool: - failures = pool.starmap(functools.partial(worker, timeout=timeout), notebooks) - num_failures = 0 - for path, failure in zip(notebooks, failures): - if failure is not None: - print(f"'{path}' failed: {failure}", file=sys.stderr) - num_failures += 1 - return num_failures - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tools/github_poBranch_update_key.enc b/tools/github_poBranch_update_key.enc deleted file mode 100644 index cbffab9d4089..000000000000 Binary files a/tools/github_poBranch_update_key.enc and /dev/null differ diff --git a/tox.ini b/tox.ini index fa6d1017d3e2..74436293597b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,9 @@ [tox] minversion = 3.3.0 -envlist = py38, py39, py310, py311, lint-incr +envlist = py38, py39, py310, py311, py312, lint-incr isolated_build = true [testenv] -usedevelop = True install_command = pip install -c{toxinidir}/constraints.txt -U {opts} {packages} setenv = VIRTUAL_ENV={envdir} @@ -15,17 +14,18 @@ setenv = QISKIT_TEST_CAPTURE_STREAMS=1 QISKIT_PARALLEL=FALSE passenv = RAYON_NUM_THREADS, OMP_NUM_THREADS, QISKIT_PARALLEL, RUST_BACKTRACE, SETUPTOOLS_ENABLE_FEATURES, QISKIT_TESTS, QISKIT_IN_PARALLEL -deps = setuptools_rust # This is work around for the bug of tox 3 (see #8606 for more details.) - -r{toxinidir}/requirements.txt - -r{toxinidir}/requirements-dev.txt +deps = + setuptools_rust # This is work around for the bug of tox 3 (see #8606 for more details.) + -r{toxinidir}/requirements.txt + -r{toxinidir}/requirements-dev.txt commands = stestr run {posargs} [testenv:lint] basepython = python3 commands = - ruff check qiskit test tools examples setup.py qiskit_pkg - black --check {posargs} qiskit test tools examples setup.py qiskit_pkg + ruff check qiskit test tools examples setup.py + black --check {posargs} qiskit test tools examples setup.py pylint -rn qiskit test tools # This line is commented out until #6649 merges. We can't run this currently # via tox because tox doesn't support globbing @@ -39,9 +39,9 @@ commands = basepython = python3 allowlist_externals = git commands = - ruff check qiskit test tools examples setup.py qiskit_pkg - black --check {posargs} qiskit test tools examples setup.py qiskit_pkg - -git fetch -q https://github.com/Qiskit/qiskit-terra.git :lint_incr_latest + ruff check qiskit test tools examples setup.py + black --check {posargs} qiskit test tools examples setup.py + -git fetch -q https://github.com/Qiskit/qiskit.git :lint_incr_latest python {toxinidir}/tools/pylint_incr.py -rn -j4 -sn --paths :/qiskit/*.py :/test/*.py :/tools/*.py python {toxinidir}/tools/pylint_incr.py -rn -j4 -sn --disable='invalid-name,missing-module-docstring,redefined-outer-name' --paths :(glob,top)examples/python/*.py python {toxinidir}/tools/verify_headers.py qiskit test tools examples @@ -50,40 +50,33 @@ commands = reno lint [testenv:black] -commands = black {posargs} qiskit test tools examples setup.py qiskit_pkg +skip_install = true +deps = + -r requirements-dev.txt +commands = black {posargs} qiskit test tools examples setup.py [testenv:coverage] basepython = python3 setenv = {[testenv]setenv} PYTHON=coverage3 run --source qiskit --parallel-mode -deps = -r{toxinidir}/requirements.txt - -r{toxinidir}/requirements-dev.txt - -r{toxinidir}/requirements-optional.txt +deps = + {[testenv]deps} + -r{toxinidir}/requirements-optional.txt commands = stestr run {posargs} coverage3 combine coverage3 report [testenv:docs] -# Editable mode breaks macOS: https://github.com/sphinx-doc/sphinx/issues/10943 -usedevelop = False basepython = python3 setenv = {[testenv]setenv} QISKIT_SUPPRESS_PACKAGING_WARNINGS=Y RUST_DEBUG=1 # Faster to compile. -passenv = {[testenv]passenv}, QISKIT_DOCS_BUILD_TUTORIALS, QISKIT_CELL_TIMEOUT, DOCS_PROD_BUILD deps = - setuptools_rust # This is work around for the bug of tox 3 (see #8606 for more details.) - -r{toxinidir}/requirements-dev.txt + {[testenv]deps} -r{toxinidir}/requirements-optional.txt - -r{toxinidir}/requirements-tutorials.txt - # Some optionals depend on Terra. We want to make sure pip satisfies that requirement from a local - # installation, not from PyPI. But Tox normally doesn't install the local installation until - # after `deps` is installed. So, instead, we tell pip to do the local installation at the same - # time as the optionals. See https://github.com/Qiskit/qiskit-terra/pull/9477. - . commands = sphinx-build -W -j auto -T --keep-going -b html docs/ docs/_build/html {posargs} @@ -94,17 +87,3 @@ allowlist_externals = rm commands = rm -rf {toxinidir}/docs/stubs/ {toxinidir}/docs/_build {toxinidir}/docs/locale - -[testenv:tutorials] -base = docs -commands = - python tools/execute_tutorials.py {toxinidir}/docs/tutorials --out={toxinidir}/executed_tutorials {posargs} - -[testenv:gettext] -base = docs -deps = - {[testenv:docs]deps} - sphinx-intl -commands = - sphinx-build -b gettext docs docs/_build/gettext {posargs} - sphinx-intl -c docs/conf.py update -p docs/_build/gettext -l en -d docs/locale